Merge pull request #14 from TimonWeitkamp/review_perField_code

Growth trend
This commit is contained in:
DimitraVeropoulou 2026-02-23 14:33:03 +01:00 committed by GitHub
commit 29c1e77d06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 67 additions and 38 deletions

View file

@ -793,10 +793,14 @@ calculate_all_field_analysis_agronomic_support <- function(
# Safely identify immediate child directories (not including root) # Safely identify immediate child directories (not including root)
# Use list.files + dir.exists filter instead of list.dirs for robustness # Use list.files + dir.exists filter instead of list.dirs for robustness
all_entries <- list.files(current_mosaic_dir, full.names = FALSE) all_entries <- list.files(current_mosaic_dir, full.names = FALSE)
field_dirs <- all_entries[sapply( # Validate input and coerce-safe checks
file.path(current_mosaic_dir, all_entries), if (is.null(current_mosaic_dir) || !is.character(current_mosaic_dir) || length(current_mosaic_dir) != 1) {
dir.exists stop("current_mosaic_dir must be a single path string")
)] }
paths <- file.path(current_mosaic_dir, all_entries)
# Use vapply to guarantee a logical vector (avoid sapply returning a list)
is_dir <- vapply(paths, dir.exists, logical(1))
field_dirs <- all_entries[is_dir]
is_per_field <- length(field_dirs) > 0 && is_per_field <- length(field_dirs) > 0 &&
file.exists(file.path(current_mosaic_dir, field_dirs[1], week_file)) file.exists(file.path(current_mosaic_dir, field_dirs[1], week_file))
@ -906,12 +910,10 @@ calculate_all_field_analysis_agronomic_support <- function(
# Load historical field statistics to build weekly mean CI time series per field # Load historical field statistics to build weekly mean CI time series per field
# (growth_decline_kpi expects temporal series, not spatial pixel arrays) # (growth_decline_kpi expects temporal series, not spatial pixel arrays)
weekly_mean_ci_by_field <- list() # Initialize list with one element per field (empty numeric vectors)
n_fields <- nrow(field_boundaries_sf)
# Build list of weekly mean CI values for each field (4-week lookback) weekly_mean_ci_by_field <- vector("list", n_fields)
for (field_idx in seq_len(nrow(field_boundaries_sf))) { for (fi in seq_len(n_fields)) weekly_mean_ci_by_field[[fi]] <- numeric(0)
weekly_ci_values <- c()
}
# Try to load historical data for trend calculation # Try to load historical data for trend calculation
if (!is.null(output_dir) && !is.null(project_dir)) { if (!is.null(output_dir) && !is.null(project_dir)) {
@ -920,7 +922,7 @@ calculate_all_field_analysis_agronomic_support <- function(
project_dir = project_dir, project_dir = project_dir,
current_week = current_week, current_week = current_week,
current_year = current_year, current_year = current_year,
reports_dir = output_dir, kpi_reports_dir = output_dir,
num_weeks = 4, num_weeks = 4,
auto_generate = FALSE, auto_generate = FALSE,
field_boundaries_sf = field_boundaries_sf field_boundaries_sf = field_boundaries_sf
@ -929,35 +931,61 @@ calculate_all_field_analysis_agronomic_support <- function(
if (!is.null(historical_data) && length(historical_data) > 0) { if (!is.null(historical_data) && length(historical_data) > 0) {
message(" Building weekly mean CI time series from historical data...") message(" Building weekly mean CI time series from historical data...")
# Initialize list with empty vectors for each field
for (field_idx in seq_len(nrow(field_boundaries_sf))) {
weekly_mean_ci_by_field[[field_idx]] <- c()
}
# Extract Mean_CI from each historical week (reverse order to go chronologically) # Extract Mean_CI from each historical week (reverse order to go chronologically)
possible_mean_cols <- c("Mean_CI", "mean_ci", "MeanCI", "meanCI", "mean.ci")
possible_id_cols <- c("Field_id", "field_id", "Field", "field", "Field_name", "field_name")
for (hist_idx in rev(seq_along(historical_data))) { for (hist_idx in rev(seq_along(historical_data))) {
hist_week <- historical_data[[hist_idx]] hist_week <- historical_data[[hist_idx]]
hist_data <- hist_week$data hist_data <- hist_week$data
# Extract Mean_CI column if available # Skip empty week data
if ("Mean_CI" %in% names(hist_data)) { if (is.null(hist_data) || length(hist_data) == 0) next
# Match fields between historical data and field_boundaries
for (field_idx in seq_len(nrow(field_boundaries_sf))) { # Coerce to data.frame if needed
field_name <- field_boundaries_sf$field[field_idx] if (!is.data.frame(hist_data)) {
hist_data <- tryCatch(as.data.frame(hist_data, stringsAsFactors = FALSE), error = function(e) NULL)
# Find matching row in historical data by field name/ID }
field_row <- which( if (is.null(hist_data) || !is.data.frame(hist_data)) next
(hist_data$Field_id == field_name | hist_data$Field_name == field_name) &
!is.na(hist_data$Mean_CI) mean_col <- intersect(possible_mean_cols, names(hist_data))
) if (length(mean_col) == 0) {
message(paste0(" Warning: historical week ", hist_week$week, "_", hist_week$year, " missing Mean_CI column - skipping"))
if (length(field_row) > 0) { next
mean_ci_val <- as.numeric(hist_data$Mean_CI[field_row[1]]) }
if (!is.na(mean_ci_val)) { mean_col <- mean_col[1]
weekly_mean_ci_by_field[[field_idx]] <- c(weekly_mean_ci_by_field[[field_idx]], mean_ci_val)
} id_col <- intersect(possible_id_cols, names(hist_data))
use_row_order <- FALSE
if (length(id_col) == 0) {
if (nrow(hist_data) == n_fields) {
use_row_order <- TRUE
} else {
message(paste0(" Warning: historical week ", hist_week$week, "_", hist_week$year, " has no id/name column and rowcount != n_fields - skipping"))
next
}
} else {
id_col <- id_col[1]
}
# Normalize to character columns for matching
hist_df <- as.data.frame(hist_data, stringsAsFactors = FALSE)
hist_df[[mean_col]] <- as.character(hist_df[[mean_col]])
if (!use_row_order) hist_df[[id_col]] <- as.character(hist_df[[id_col]])
for (field_idx in seq_len(n_fields)) {
mean_ci_val <- NA_real_
if (use_row_order) {
if (field_idx <= nrow(hist_df)) {
mean_ci_val <- suppressWarnings(as.numeric(hist_df[[mean_col]][field_idx]))
}
} else {
fid <- as.character(field_boundaries_sf$field[field_idx])
matches <- which(!is.na(hist_df[[id_col]]) & hist_df[[id_col]] == fid)
if (length(matches) > 0) {
mean_ci_val <- suppressWarnings(as.numeric(hist_df[[mean_col]][matches[1]]))
} }
} }
if (!is.na(mean_ci_val)) weekly_mean_ci_by_field[[field_idx]] <- c(weekly_mean_ci_by_field[[field_idx]], mean_ci_val)
} }
} }
@ -1168,7 +1196,7 @@ calculate_all_field_analysis_agronomic_support <- function(
project_dir = project_dir project_dir = project_dir
) )
message(paste("\n✓ AURA KPI calculation complete. Week", current_week, current_year)) message(paste("\n✓ KPI calculation complete. Week", current_week, current_year))
return(list( return(list(
field_analysis_df = field_detail_df, field_analysis_df = field_detail_df,

View file

@ -1063,7 +1063,7 @@ load_or_calculate_weekly_stats <- function(week_num, year, project_dir, field_bo
} }
#' Load historical field data from CSV (4-week lookback) #' Load historical field data from CSV (4-week lookback)
load_historical_field_data <- function(project_dir, current_week, current_year, reports_dir, num_weeks = 4, auto_generate = TRUE, field_boundaries_sf = NULL, daily_vals_dir = NULL) { load_historical_field_data <- function(project_dir, current_week, current_year, kpi_reports_dir, num_weeks = 4, auto_generate = TRUE, field_boundaries_sf = NULL, daily_vals_dir = NULL) {
historical_data <- list() historical_data <- list()
loaded_weeks <- c() loaded_weeks <- c()
@ -1075,7 +1075,7 @@ load_historical_field_data <- function(project_dir, current_week, current_year,
target_year <- target$year target_year <- target$year
csv_filename <- paste0(project_dir, "_field_analysis_week", sprintf("%02d_%d", target_week, target_year), ".csv") csv_filename <- paste0(project_dir, "_field_analysis_week", sprintf("%02d_%d", target_week, target_year), ".csv")
csv_path <- file.path(reports_dir, "field_analysis", csv_filename) csv_path <- file.path(kpi_reports_dir, csv_filename)
if (file.exists(csv_path)) { if (file.exists(csv_path)) {
tryCatch({ tryCatch({

View file

@ -52,7 +52,8 @@ CLIENT_TYPE_MAP <- list(
"simba" = "agronomic_support", "simba" = "agronomic_support",
"john" = "agronomic_support", "john" = "agronomic_support",
"huss" = "agronomic_support", "huss" = "agronomic_support",
"aura" = "agronomic_support" "aura" = "agronomic_support",
"tpc" = "agronomic_support"
) )
#' Get client type for a project #' Get client type for a project