diff --git a/NEWS.md b/NEWS.md index 3501a9d0..fbe5e79e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,13 @@ -# naomi 2.10.2 +naomi 2.10.3 * Improve error generated when user fits model at admin level where no population data exists. +naomi 2.10.2 +* Add ANC testing outputs to T4 projection period for including in PEPFAR datapack output. +* Rename Datapack input CSV in the output ZIP folder for 2025 to `"pepfar_datapack_indicators_2025.csv"`. +* Add ANC testing indicators to T4 projection reprsenting the end of one year COP planning + period. New indicators are `PMTCT_STAT*.T` and `PMTCT_ART*.T` + # naomi 2.10.1 * Patch error in reading `anc_already_art` from Spectrum PJNZ file (was errantly diff --git a/R/model.R b/R/model.R index c016a441..663813fe 100644 --- a/R/model.R +++ b/R/model.R @@ -585,6 +585,10 @@ naomi_model_frame <- function(area_merged, spec_prev_t4 = prevalence, spec_incid_t4 = incidence, spec_artcov_t4 = art_coverage, + spec_unaware_untreated_prop_t4 = unaware_untreated_prop, + asfr_t4 = asfr, + frr_plhiv_t4 = frr_plhiv, + frr_already_art_t4 = frr_already_art ), by = c("spectrum_region_code", "sex", "age_group") ) %>% diff --git a/R/outputs.R b/R/outputs.R index 40485f1d..8e04f485 100644 --- a/R/outputs.R +++ b/R/outputs.R @@ -184,6 +184,16 @@ extract_indicators <- function(naomi_fit, naomi_mf, na.rm = FALSE) { "anc_rho_t3_out" = "anc_prevalence", "anc_alpha_t3_out" = "anc_art_coverage") + indicators_anc_t4 <- c("anc_clients_t4_out" = "anc_clients", + "anc_plhiv_t4_out" = "anc_plhiv", + "anc_already_art_t4_out" = "anc_already_art", + "anc_art_new_t4_out" = "anc_art_new", + "anc_known_pos_t4_out" = "anc_known_pos", + "anc_tested_pos_t4_out" = "anc_tested_pos", + "anc_tested_neg_t4_out" = "anc_tested_neg", + "anc_rho_t4_out" = "anc_prevalence", + "anc_alpha_t4_out" = "anc_art_coverage") + indicator_anc_est_t1 <- Map(get_est, names(indicators_anc_t1), indicators_anc_t1, naomi_mf$calendar_quarter1, list(naomi_mf$mf_anc_out)) @@ -191,11 +201,14 @@ extract_indicators <- function(naomi_fit, naomi_mf, na.rm = FALSE) { naomi_mf$calendar_quarter2, list(naomi_mf$mf_anc_out)) indicator_anc_est_t3 <- Map(get_est, names(indicators_anc_t3), indicators_anc_t3, naomi_mf$calendar_quarter3, list(naomi_mf$mf_anc_out)) + indicator_anc_est_t4 <- Map(get_est, names(indicators_anc_t4), indicators_anc_t4, + naomi_mf$calendar_quarter4, list(naomi_mf$mf_anc_out)) indicator_anc_est_t1 <- dplyr::bind_rows(indicator_anc_est_t1) indicator_anc_est_t2 <- dplyr::bind_rows(indicator_anc_est_t2) indicator_anc_est_t3 <- dplyr::bind_rows(indicator_anc_est_t3) + indicator_anc_est_t4 <- dplyr::bind_rows(indicator_anc_est_t4) mf_anc_out <- naomi_mf$mf_areas %>% dplyr::transmute(area_id, @@ -210,6 +223,7 @@ extract_indicators <- function(naomi_fit, naomi_mf, na.rm = FALSE) { indicator_est_t3, indicator_anc_est_t3, indicator_est_t4, + indicator_anc_est_t4, indicator_est_t5 ) diff --git a/R/pepfar-datapack.R b/R/pepfar-datapack.R index 504cd7af..d808b69a 100644 --- a/R/pepfar-datapack.R +++ b/R/pepfar-datapack.R @@ -1,4 +1,4 @@ -PEPFAR_DATAPACK_FILENAME <- "pepfar_datapack_indicators_2024.csv" +PEPFAR_DATAPACK_FILENAME <- "pepfar_datapack_indicators_2025.csv" #' Export naomi outputs to PEPFAR Data Pack format #' diff --git a/R/tmb-model-r-implementation.R b/R/tmb-model-r-implementation.R index 92e1b11a..e4be9c03 100644 --- a/R/tmb-model-r-implementation.R +++ b/R/tmb-model-r-implementation.R @@ -758,6 +758,26 @@ naomi_objective_function_r <- function(d, p) { infections_t4 <- lambda_t4 * (d$population_t4 - plhiv_t4) + ## Note: currently assuming same district effects parameters from t2 for t4 + mu_anc_rho_t4 <- qlogis(rho_t4) + + d$logit_anc_rho_t2_offset + + d$X_ancrho %*% (p$beta_anc_rho + p$beta_anc_rho_t2) + + d$Z_ancrho_x %*% (p$ui_anc_rho_x + p$ui_anc_rho_xt) + mu_anc_rho_t4 <- as.vector(mu_anc_rho_t4) + anc_rho_t4 <- stats::plogis(mu_anc_rho_t4) + + mu_anc_alpha_t4 <- mu_alpha_t4 + + d$logit_anc_alpha_t4_offset + + d$X_ancalpha %*% (p$beta_anc_alpha + p$beta_anc_alpha_t2) + + d$Z_ancalpha_x %*% (p$ui_anc_alpha_x + p$ui_anc_alpha_xt) + mu_anc_alpha_t4 <- as.vector(mu_anc_alpha_t4) + anc_alpha_t4 <- plogis(mu_anc_alpha_t4) + + anc_clients_t4 <- d$population_t4 * exp(d$log_asfr_t4_offset + mu_asfr) + anc_plhiv_t4 <- anc_clients_t4 * anc_rho_t4 + anc_already_art_t4 <- anc_plhiv_t4 * anc_alpha_t4 + + prop_art_ij_t4 <- as.vector(d$Xart_idx %*% prop_art_t4) * as.vector(d$Xart_gamma %*% gamma_art_t2) ## Note: using same ART attendance as T2 population_ij_t4 <- as.vector(d$Xart_idx %*% d$population_t4) artnum_ij_t4 <- population_ij_t4 * prop_art_ij_t4 @@ -774,11 +794,31 @@ naomi_objective_function_r <- function(d, p) { infections_t4_out <- as.vector(d$A_out %*% infections_t4) lambda_t4_out <- infections_t4_out / (population_t4_out - plhiv_t4_out) + anc_clients_t4_out <- as.vector(d$A_anc_out %*% anc_clients_t4) + anc_plhiv_t4_out <- as.vector(d$A_anc_out %*% anc_plhiv_t4) + anc_already_art_t4_out <- as.vector(d$A_anc_out %*% anc_already_art_t4) + anc_art_new_t4_out <- anc_plhiv_t4_out - anc_already_art_t4_out + anc_known_pos_t4_out <- anc_already_art_t4_out + anc_tested_pos_t4_out <- anc_plhiv_t4_out - anc_known_pos_t4_out + anc_tested_neg_t4_out <- anc_clients_t4_out - anc_plhiv_t4_out + + anc_rho_t4_out <- anc_plhiv_t4_out / anc_clients_t4_out + anc_alpha_t4_out <- anc_already_art_t4_out / anc_plhiv_t4_out + report_t4 <- list(population_t4_out = population_t4_out, plhiv_t4_out = plhiv_t4_out, plhiv_attend_t4_out = plhiv_attend_t4_out, lambda_t4_out = lambda_t4_out, - infections_t4_out = infections_t4_out) + infections_t4_out = infections_t4_out, + anc_clients_t4_out = anc_clients_t4_out, + anc_plhiv_t4_out = anc_plhiv_t4_out, + anc_already_art_t4_out = anc_already_art_t4_out, + anc_art_new_t4_out = anc_art_new_t4_out, + anc_known_pos_t4_out = anc_known_pos_t4_out, + anc_tested_pos_t4_out = anc_tested_pos_t4_out, + anc_tested_neg_t4_out = anc_tested_neg_t4_out, + anc_rho_t4_out = anc_rho_t4_out, + anc_alpha_t4_out = anc_alpha_t4_out) ## Projection to time 5 diff --git a/R/tmb-model.R b/R/tmb-model.R index 4b74c09c..d1b2bb83 100644 --- a/R/tmb-model.R +++ b/R/tmb-model.R @@ -294,12 +294,15 @@ prepare_tmb_inputs <- function(naomi_data, log_asfr_t1_offset = log(df$asfr_t1), log_asfr_t2_offset = log(df$asfr_t2), log_asfr_t3_offset = log(df$asfr_t3), + log_asfr_t4_offset = log(df$asfr_t4), logit_anc_rho_t1_offset = log(df$frr_plhiv_t1), logit_anc_rho_t2_offset = log(df$frr_plhiv_t2), logit_anc_rho_t3_offset = log(df$frr_plhiv_t3), + logit_anc_rho_t4_offset = log(df$frr_plhiv_t4), logit_anc_alpha_t1_offset = log(df$frr_already_art_t1), logit_anc_alpha_t2_offset = log(df$frr_already_art_t2), logit_anc_alpha_t3_offset = log(df$frr_already_art_t3), + logit_anc_alpha_t4_offset = log(df$frr_already_art_t4), ## logit_rho_offset = naomi_data$mf_model$logit_rho_offset * naomi_data$mf_model$bin_rho_model, logit_alpha_offset = naomi_data$mf_model$logit_alpha_offset, diff --git a/inst/datapack/datapack_indicator_mapping.csv b/inst/datapack/datapack_indicator_mapping.csv index 76fa2b5b..cc7b083d 100644 --- a/inst/datapack/datapack_indicator_mapping.csv +++ b/inst/datapack/datapack_indicator_mapping.csv @@ -11,6 +11,13 @@ anc_clients,RM8gRoxtsNw,PMTCT_STAT_SUBNAT.D.T_1,# Pregnant Women who present at anc_known_pos,tAE7ZD7p9zu,PMTCT_STAT_SUBNAT.N.Known.Pos.T_1,# Pregnant Women who are known HIV-positive at ANC1,TRUE,3 anc_tested_neg,tAE7ZD7p9zu,PMTCT_STAT_SUBNAT.N.New.Neg.T_1,# Pregnant Women who are newly tested and found HIV-negative at ANC1,TRUE,3 anc_tested_pos,tAE7ZD7p9zu,PMTCT_STAT_SUBNAT.N.New.Pos.T_1,# Pregnant Women who are newly tested and found HIV-positive at ANC1,TRUE,3 +anc_plhiv,PMTCT_ART_SUBNAT.D.T,PMTCT_ART_SUBNAT.D.T,# HIV-positIve Pregnant Women identified at ANC1,TRUE,4 +anc_already_art,PMTCT_ART_SUBNAT.N.Already.T,PMTCT_ART_SUBNAT.N.Already.T,# HIV-positive Pregnant Women already on ART at ANC1,TRUE,4 +anc_art_new,PMTCT_ART_SUBNAT.N.New.T,PMTCT_ART_SUBNAT.N.New.T,# HIV-positive Pregnant Women newly diagnosed at ANC1 and placed on ART,TRUE,4 +anc_clients,PMTCT_STAT_SUBNAT.D.T,PMTCT_STAT_SUBNAT.D.T,# Pregnant Women who present at ANC1,TRUE,4 +anc_known_pos,PMTCT_STAT_SUBNAT.N.Known.Pos.T,PMTCT_STAT_SUBNAT.N.Known.Pos.T,# Pregnant Women who are known HIV-positive at ANC1,TRUE,4 +anc_tested_neg,PMTCT_STAT_SUBNAT.N.New.Neg.T,PMTCT_STAT_SUBNAT.N.New.Neg.T,# Pregnant Women who are newly tested and found HIV-negative at ANC1,TRUE,4 +anc_tested_pos,PMTCT_STAT_SUBNAT.N.New.Pos.T,PMTCT_STAT_SUBNAT.N.New.Pos.T,# Pregnant Women who are newly tested and found HIV-positive at ANC1,TRUE,4 art_current,xghQXueYJxu,TX_CURR_SUBNAT.T_1,Total # of PLHIV on ART,TRUE,3 art_coverage,PopART.Rt.T_1,PopART.Rt.T_1,ART coverage,FALSE,3 aware_plhiv_num,nF19GOjcnoD,DIAGNOSED_SUBNAT.T_1,Number of PLHIV aware of HIV positive status,TRUE,3 diff --git a/inst/metadata/colour_scales.csv b/inst/metadata/colour_scales.csv index 48d2f23d..f9233c6a 100644 --- a/inst/metadata/colour_scales.csv +++ b/inst/metadata/colour_scales.csv @@ -32,6 +32,7 @@ default,art_new,interpolateGreens,0,80000,FALSE default,vl_tested_12mos,interpolatePurples,0,60000,FALSE default,vl_suppressed_12mos,interpolatePurples,0,60000,FALSE default,population_ratio,interpolateGreys,0,1.2,FALSE +default,population_proportion,interpolateGreys,0,1.2,FALSE default,plhiv_ratio,interpolateReds,0,1.2,FALSE default,infections_ratio,interpolateBlues,0,1.2,FALSE default,art_current_ratio,interpolateGreens,0,1.2,FALSE diff --git a/inst/metadata/meta_indicator.csv b/inst/metadata/meta_indicator.csv index 01f57628..b43d1f32 100644 --- a/inst/metadata/meta_indicator.csv +++ b/inst/metadata/meta_indicator.csv @@ -37,3 +37,4 @@ aware_plhiv_prop_ratio,t_(AWARE_PLHIV_PROP_RATIO),,,35,FALSE,, incidence_ratio,t_(INCIDENCE_RATIO),,,36,FALSE,, anc_prevalence_age_matched,t_(ANC_PREVALENCE_AGE_MATCHED),,,37,TRUE,, anc_art_coverage_age_matched,t_(ANC_ART_COVERAGE_AGE_MATCHED),,,38,TRUE,, +population_proportion,t_(POPULATION_PROPORTION),,,39,FALSE,, diff --git a/inst/metadata/metadata.csv b/inst/metadata/metadata.csv index b4f1534f..b02be81e 100644 --- a/inst/metadata/metadata.csv +++ b/inst/metadata/metadata.csv @@ -1,12 +1,12 @@ data_type,plot_type,indicator,value_column,error_low_column,error_high_column,indicator_column,indicator_value,name,scale,accuracy,format -survey,choropleth,prevalence,estimate,,,indicator,prevalence,t_(HIV_PREVALENCE),1,,"""0.0%""" -survey,choropleth,art_coverage,estimate,,,indicator,art_coverage,t_(ART_COVERAGE),1,,"""0.0%""" +survey,choropleth,prevalence,estimate,ci_lower,ci_upper,indicator,prevalence,t_(HIV_PREVALENCE),1,,"""0.0%""" +survey,choropleth,art_coverage,estimate,ci_lower,ci_upper,indicator,art_coverage,t_(ART_COVERAGE),1,,"""0.0%""" programme,choropleth,art_current,art_current,,,,,t_(ART_NUMBER_ATTENDING),1,100,"""0,0""" programme,choropleth,art_new,art_new,,,,,t_(ART_NEW_TOTAL),1,100,"""0,0""" programme,choropleth,vl_tested_12mos,vl_tested_12mos,,,,,t_(VL_TESTED_12MOS_TOTAL),1,100,"""0,0""" programme,choropleth,vl_suppressed_12mos,vl_suppressed_12mos,,,,,t_(VL_SUPPRESSED_12MOS_TOTAL),1,100,"""0,0""" -survey,choropleth,recent_infected,estimate,,,indicator,recent_infected,t_(PROPORTION_RECENTLY_INFECTED),1,1,"""0.00%""" -survey,choropleth,viral_suppression_plhiv,estimate,,,indicator,viral_suppression_plhiv,t_(VLS),1,,"""0.0%""" +survey,choropleth,recent_infected,estimate,ci_lower,ci_upper,indicator,recent_infected,t_(PROPORTION_RECENTLY_INFECTED),1,1,"""0.00%""" +survey,choropleth,viral_suppression_plhiv,estimate,ci_lower,ci_upper,indicator,viral_suppression_plhiv,t_(VLS),1,,"""0.0%""" anc,choropleth,anc_prevalence,anc_prevalence,,,,,t_(ANC_HIV_PREVALENCE),1,,"""0.0%""" anc,choropleth,anc_art_coverage,anc_art_coverage,,,,,t_(ANC_PRIOR_ART_COVERAGE),1,,"""0.0%""" output,choropleth,prevalence,mean,lower,upper,indicator,prevalence,t_(HIV_PREVALENCE),1,,"""0.0%""" @@ -89,4 +89,4 @@ input_comparison,barchart,anc_known_pos,value,,,,anc_known_pos,t_(ANC_KNOWN_POS) input_comparison,barchart,anc_tested,value,,,,anc_tested,t_(ANC_TESTED),1,1,"""0,0""" input_comparison,barchart,anc_tested_pos,value,,,,anc_tested_pos,t_(ANC_TESTED_POS),1,1,"""0,0""" population,pyramid,population,population,,,,population,t_(POPULATION),1,1,"""0,0""" -population,pyramid,population_ratio,population,,,,population_ratio,t_(POPULATION_RATIO),1,1,"""0.0%""" +population,pyramid,population_proportion,population,,,,population_proportion,t_(POPULATION_PROPORTION),1,1,"""0.0%""" diff --git a/inst/traduire/en-translation.json b/inst/traduire/en-translation.json index c64d1763..a21d59fb 100644 --- a/inst/traduire/en-translation.json +++ b/inst/traduire/en-translation.json @@ -276,5 +276,6 @@ "DOWNLOAD_AGYW_DESCRIPTION": "Naomi AGYW tool uploaded from Naomi web app", "NUMBER_ON_ART": "Number on ART", "NUMBER_ON_ART_DESC": "Number on ART description", - "MISSING_POP_LEVEL": "Unable to generate model estimates at the {{model_level}} level because population data only available at the {{pop_level}} level/s. Please review model options or population data inputs." + "MISSING_POP_LEVEL": "Unable to generate model estimates at the {{model_level}} level because population data only available at the {{pop_level}} level/s. Please review model options or population data inputs.", + "POPULATION_PROPORTION": "Population proportion" } diff --git a/inst/traduire/fr-translation.json b/inst/traduire/fr-translation.json index 444d7512..7b8e5a72 100644 --- a/inst/traduire/fr-translation.json +++ b/inst/traduire/fr-translation.json @@ -148,7 +148,6 @@ "ART_CHILD": "Enfants sous TARV", "ART_ADULT_SEX_RATIO": "Rapport de masculinité des adultes TARV", "ART_CHILD_ADULT_RATIO": "Rapport enfant-adulte ART sous TARV", - "ANC_STATUS": "Statut connu de la CPN", "ANC_PREVALENCE": "Prévalence CPN", "ANC_ART_COVERAGE": "Couverture de TARV au CPN", "ART_NEW_TOTAL": "TARV (Nouveau)", @@ -254,7 +253,6 @@ "AWARE_PLHIV_PROP_RATIO": "Proportion de PVVIH conscients rapport", "INCIDENCE_RATIO": "Rapport d'incidence du VIH", "WARNING_PROGRAMME_DATA_NOT_EQUAL_TO_SPECTRUM": "Les données infranationales de Naomi ne correspondent pas aux données nationales de Spectrum. Vérifier le tableau de révision des intrants pour: \n{{years}}", - "WARNING_ADDITIONAL": "et {{count}} de plus", "PJNZ_INVALID_ZIP": "Le zip ne contient aucun fichier PJNZ", "ANC_PREVALENCE_AGE_MATCHED": "Prévalence CPN appariés par âge", "ANC_ART_COVERAGE_AGE_MATCHED": "Couverture de TARV au CPN appariés par âge", @@ -275,6 +273,7 @@ "DOWNLOAD_COMPARISON_DESCRIPTION": "Rapport de comparaison Naomi téléchargé à partir de l'application web Naomi", "NUMBER_ON_ART": "Nombre de personnes sous TARV", "NUMBER_ON_ART_DESC": "Number on ART description", - "MISSING_POPULATION_LEVEL": "Les données de population ne sont pas disponibles pour le niveau admin sélectionné pour les projections du modèle. Veuillez revoir la sélection des options du modèle pour vous assurer que la sélection du niveau de la zone est correcte.", - "MISSING_POP_LEVEL": "Unable to generate model estimates at the {{model_level}} level because population data only available at the {{pop_level}} level/s. Please review model options or population data inputs." + "MISSING_POP_LEVEL": "Impossible de générer des estimations de modèle au niveau {{model_level}} car les données de population ne sont disponibles qu'au niveau {{pop_level}}. Veuillez revoir les options du modèle ou les données démographiques.", + "POPULATION_PROPORTION": "Proportion de la population" + } diff --git a/inst/traduire/pt-translation.json b/inst/traduire/pt-translation.json index 131e0833..c2d31675 100644 --- a/inst/traduire/pt-translation.json +++ b/inst/traduire/pt-translation.json @@ -148,7 +148,6 @@ "ART_CHILD": "crianças (<15) em TARV", "ART_ADULT_SEX_RATIO": "Relação homem com mulher em adultos no TARV", "ART_CHILD_ADULT_RATIO": "Relação criança-adulto da TARV", - "ANC_STATUS": "Estatuto conhecido do CPN", "ANC_PREVALENCE": "Prevalência do CPN", "ANC_ART_COVERAGE": "Cobertura TARV arte em CPN", "ART_NEW_TOTAL": "TARV (novo)", @@ -254,7 +253,6 @@ "AWARE_PLHIV_PROP_RATIO": "Proporção de PVVIH com conhecimento relação", "INCIDENCE_RATIO": "Incidência de VIH relação", "WARNING_PROGRAMME_DATA_NOT_EQUAL_TO_SPECTRUM": "Os dados subnacionais da Naomi não são iguais aos dados nacionais da Spectrum. Verificar a tabela de revisão de entradas para: \n{{years}}", - "WARNING_ADDITIONAL": "e {{count}} mais", "PJNZ_INVALID_ZIP": "O zip não contém ficheiros PJNZ", "ANC_PREVALENCE_AGE_MATCHED": "Prevalência do CPN idade compatível", "ANC_ART_COVERAGE_AGE_MATCHED": "Cobertura TARV em CPN idade compatível", @@ -275,6 +273,6 @@ "DOWNLOAD_COMPARISON_DESCRIPTION": "Relatório de comparação Naomi carregado a partir da aplicação web Naomi", "NUMBER_ON_ART": "Nombre de personnes sous TARV", "NUMBER_ON_ART_DESC": "Number on ART description", - "MISSING_POPULATION_LEVEL": "Os dados da população não estão disponíveis para o nível administrativo selecionado para as projecções do modelo. Rever a seleção das opções do modelo para garantir que a seleção do nível de área está correta.", - "MISSING_POP_LEVEL": "Unable to generate model estimates at the {{model_level}} level because population data only available at the {{pop_level}} level/s. Please review model options or population data inputs." + "MISSING_POP_LEVEL": "Não é possível gerar estimativas do modelo ao nível {{model_level}} porque os dados da população só estão disponíveis ao nível {{pop_level}}. Reveja as opções do modelo ou as entradas de dados da população.", + "POPULATION_PROPORTION": "Proporção da população" } diff --git a/scripts/collect_translations.R b/scripts/collect_translations.R new file mode 100755 index 00000000..77521523 --- /dev/null +++ b/scripts/collect_translations.R @@ -0,0 +1,85 @@ +#!/usr/bin/env Rscript + +## Run as ./collect_translations.R 2021-11-17 +## Date format is YYYY-MM-DD +args = commandArgs(trailingOnly=TRUE) +if (!is.null(args[1]) && !is.na(args[1])) { + from_date <- args[1] +} else { + from_date <- NULL +} + +files <- file.path("inst/traduire", list.files("inst/traduire")) + +## Helpers for reading file from git archive +`%||%` <- function(a, b) { + if (is.null(a)) b else a +} +system3 <- function(command, args) { + res <- suppressWarnings(system2(command, args, stdout = TRUE, stderr = TRUE)) + code <- attr(res, "status") %||% 0 + attr(res, "status") <- NULL + list(success = code == 0, + code = code, + output = res) +} +git_run <- function(args, root = NULL, check = FALSE) { + if (!is.null(root)) { + args <- c("-C", root, args) + } + res <- system3(git, args) + if (check && !res$success) { + stop(sprintf("Error code %d running command:\n%s", + res$code, paste0(" > ", res$output, collapse = "\n"))) + } + res +} +git_show <- function(path, date) { + path <- sprintf("HEAD@{%s}:./%s", date, path) + git <- unname(Sys.which("git")) + res <- system3(git, c("show", path)) + if (!res$success) { + stop(sprintf("Error code %d running command:\n%s", + res$code, paste0(" > ", res$output, collapse = "\n"))) + } + res$output +} + +yml <- lapply(files, function(file) { + lang <- strsplit(basename(file), "-")[[1]][1] + current_strings <- jsonlite::read_json(file) + d <- data.frame(names(current_strings), + unname(unlist(current_strings))) + colnames(d) <- c("key", lang) + d +}) + +if (!is.null(from_date)) { + old_yml <- lapply(files, function(file) { + lang <- strsplit(basename(file), "-")[[1]][1] + content <- git_show(file, from_date) + strings <- jsonlite::fromJSON(content, simplifyVector = FALSE) + d <- data.frame(names(strings), + unname(unlist(strings))) + colnames(d) <- c("key", lang) + d + }) + + yml <- Map(function(old, current) { + ## Keep rows from current not in old or if the value has changed + row.names(current) <- current$key + row.names(old) <- old$key + keys_out <- vapply(row.names(current), function(key) { + !(key %in% row.names(old)) || any(current[key, ] != old[key, ]) + }, logical(1)) + row.names(current) <- NULL + current[keys_out, ] + }, old_yml, yml) +} + +translations <- Reduce(function(x, y) merge(x, y, by = "key", all = TRUE), + yml, accumulate = FALSE) + +out <- sprintf("translations-%s.csv", Sys.Date()) +message(sprintf("Saving translations to %s", out)) +write.csv(translations, out, row.names = FALSE, na = "") diff --git a/src/tmb.cpp b/src/tmb.cpp index 8fb3173b..9e8b79c3 100644 --- a/src/tmb.cpp +++ b/src/tmb.cpp @@ -104,14 +104,17 @@ Type objective_function::operator() () DATA_VECTOR(log_asfr_t1_offset); DATA_VECTOR(log_asfr_t2_offset); DATA_VECTOR(log_asfr_t3_offset); + DATA_VECTOR(log_asfr_t4_offset); DATA_VECTOR(logit_anc_rho_t1_offset); DATA_VECTOR(logit_anc_rho_t2_offset); DATA_VECTOR(logit_anc_rho_t3_offset); + DATA_VECTOR(logit_anc_rho_t4_offset); DATA_VECTOR(logit_anc_alpha_t1_offset); DATA_VECTOR(logit_anc_alpha_t2_offset); DATA_VECTOR(logit_anc_alpha_t3_offset); + DATA_VECTOR(logit_anc_alpha_t4_offset); DATA_SPARSE_MATRIX(Z_asfr_x); DATA_SPARSE_MATRIX(Z_ancrho_x); @@ -1019,6 +1022,24 @@ Type objective_function::operator() () vector infections_t4(lambda_t4 * (population_t4 - plhiv_t4)); + // Note: currently assuming same district effects parameters from t2 for t4 + vector mu_anc_rho_t4(logit(rho_t4) + + logit_anc_rho_t2_offset + + X_ancrho * vector(beta_anc_rho + beta_anc_rho_t2) + + Z_ancrho_x * vector(ui_anc_rho_x + ui_anc_rho_xt)); + vector anc_rho_t4(invlogit(mu_anc_rho_t4)); + + vector mu_anc_alpha_t4(mu_alpha_t4 + + logit_anc_alpha_t4_offset + + X_ancalpha * vector(beta_anc_alpha + beta_anc_alpha_t2) + + Z_ancalpha_x * vector(ui_anc_alpha_x + ui_anc_alpha_xt)); + vector anc_alpha_t4(invlogit(mu_anc_alpha_t4)); + + vector anc_clients_t4(population_t4 * exp(log_asfr_t4_offset + mu_asfr)); + vector anc_plhiv_t4(anc_clients_t4 * anc_rho_t4); + vector anc_already_art_t4(anc_plhiv_t4 * anc_alpha_t4); + + vector prop_art_ij_t4((Xart_idx * prop_art_t4) * (Xart_gamma * gamma_art_t2)); // Note: using same ART attendance as T2 vector population_ij_t4(Xart_idx * population_t4); vector artnum_ij_t4(population_ij_t4 * prop_art_ij_t4); @@ -1040,6 +1061,18 @@ Type objective_function::operator() () vector infections_t4_out(A_out * infections_t4); vector lambda_t4_out(infections_t4_out / (population_t4_out - plhiv_t4_out)); + vector anc_clients_t4_out(A_anc_out * anc_clients_t4); + vector anc_plhiv_t4_out(A_anc_out * anc_plhiv_t4); + vector anc_already_art_t4_out(A_anc_out * anc_already_art_t4); + vector anc_art_new_t4_out(anc_plhiv_t4_out - anc_already_art_t4_out); + vector anc_known_pos_t4_out(anc_already_art_t4_out); + vector anc_tested_pos_t4_out(anc_plhiv_t4_out - anc_known_pos_t4_out); + vector anc_tested_neg_t4_out(anc_clients_t4_out - anc_plhiv_t4_out); + + vector anc_rho_t4_out(anc_plhiv_t4_out / anc_clients_t4_out); + vector anc_alpha_t4_out(anc_already_art_t4_out / anc_plhiv_t4_out); + + REPORT(population_t4_out); // REPORT(rho_t4_out); REPORT(plhiv_t4_out); @@ -1053,6 +1086,17 @@ Type objective_function::operator() () REPORT(lambda_t4_out); REPORT(infections_t4_out); + REPORT(anc_clients_t4_out); + REPORT(anc_plhiv_t4_out); + REPORT(anc_already_art_t4_out); + REPORT(anc_art_new_t4_out); + REPORT(anc_known_pos_t4_out); + REPORT(anc_tested_pos_t4_out); + REPORT(anc_tested_neg_t4_out); + REPORT(anc_rho_t4_out); + REPORT(anc_alpha_t4_out); + + // Projection to time 5 // Only PLHIV, ART and infections calculated. No ANC or awareness indicators diff --git a/tests/testthat/test-01-run-model.R b/tests/testthat/test-01-run-model.R index 6ef10e97..3a2229ff 100644 --- a/tests/testthat/test-01-run-model.R +++ b/tests/testthat/test-01-run-model.R @@ -311,12 +311,12 @@ test_that("model run can be calibrated", { ## * 1 output time - 4 indicators [NOT INCLUDED IN PLOT OUTPUTS] ## ## ANC indicators outputs - ## 3 = number or output times + ## 4 = number or output times ## 9 = number of ANC indicators ## 9 = number of areas ## 12 = number of ANC age groups expect_equal(nrow(calibrated_output_obj$output_package$indicators), - 33 * 3 * 9 * (3 * 16 + 1 * 5 + 1 * 4) + 3 * 9 * 9 * 12) + 33 * 3 * 9 * (3 * 16 + 1 * 5 + 1 * 4) + 4 * 9 * 9 * 12) ## Plot data output: T3 and T4 indicators not included -> fewer rows plot_data_output <- read_hintr_output(calibrated_output$plot_data_path) @@ -505,7 +505,7 @@ test_that("Model can be run without .shiny90 file", { ## Check there is some data ## 11 indicators (5 fewer because missing awareness of status indicators) expect_equal(nrow(indicators_output$output_package$indicators), - 33 * 3 * 9 * (3 * 11 + 1 * 5 + 1 * 4) + 3 * 9 * 9 * 12) + 33 * 3 * 9 * (3 * 11 + 1 * 5 + 1 * 4) + 4 * 9 * 9 * 12) }) test_that("hintr_run_model can skip validation", { diff --git a/tests/testthat/test-metadata.R b/tests/testthat/test-metadata.R index 7a8d374f..2c192a80 100644 --- a/tests/testthat/test-metadata.R +++ b/tests/testthat/test-metadata.R @@ -89,7 +89,7 @@ test_that("can get plot metadata for missing country with defaults", { "art_coverage_ratio", "aware_plhiv_prop_ratio", "incidence_ratio", "anc_prevalence_age_matched", "anc_art_coverage_age_matched", "number_on_art", - "anc_tested")) + "anc_tested", "population_proportion")) }) test_that("colour scales metadata is well formed", { @@ -108,7 +108,7 @@ test_that("colour scales metadata is well formed", { "art_current_ratio", "unaware_plhiv_num_ratio", "prevalence_ratio", "art_coverage_ratio", "aware_plhiv_prop_ratio", "incidence_ratio", "anc_prevalence_age_matched", "anc_art_coverage_age_matched", - "number_on_art", "anc_tested")) + "number_on_art", "anc_tested", "population_proportion")) expect_equal(nrow(unique(scales[, c("iso3", "indicator")])), nrow(scales)) expect_true(is.numeric(scales$min)) expect_true(is.numeric(scales$max)) @@ -142,7 +142,8 @@ test_that("metadata is well formed", { "infections_ratio", "art_current_ratio", "unaware_plhiv_num_ratio", "prevalence_ratio", "art_coverage_ratio", "aware_plhiv_prop_ratio", "incidence_ratio", "anc_prevalence_age_matched", - "anc_art_coverage_age_matched", "number_on_art", "anc_tested")) + "anc_art_coverage_age_matched", "number_on_art", "anc_tested", + "population_proportion")) expect_equal(nrow(unique(meta[, c("data_type", "plot_type", "indicator")])), nrow(meta)) expect_true(all(meta$plot_type %in% c("choropleth", "barchart", "pyramid"))) @@ -169,7 +170,7 @@ test_that("metadata is well formed", { "ART coverage ratio", "Proportion PLHIV aware ratio", "Incidence ratio", "ANC prevalence age matched", "ANC ART coverage age matched", "Number on ART", - "ANC tested")) + "ANC tested", "Population proportion")) expect_equal( colnames(meta), c("data_type", "plot_type", "indicator", "value_column", "error_low_column",