From 8924fd1273665d0c7a01d8ae4d7012280b184835 Mon Sep 17 00:00:00 2001 From: Timon Date: Tue, 10 Mar 2026 14:06:45 +0100 Subject: [PATCH] Refactor parallel processing in growth model utilities and streamline field alert generation in report utilities --- r_app/30_growth_model_utils.R | 196 ++++++----- ..._CI_report_with_kpis_agronomic_support.Rmd | 317 ++---------------- r_app/90_report_utils.R | 292 +++++++++++++++- r_app/translations/translations.xlsx | Bin 46031 -> 46387 bytes 4 files changed, 410 insertions(+), 395 deletions(-) diff --git a/r_app/30_growth_model_utils.R b/r_app/30_growth_model_utils.R index cca107e..189d968 100644 --- a/r_app/30_growth_model_utils.R +++ b/r_app/30_growth_model_utils.R @@ -71,54 +71,43 @@ load_combined_ci_data <- function(daily_vals_dir, harvesting_data = NULL) { safe_log(sprintf("Filtered to %d files within harvest season date range", length(all_daily_files))) } - # Set up parallel future plan (Windows PSOCK multisession; Mac/Linux can use forking) - # Automatically detect available cores and limit to reasonable number - n_cores <- min(parallel::detectCores() - 1, 8) # Use max 8 cores (diminishing returns after) - future::plan(strategy = future::multisession, workers = n_cores) - safe_log(sprintf("Using %d parallel workers for file I/O", n_cores)) - - # Parallel file reading: future_map_dfr processes each file in parallel - # Returns combined dataframe directly (no need to rbind) - combined_long <- furrr::future_map_dfr( - all_daily_files, - .progress = TRUE, - .options = furrr::furrr_options(seed = TRUE), - function(file) { - # Extract date from filename: {YYYY-MM-DD}.rds - filename <- basename(file) - date_str <- tools::file_path_sans_ext(filename) - - # Parse date - if (nchar(date_str) == 10 && grepl("^\\d{4}-\\d{2}-\\d{2}$", date_str)) { - parsed_date <- as.Date(date_str, format = "%Y-%m-%d") - } else { - return(data.frame()) # Return empty dataframe if parse fails - } - - if (is.na(parsed_date)) { - return(data.frame()) - } - - # Read RDS file - tryCatch({ - rds_data <- readRDS(file) - - if (is.null(rds_data) || nrow(rds_data) == 0) { - return(data.frame()) - } - - # Add date column to the data - rds_data %>% - dplyr::mutate(Date = parsed_date) - - }, error = function(e) { - return(data.frame()) # Return empty dataframe on error - }) - } - ) - - # Return to sequential processing to avoid nested parallelism - future::plan(future::sequential) + # Adaptive core count: scale with file count to avoid parallel overhead on small projects + n_files <- length(all_daily_files) + n_cores_io <- if (n_files < 200) { + 1 + } else if (n_files < 600) { + 2 + } else if (n_files < 1500) { + min(parallel::detectCores() - 1, 4) + } else { + min(parallel::detectCores() - 1, 8) + } + safe_log(sprintf("Using %d parallel workers for file I/O (%d files)", n_cores_io, n_files)) + + read_one_file <- function(file) { + filename <- basename(file) + date_str <- tools::file_path_sans_ext(filename) + if (nchar(date_str) != 10 || !grepl("^\\d{4}-\\d{2}-\\d{2}$", date_str)) return(data.frame()) + parsed_date <- as.Date(date_str, format = "%Y-%m-%d") + if (is.na(parsed_date)) return(data.frame()) + tryCatch({ + rds_data <- readRDS(file) + if (is.null(rds_data) || nrow(rds_data) == 0) return(data.frame()) + rds_data %>% dplyr::mutate(Date = parsed_date) + }, error = function(e) data.frame()) + } + + if (n_cores_io > 1) { + future::plan(strategy = future::multisession, workers = n_cores_io) + combined_long <- furrr::future_map_dfr( + all_daily_files, read_one_file, + .progress = TRUE, + .options = furrr::furrr_options(seed = TRUE) + ) + future::plan(future::sequential) + } else { + combined_long <- purrr::map_dfr(all_daily_files, read_one_file) + } if (nrow(combined_long) == 0) { safe_log("Warning: No valid CI data loaded from daily files", "WARNING") @@ -244,57 +233,81 @@ generate_interpolated_ci_data <- function(years, harvesting_data, ci_data) { failed_fields <- list() total_fields <- 0 successful_fields <- 0 - + + # Pre-compute total valid fields across all years to decide core count once + total_valid_fields <- sum(sapply(years, function(yr) { + sfs <- harvesting_data %>% + dplyr::filter(year == yr, !is.na(season_start)) %>% + dplyr::pull(sub_field) + sum(sfs %in% unique(ci_data$sub_field)) + })) + + # Adaptive core count: scale with field count, avoid parallel overhead for small projects + n_cores_interp <- if (total_valid_fields <= 1) { + 1 + } else if (total_valid_fields <= 10) { + 2 + } else if (total_valid_fields <= 50) { + min(parallel::detectCores() - 1, 4) + } else { + min(parallel::detectCores() - 1, 8) + } + + safe_log(sprintf("Interpolating %d fields across %d year(s) using %d worker(s)", + total_valid_fields, length(years), n_cores_interp)) + + # Set up parallel plan once before the year loop (avoid per-year startup cost) + if (n_cores_interp > 1) { + future::plan(strategy = future::multisession, workers = n_cores_interp) + } + # Process each year result <- purrr::map_df(years, function(yr) { # Get the fields harvested in this year with valid season start dates sub_fields <- harvesting_data %>% dplyr::filter(year == yr, !is.na(season_start)) %>% dplyr::pull(sub_field) - - if (length(sub_fields) == 0) { - return(data.frame()) - } - + + if (length(sub_fields) == 0) return(data.frame()) + # Filter sub_fields to only include those with value data in ci_data valid_sub_fields <- sub_fields %>% purrr::keep(~ any(ci_data$sub_field == .x)) - - if (length(valid_sub_fields) == 0) { - return(data.frame()) - } - + + if (length(valid_sub_fields) == 0) return(data.frame()) + total_fields <<- total_fields + length(valid_sub_fields) - safe_log(sprintf("Year %d: Processing %d fields in parallel", yr, length(valid_sub_fields))) - - # Set up parallel future plan for field interpolation - # Allocate 1 core per ~100 fields (with minimum 2 cores) - n_cores <- max(2, min(parallel::detectCores() - 1, ceiling(length(valid_sub_fields) / 100))) - future::plan(strategy = future::multisession, workers = n_cores) - - # PARALLELIZE: Process all fields in parallel (each extracts & interpolates independently) - result_list <- furrr::future_map( - valid_sub_fields, - .progress = TRUE, - .options = furrr::furrr_options(seed = TRUE), - function(field) { - # Call with verbose=FALSE to suppress warnings during parallel iteration - extract_CI_data(field, - harvesting_data = harvesting_data, - field_CI_data = ci_data, + safe_log(sprintf("Year %d: Processing %d fields", yr, length(valid_sub_fields))) + + # Process fields — parallel if workers > 1, otherwise plain map (no overhead) + if (n_cores_interp > 1) { + result_list <- furrr::future_map( + valid_sub_fields, + .progress = TRUE, + .options = furrr::furrr_options(seed = TRUE), + function(field) { + extract_CI_data(field, + harvesting_data = harvesting_data, + field_CI_data = ci_data, + season = yr, + verbose = FALSE) + } + ) + } else { + result_list <- purrr::map(valid_sub_fields, function(field) { + extract_CI_data(field, + harvesting_data = harvesting_data, + field_CI_data = ci_data, season = yr, - verbose = FALSE) - } - ) - - # Return to sequential processing - future::plan(future::sequential) - + verbose = TRUE) + }) + } + # Process results and tracking for (i in seq_along(result_list)) { field_result <- result_list[[i]] field_name <- valid_sub_fields[i] - + if (nrow(field_result) > 0) { successful_fields <<- successful_fields + 1 } else { @@ -305,15 +318,16 @@ generate_interpolated_ci_data <- function(years, harvesting_data, ci_data) { ) } } - + # Combine all results for this year - result_list <- result_list[sapply(result_list, nrow) > 0] # Keep only non-empty - if (length(result_list) > 0) { - purrr::list_rbind(result_list) - } else { - data.frame() - } + result_list <- result_list[sapply(result_list, nrow) > 0] + if (length(result_list) > 0) purrr::list_rbind(result_list) else data.frame() }) + + # Tear down parallel plan once after all years are processed + if (n_cores_interp > 1) { + future::plan(future::sequential) + } # Print summary safe_log(sprintf("\n=== Interpolation Summary ===")) diff --git a/r_app/90_CI_report_with_kpis_agronomic_support.Rmd b/r_app/90_CI_report_with_kpis_agronomic_support.Rmd index f4f8179..098d00b 100644 --- a/r_app/90_CI_report_with_kpis_agronomic_support.Rmd +++ b/r_app/90_CI_report_with_kpis_agronomic_support.Rmd @@ -498,59 +498,7 @@ tryCatch({ localisation <<- NULL }) -# tr_key() is defined in 90_report_utils.R (sourced above) - -# ============================================================================ -# SHARED TREND MAPPING HELPER -# ============================================================================ -# Canonical function for converting trend text to arrows/formatted text -# Normalizes all legacy and current trend category names to standardized output -# Used by: combined_kpi_table, field_details_table, and compact_field_display chunks -map_trend_to_arrow <- function(text_vec, include_text = FALSE) { - # Normalize: convert to character and lowercase - text_lower <- tolower(as.character(text_vec)) - - # Apply mapping to each element - sapply(text_lower, function(text) { - # Handle NA and empty values - if (is.na(text) || text == "" || nchar(trimws(text)) == 0) { - return(NA_character_) - } - - # Determine category and build output with translated labels - # Using word-boundary anchored patterns (perl=TRUE) to avoid substring mis-matches - if (grepl("\\bstrong growth\\b", text, perl = TRUE)) { - arrow <- "↑↑" - trans_key <- "Strong growth" - } else if (grepl("\\b(?:slight|weak) growth\\b|(? @@ -768,144 +716,12 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table `r tr_key("field_alerts")` ```{r field_alerts_table, echo=FALSE, results='asis'} -# Generate alerts for all fields -generate_field_alerts <- function(field_details_table) { - if (is.null(field_details_table) || nrow(field_details_table) == 0) { - return(NULL) # Return NULL to signal no data - } - - # Check for required columns - required_cols <- c("Field", "Growth Uniformity", "Yield Forecast (t/ha)", - "Gap Score", "Decline Risk", "Patchiness Risk", "Mean CI", "CV Value", "Moran's I") - missing_cols <- setdiff(required_cols, colnames(field_details_table)) - - if (length(missing_cols) > 0) { - message("Field details missing required columns: ", paste(missing_cols, collapse = ", ")) - return(NULL) # Return NULL if required columns are missing - } - - alerts_list <- list() - - # Get unique fields - unique_fields <- unique(field_details_table$Field) - - for (field_name in unique_fields) { - field_data <- field_details_table %>% filter(Field == field_name) - - # Aggregate data for the field - field_summary <- field_data %>% - summarise( - uniformity_levels = paste(unique(`Growth Uniformity`), collapse = "/"), - avg_yield_forecast = mean(`Yield Forecast (t/ha)`, na.rm = TRUE), - max_gap_score = max(`Gap Score`, na.rm = TRUE), - highest_decline_risk = case_when( - any(`Decline Risk` == "Very-high") ~ "Very-high", - any(`Decline Risk` == "High") ~ "High", - any(`Decline Risk` == "Moderate") ~ "Moderate", - any(`Decline Risk` == "Low") ~ "Low", - TRUE ~ "Unknown" - ), - highest_patchiness_risk = case_when( - any(`Patchiness Risk` == "High") ~ "High", - any(`Patchiness Risk` == "Medium") ~ "Medium", - any(`Patchiness Risk` == "Low") ~ "Low", - any(`Patchiness Risk` == "Minimal") ~ "Minimal", - TRUE ~ "Unknown" - ), - avg_mean_ci = mean(`Mean CI`, na.rm = TRUE), - avg_cv = mean(`CV Value`, na.rm = TRUE), - .groups = 'drop' - ) - - # Generate alerts for this field based on simplified CV-Moran's I priority system (3 levels) - field_alerts <- c() - - # Get CV and Moran's I values - avg_cv <- field_summary$avg_cv - morans_i <- mean(field_data[["Moran's I"]], na.rm = TRUE) - - # Determine priority level (1=Urgent, 2=Monitor, 3=No stress) - priority_level <- get_field_priority_level(avg_cv, morans_i) - - # Generate alerts based on priority level - if (priority_level == 1) { - field_alerts <- c(field_alerts, tr_key("priority")) - } else if (priority_level == 2) { - field_alerts <- c(field_alerts, tr_key("monitor")) - } - # Priority 3: No alert (no stress) - - # Keep other alerts for decline risk, patchiness risk, gap score - if (field_summary$highest_decline_risk %in% c("High", "Very-high")) { - field_alerts <- c(field_alerts, tr_key("growth_decline")) - } - if (field_summary$highest_patchiness_risk == "High") { - field_alerts <- c(field_alerts, tr_key("high_patchiness")) - } - if (field_summary$max_gap_score > 20) { - field_alerts <- c(field_alerts, tr_key("gaps_present")) - } - - # Only add alerts if there are any (skip fields with no alerts) - if (length(field_alerts) > 0) { - # Add to alerts list - for (alert in field_alerts) { - alerts_list[[length(alerts_list) + 1]] <- data.frame( - Field = field_name, - Alert = alert - ) - } - } - } - - # Combine all alerts - if (length(alerts_list) > 0) { - alerts_df <- do.call(rbind, alerts_list) - return(alerts_df) - } else { - return(data.frame(Field = character(), Alert = character())) - } -} +# generate_field_alerts() is defined in 90_report_utils.R (sourced above). +# field_details_table has already been normalised by normalize_field_details_columns(). # Generate and display alerts table if (exists("field_details_table") && !is.null(field_details_table) && nrow(field_details_table) > 0) { - # Adapter: Map normalized column names back to legacy names for generate_field_alerts() - # (generates from the normalized schema created by normalize_field_details_columns + column_mappings) - field_details_for_alerts <- field_details_table - - # Rename normalized columns back to legacy names (only if they exist) - if ("Field_id" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(Field = Field_id) - } - if ("Mean_CI" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Mean CI` = Mean_CI) - } - if ("CV" %in% names(field_details_for_alerts) && !("CV Value" %in% names(field_details_for_alerts))) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`CV Value` = CV) - } - if ("TCH_Forecasted" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Yield Forecast (t/ha)` = TCH_Forecasted) - } - if ("Gap_Score" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Gap Score` = Gap_Score) - } - if ("Uniformity_Category" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Growth Uniformity` = Uniformity_Category) - } - if ("Decline_Risk" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Decline Risk` = Decline_Risk) - } - if ("Decline_Severity" %in% names(field_details_for_alerts) && !("Decline Risk" %in% names(field_details_for_alerts))) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Decline Risk` = Decline_Severity) - } - if ("Patchiness_Risk" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Patchiness Risk` = Patchiness_Risk) - } - if ("Morans_I" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Moran's I` = Morans_I) - } - - alerts_data <- generate_field_alerts(field_details_for_alerts) + alerts_data <- generate_field_alerts(field_details_table) if (!is.null(alerts_data) && nrow(alerts_data) > 0) { ft <- flextable(alerts_data) %>% # set_caption("Field Alerts Summary") %>% @@ -1014,36 +830,23 @@ if (!exists("field_details_table") || is.null(field_details_table)) { tryCatch({ safe_log("Starting farm-level raster aggregation for overview maps") - # Helper function to safely aggregate mosaics for a specific week - aggregate_mosaics_safe <- function(week_num, year_num, label) { - tryCatch({ - safe_log(paste("Aggregating mosaics for", label, "(week", week_num, ",", year_num, ")")) - - # Call the utility function from 90_report_utils.R - # This function reads all per-field mosaics and merges them into a single raster - farm_mosaic <- aggregate_per_field_mosaics_to_farm_level( - weekly_mosaic_dir = weekly_CI_mosaic, - target_week = week_num, - target_year = year_num - ) - - if (!is.null(farm_mosaic)) { - safe_log(paste("✓ Successfully aggregated farm mosaic for", label, "")) - return(farm_mosaic) - } else { - safe_log(paste("Warning: Farm mosaic is NULL for", label), "WARNING") - return(NULL) - } - }, error = function(e) { - safe_log(paste("Error aggregating mosaics for", label, ":", e$message), "WARNING") - return(NULL) - }) - } - - # Aggregate mosaics for three weeks: current, week-1, week-3 - farm_mosaic_current <- aggregate_mosaics_safe(current_week, current_iso_year, "current week") - farm_mosaic_minus_1 <- aggregate_mosaics_safe(as.numeric(week_minus_1), week_minus_1_year, "week-1") - farm_mosaic_minus_3 <- aggregate_mosaics_safe(as.numeric(week_minus_3), week_minus_3_year, "week-3") + # Aggregate per-field mosaics into farm-level rasters for current, week-1, week-3 + # aggregate_per_field_mosaics_to_farm_level() is defined in 90_report_utils.R (sourced above) + farm_mosaic_current <- aggregate_per_field_mosaics_to_farm_level( + weekly_mosaic_dir = weekly_CI_mosaic, + target_week = current_week, + target_year = current_iso_year + ) + farm_mosaic_minus_1 <- aggregate_per_field_mosaics_to_farm_level( + weekly_mosaic_dir = weekly_CI_mosaic, + target_week = as.numeric(week_minus_1), + target_year = week_minus_1_year + ) + farm_mosaic_minus_3 <- aggregate_per_field_mosaics_to_farm_level( + weekly_mosaic_dir = weekly_CI_mosaic, + target_week = as.numeric(week_minus_3), + target_year = week_minus_3_year + ) # Extract CI band (5th band, or named "CI") from each aggregated mosaic farm_ci_current <- NULL @@ -1098,18 +901,7 @@ tryCatch({ AllPivots0_ll <- AllPivots0 target_crs <- "EPSG:4326" - downsample_raster <- function(r, max_cells = 2000000) { - if (is.null(r)) { - return(NULL) - } - n_cells <- terra::ncell(r) - if (!is.na(n_cells) && n_cells > max_cells) { - fact <- ceiling(sqrt(n_cells / max_cells)) - safe_log(paste("Downsampling raster by factor", fact), "INFO") - return(terra::aggregate(r, fact = fact, fun = mean, na.rm = TRUE)) - } - r - } + # downsample_raster() is defined in 90_report_utils.R (sourced above) if (!is.null(farm_ci_current) && !terra::is.lonlat(farm_ci_current)) { farm_ci_current_ll <- terra::project(farm_ci_current, target_crs, method = "bilinear") @@ -1383,14 +1175,8 @@ tryCatch({ dplyr::group_by(field) %>% dplyr::summarise(.groups = 'drop') - # Helper to get week/year from a date - get_week_year <- function(date) { - list( - week = as.numeric(format(date, "%V")), - year = as.numeric(format(date, "%G")) - ) - } - + # get_week_year() is defined in 90_report_utils.R (sourced above) + # Calculate week/year for current and historical weeks current_ww <- get_week_year(as.Date(today)) minus_1_ww <- get_week_year(as.Date(today) - lubridate::weeks(1)) @@ -1400,26 +1186,8 @@ tryCatch({ message(paste("Processing", nrow(AllPivots_merged), "fields for weeks:", current_ww$week, minus_1_ww$week, minus_2_ww$week, minus_3_ww$week)) - # Helper function to safely load per-field mosaic if it exists - load_per_field_mosaic <- function(base_dir, field_name, week, year) { - path <- file.path(base_dir, field_name, paste0("week_", sprintf("%02d", week), "_", year, ".tif")) - if (file.exists(path)) { - tryCatch({ - rast_obj <- terra::rast(path) - # Extract CI band if present, otherwise first band - if ("CI" %in% names(rast_obj)) { - return(rast_obj[["CI"]]) - } else if (nlyr(rast_obj) > 0) { - return(rast_obj[[1]]) - } - }, error = function(e) { - message(paste("Warning: Could not load", path, ":", e$message)) - return(NULL) - }) - } - return(NULL) - } - + # load_per_field_mosaic() is defined in 90_report_utils.R (sourced above) + # Iterate through fields using purrr::walk purrr::walk(AllPivots_merged$field, function(field_name) { tryCatch({ @@ -1558,38 +1326,7 @@ tryCatch({ }) ``` -```{r generate_subarea_visualizations, eval=FALSE, echo=FALSE, fig.height=3.8, fig.width=6.5, message=FALSE, warning=FALSE, dpi=150, results='asis'} -# Alternative visualization grouped by sub-area (disabled by default) -tryCatch({ - # Group pivots by sub-area - pivots_grouped <- AllPivots0 - - # Iterate over each subgroup - for (subgroup in unique(pivots_grouped$sub_area)) { - # Add subgroup heading - cat("\n") - cat("## Subgroup: ", subgroup, "\n") - - # Filter data for current subgroup - subset_data <- dplyr::filter(pivots_grouped, sub_area == subgroup) - - # Generate visualizations for each field in the subgroup - purrr::walk(subset_data$field, function(field_name) { - cat("\n") - ci_plot(field_name) - cat("\n") - cum_ci_plot(field_name) - cat("\n") - }) - - # Add page break after each subgroup - cat("\\newpage\n") - } -}, error = function(e) { - safe_log(paste("Error in subarea visualization section:", e$message), "ERROR") - cat("Error generating subarea plots. See log for details.\n") -}) -``` +cat("\\newpage\n\n") `r tr_key("detailed_field")` diff --git a/r_app/90_report_utils.R b/r_app/90_report_utils.R index a748fb7..b713290 100644 --- a/r_app/90_report_utils.R +++ b/r_app/90_report_utils.R @@ -1214,31 +1214,295 @@ generate_field_kpi_summary <- function(field_name, field_details_table, CI_quadr #' Normalize field_details_table column structure #' -#' Standardizes column names and ensures all expected KPI columns exist. -#' Handles Field → Field_id rename and injects missing columns as NA. +#' Standardizes column names from various legacy and pipeline-generated schemas +#' into a single canonical set, then ensures all expected KPI columns exist +#' (adding \code{NA} columns for any that are absent). #' -#' @param field_details_table data.frame to normalize -#' @return data.frame with standardized column structure +#' Rename rules applied in order: +#' \itemize{ +#' \item \code{Field} → \code{Field_id} +#' \item \code{Mean CI} → \code{Mean_CI} +#' \item \code{CV Value} → \code{CV} +#' \item \code{TCH_Forecasted} / \code{Yield Forecast (t/ha)} → \code{TCH_Forecasted} +#' \item \code{Gap Score} → \code{Gap_Score} +#' \item \code{Growth Uniformity} / \code{Uniformity_Category} → \code{Uniformity_Interpretation} +#' \item \code{Decline_Risk} → \code{Decline_Severity} +#' \item \code{Moran's I} / \code{Morans_I} → \code{Morans_I} +#' } +#' +#' @param field_details_table A data.frame to normalize. +#' @return A data.frame with standardized column names and all expected KPI +#' columns present (missing ones filled with \code{NA}). normalize_field_details_columns <- function(field_details_table) { if (is.null(field_details_table) || nrow(field_details_table) == 0) { return(field_details_table) } - - # Rename Field → Field_id if needed - if ("Field" %in% names(field_details_table) && !("Field_id" %in% names(field_details_table))) { - field_details_table <- field_details_table %>% - dplyr::rename(Field_id = Field) + + rename_if_missing <- function(df, from, to) { + if (from %in% names(df) && !to %in% names(df)) + df <- dplyr::rename(df, !!to := !!rlang::sym(from)) + df } - + + field_details_table <- field_details_table %>% + rename_if_missing("Field", "Field_id") %>% + rename_if_missing("Mean CI", "Mean_CI") %>% + rename_if_missing("CV Value", "CV") %>% + rename_if_missing("Yield Forecast (t/ha)", "TCH_Forecasted") %>% + rename_if_missing("Gap Score", "Gap_Score") %>% + rename_if_missing("Growth Uniformity", "Uniformity_Interpretation") %>% + rename_if_missing("Uniformity_Category", "Uniformity_Interpretation") %>% + rename_if_missing("Decline_Risk", "Decline_Severity") %>% + rename_if_missing("Moran's I", "Morans_I") + # Ensure all expected KPI columns exist; add as NA if missing - expected_cols <- c("Field_id", "Mean_CI", "CV", "TCH_Forecasted", "Gap_Score", - "Trend_Interpretation", "Weekly_CI_Change", "Uniformity_Interpretation", - "Decline_Severity", "Patchiness_Risk") + expected_cols <- c( + "Field_id", "Mean_CI", "CV", "Morans_I", "TCH_Forecasted", "Gap_Score", + "Trend_Interpretation", "Weekly_CI_Change", "Uniformity_Interpretation", + "Decline_Severity", "Patchiness_Risk" + ) for (col in expected_cols) { if (!col %in% names(field_details_table)) { field_details_table[[col]] <- NA } } - + return(field_details_table) } + +# ============================================================================== +# TREND / ARROW HELPERS +# ============================================================================== + +#' Map trend text to arrow symbols or formatted labels +#' +#' Converts trend category strings (e.g. \code{"strong growth"}, +#' \code{"slight decline"}) to Unicode arrow symbols, optionally combined with +#' translated text labels. Normalises legacy and current trend category names +#' to a canonical output. Vectorised over \code{text_vec}. +#' +#' @param text_vec Character vector of trend category strings. +#' @param include_text Logical. If \code{TRUE}, returns +#' \code{"Label (arrow)"}; if \code{FALSE} (default), returns the arrow +#' symbol only. +#' @return Character vector the same length as \code{text_vec}. \code{NA} is +#' returned for missing / empty inputs; an em-dash (\code{"—"}) is returned +#' for unrecognised values when \code{include_text = FALSE}. +#' @seealso \code{\link{tr_key}} +#' +map_trend_to_arrow <- function(text_vec, include_text = FALSE) { + text_lower <- tolower(as.character(text_vec)) + + sapply(text_lower, function(text) { + if (is.na(text) || nchar(trimws(text)) == 0) return(NA_character_) + + if (grepl("\\bstrong growth\\b", text, perl = TRUE)) { + arrow <- "↑↑"; trans_key <- "Strong growth" + } else if (grepl("\\b(?:slight|weak) growth\\b|(? max_cells) { + fact <- ceiling(sqrt(n_cells / max_cells)) + safe_log(paste("Downsampling raster by factor", fact), "INFO") + return(terra::aggregate(r, fact = fact, fun = fun, na.rm = TRUE)) + } + r +} + +#' Load the CI band from a per-field weekly mosaic +#' +#' Locates the weekly mosaic TIF for the given field and week via +#' \code{\link{get_per_field_mosaic_path}}, loads it with +#' \code{terra::rast()}, and returns the CI band (the layer named \code{"CI"}, +#' or the first layer as a fallback). +#' +#' @param base_dir Path to the \code{weekly_mosaic} directory. +#' @param field_name Name of the field sub-directory. +#' @param week ISO week number. +#' @param year ISO year. +#' @return A single-layer \code{SpatRaster} (CI band), or \code{NULL} if the +#' file does not exist or cannot be loaded. +#' @seealso \code{\link{get_per_field_mosaic_path}} +#' +load_per_field_mosaic <- function(base_dir, field_name, week, year) { + path <- get_per_field_mosaic_path(base_dir, field_name, week, year) + if (is.null(path)) return(NULL) + + tryCatch({ + rast_obj <- terra::rast(path) + if ("CI" %in% names(rast_obj)) { + return(rast_obj[["CI"]]) + } else if (terra::nlyr(rast_obj) > 0) { + return(rast_obj[[1]]) + } + NULL + }, error = function(e) { + safe_log(paste("Could not load mosaic:", path, "-", e$message), "WARNING") + NULL + }) +} + +# ============================================================================== +# FIELD ALERT GENERATION +# ============================================================================== + +#' Generate field-level alert flags from normalised KPI data +#' +#' Evaluates each field's CV, Moran's I, decline severity, patchiness risk, +#' and gap score against threshold rules, returning a tidy data frame of +#' translated alert messages. Only fields that trigger at least one alert are +#' included in the output. +#' +#' Expects a table that has been passed through +#' \code{\link{normalize_field_details_columns}}, which guarantees the columns +#' \code{Field_id}, \code{CV}, \code{Morans_I}, \code{Decline_Severity}, +#' \code{Patchiness_Risk}, and \code{Gap_Score} are present. +#' +#' Alert rules: +#' \itemize{ +#' \item Priority 1 (Urgent) or 2 (Monitor) from +#' \code{\link{get_field_priority_level}} based on CV / Moran's I. +#' \item Decline risk High or Very-high. +#' \item Patchiness risk High. +#' \item Gap score \eqn{> 20}. +#' } +#' +#' @param field_details_table A data frame normalised by +#' \code{\link{normalize_field_details_columns}}. +#' @return A data frame with columns \code{Field} and \code{Alert}, one row +#' per alert per field. Returns an empty 0-row data frame when no alerts +#' are triggered, or \code{NULL} if the input is empty / missing required +#' columns. +#' @seealso \code{\link{get_field_priority_level}}, \code{\link{normalize_field_details_columns}} +#' +generate_field_alerts <- function(field_details_table) { + if (is.null(field_details_table) || nrow(field_details_table) == 0) { + return(NULL) + } + + required_cols <- c("Field_id", "CV", "Morans_I", "Decline_Severity", + "Patchiness_Risk", "Gap_Score") + missing_cols <- setdiff(required_cols, names(field_details_table)) + if (length(missing_cols) > 0) { + safe_log(paste("generate_field_alerts: missing columns:", + paste(missing_cols, collapse = ", ")), "WARNING") + return(NULL) + } + + summaries <- field_details_table %>% + dplyr::group_by(Field_id) %>% + dplyr::summarise( + avg_cv = mean(CV, na.rm = TRUE), + avg_morans_i = mean(Morans_I, na.rm = TRUE), + max_gap = suppressWarnings(max(Gap_Score, na.rm = TRUE)), + highest_decline = dplyr::case_when( + any(Decline_Severity == "Very-high", na.rm = TRUE) ~ "Very-high", + any(Decline_Severity == "High", na.rm = TRUE) ~ "High", + any(Decline_Severity == "Moderate", na.rm = TRUE) ~ "Moderate", + any(Decline_Severity == "Low", na.rm = TRUE) ~ "Low", + TRUE ~ "Unknown" + ), + highest_patchiness = dplyr::case_when( + any(Patchiness_Risk == "High", na.rm = TRUE) ~ "High", + any(Patchiness_Risk == "Medium", na.rm = TRUE) ~ "Medium", + any(Patchiness_Risk == "Low", na.rm = TRUE) ~ "Low", + any(Patchiness_Risk == "Minimal", na.rm = TRUE) ~ "Minimal", + TRUE ~ "Unknown" + ), + .groups = "drop" + ) %>% + dplyr::mutate( + priority = purrr::map2_int(avg_cv, avg_morans_i, get_field_priority_level), + max_gap = dplyr::if_else(is.infinite(max_gap), NA_real_, max_gap) + ) + + alerts <- summaries %>% + dplyr::mutate( + a_priority = dplyr::case_when( + priority == 1 ~ tr_key("priority"), + priority == 2 ~ tr_key("monitor"), + TRUE ~ NA_character_ + ), + a_decline = dplyr::if_else( + highest_decline %in% c("High", "Very-high"), tr_key("growth_decline"), NA_character_ + ), + a_patch = dplyr::if_else( + highest_patchiness == "High", tr_key("high_patchiness"), NA_character_ + ), + a_gap = dplyr::if_else( + !is.na(max_gap) & max_gap > 20, tr_key("gaps_present"), NA_character_ + ) + ) %>% + tidyr::pivot_longer( + cols = c(a_priority, a_decline, a_patch, a_gap), + names_to = NULL, + values_to = "Alert" + ) %>% + dplyr::filter(!is.na(Alert)) %>% + dplyr::select(Field = Field_id, Alert) + + if (nrow(alerts) == 0) { + return(data.frame(Field = character(), Alert = character())) + } + + alerts +} diff --git a/r_app/translations/translations.xlsx b/r_app/translations/translations.xlsx index ed311a5ffb180500c8e09ca0b93b140227f2d3f7..bf4973d3a2bc3efe1f845bf015e7896ac7d4fb14 100644 GIT binary patch delta 31302 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 hOj-W}A^v~%8daY3r