#' Package Manager for SmartCane Project #' #' This script manages R package versions across development, testing, and production environments. #' It uses renv for reproducible environments and ensures consistent package versions. #' #' Usage: #' source("package_manager.R") #' #' Author: SmartCane Team #' Date: 2025-06-24 # ============================================================================= # CONFIGURATION # ============================================================================= # Package requirements with your current working versions REQUIRED_PACKAGES <- list( # Core data manipulation "dplyr" = "1.1.4", "here" = "1.0.1", "lubridate" = "1.9.4", "readr" = "2.1.5", "readxl" = "1.4.5", "stringr" = "1.5.1", "tidyr" = "1.3.1", "purrr" = "1.0.2", "magrittr" = "2.0.0", # Adding this as it's commonly used # Spatial data "exactextractr" = "0.10.0", "raster" = "3.6.32", "sf" = "1.0.19", "terra" = "1.8.43", # CRITICAL: for raster processing # Visualization - CRITICAL: tmap v4 for new syntax "ggplot2" = "3.5.1", "tmap" = "4.0", # CRITICAL: for tm_scale_continuous() syntax "gridExtra" = "2.3", # Reporting "knitr" = "1.50", "rmarkdown" = "2.21.0", # Adding this as it's needed for reports # Tidyverse meta-package "tidyverse" = "2.0.0", # Machine Learning & Statistics "caret" = "7.0.1", "CAST" = "1.0.3", "randomForest" = "4.7.1.2", "rsample" = "1.3.0", # Parallel processing "furrr" = "0.3.1", "future" = "1.40.0", "progressr" = "0.15.1", # Other utilities "reshape2" = "1.4.4", "zoo" = "1.8.13" ) # Log file setup LOG_FILE <- file.path(getwd(), "package_manager.log") START_TIME <- Sys.time() # ============================================================================= # UTILITY FUNCTIONS # ============================================================================= #' Log message to both console and file log_message <- function(message, level = "INFO") { timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S") formatted_msg <- sprintf("[%s] %s - %s", level, timestamp, message) # Print to console cat(formatted_msg, "\n") # Write to log file cat(formatted_msg, "\n", file = LOG_FILE, append = TRUE) } #' Check if package is installed is_package_installed <- function(package) { package %in% rownames(installed.packages()) } #' Get installed package version get_package_version <- function(package) { if (is_package_installed(package)) { as.character(packageVersion(package)) } else { NULL } } #' Compare version strings (returns TRUE if installed >= required) version_meets_requirement <- function(installed, required) { if (is.null(installed)) return(FALSE) utils::compareVersion(installed, required) >= 0 } #' Install or update package to minimum version install_or_update_package <- function(package, required_version) { current_version <- get_package_version(package) if (is.null(current_version)) { log_message(sprintf("Installing %s (required: >= %s)", package, required_version)) tryCatch({ install.packages(package, dependencies = TRUE, quiet = TRUE) new_version <- get_package_version(package) log_message(sprintf("✓ Installed %s version %s", package, new_version), "SUCCESS") return(TRUE) }, error = function(e) { log_message(sprintf("✗ Failed to install %s: %s", package, e$message), "ERROR") return(FALSE) }) } else if (!version_meets_requirement(current_version, required_version)) { log_message(sprintf("Updating %s from %s to >= %s", package, current_version, required_version)) tryCatch({ install.packages(package, dependencies = TRUE, quiet = TRUE) new_version <- get_package_version(package) if (version_meets_requirement(new_version, required_version)) { log_message(sprintf("✓ Updated %s to version %s", package, new_version), "SUCCESS") return(TRUE) } else { log_message(sprintf("⚠ %s updated to %s but still below required %s", package, new_version, required_version), "WARNING") return(FALSE) } }, error = function(e) { log_message(sprintf("✗ Failed to update %s: %s", package, e$message), "ERROR") return(FALSE) }) } else { log_message(sprintf("✓ %s version %s meets requirement (>= %s)", package, current_version, required_version)) return(TRUE) } } # ============================================================================= # MAIN PACKAGE MANAGEMENT FUNCTIONS # ============================================================================= #' Initialize renv if not already initialized initialize_renv <- function() { log_message("Checking renv initialization...") if (!file.exists("renv.lock")) { log_message("Initializing renv for the first time...") if (!requireNamespace("renv", quietly = TRUE)) { log_message("Installing renv...") install.packages("renv") } renv::init() log_message("✓ renv initialized", "SUCCESS") } else { log_message("✓ renv already initialized") # Check if renv is already active by looking at the library path if (!requireNamespace("renv", quietly = TRUE)) { install.packages("renv") } # Check if we're already using the renv project library lib_paths <- .libPaths() if (!any(grepl("renv", lib_paths))) { log_message("Activating renv...") renv::activate() log_message("✓ renv activated") } else { log_message("✓ renv already active") } } } #' Check and install all required packages manage_packages <- function() { log_message("=== PACKAGE MANAGEMENT STARTED ===") log_message(sprintf("R version: %s", R.version.string)) success_count <- 0 failure_count <- 0 for (package in names(REQUIRED_PACKAGES)) { required_version <- REQUIRED_PACKAGES[[package]] if (install_or_update_package(package, required_version)) { success_count <- success_count + 1 } else { failure_count <- failure_count + 1 } } log_message(sprintf("Package management complete: %d success, %d failures", success_count, failure_count)) if (failure_count > 0) { log_message("Some packages failed to install/update. Check log for details.", "WARNING") } return(failure_count == 0) } #' Update renv lockfile with current package versions update_lockfile <- function() { log_message("Updating renv lockfile...") tryCatch({ renv::snapshot(prompt = FALSE) log_message("✓ renv lockfile updated", "SUCCESS") }, error = function(e) { log_message(sprintf("✗ Failed to update lockfile: %s", e$message), "ERROR") }) } #' Generate package report generate_package_report <- function() { log_message("=== PACKAGE REPORT ===") # Check each required package for (package in names(REQUIRED_PACKAGES)) { required_version <- REQUIRED_PACKAGES[[package]] current_version <- get_package_version(package) if (is.null(current_version)) { status <- "❌ NOT INSTALLED" } else if (version_meets_requirement(current_version, required_version)) { status <- "✅ OK" } else { status <- "⚠️ VERSION TOO OLD" } log_message(sprintf("%-20s | Required: >= %-8s | Installed: %-8s | %s", package, required_version, ifelse(is.null(current_version), "NONE", current_version), status)) } log_message("=== END PACKAGE REPORT ===") } #' Main function to run complete package management run_package_manager <- function() { # Initialize log cat("", file = LOG_FILE) # Clear log file log_message("SmartCane Project - Package Manager Started") log_message(sprintf("Working directory: %s", getwd())) # Step 1: Initialize renv initialize_renv() # Step 2: Generate initial report log_message("\n=== INITIAL STATE ===") generate_package_report() # Step 3: Manage packages log_message("\n=== PACKAGE INSTALLATION/UPDATES ===") success <- manage_packages() # Step 4: Update lockfile if successful if (success) { update_lockfile() } # Step 5: Generate final report log_message("\n=== FINAL STATE ===") generate_package_report() # Summary end_time <- Sys.time() duration <- round(as.numeric(difftime(end_time, START_TIME, units = "secs")), 2) log_message(sprintf("Package management completed in %s seconds", duration)) log_message(sprintf("Log saved to: %s", LOG_FILE)) if (success) { log_message("🎉 All packages successfully managed!", "SUCCESS") log_message("📋 Next steps:") log_message(" 1. Test your R scripts to ensure everything works") log_message(" 2. Commit renv.lock to version control") log_message(" 3. Share this script with your team") } else { log_message("⚠️ Some issues occurred. Check the log for details.", "WARNING") log_message("💡 You may need to:") log_message(" 1. Update R to a newer version") log_message(" 2. Install system dependencies") log_message(" 3. Check your internet connection") } return(success) } # ============================================================================= # EXECUTION # ============================================================================= # Only run if script is sourced directly (not when loaded as module) if (!exists("PACKAGE_MANAGER_LOADED")) { PACKAGE_MANAGER_LOADED <- TRUE cat("🚀 SmartCane Package Manager\n") cat("============================\n") cat("This will check and install/update all required R packages.\n") cat("Log file:", LOG_FILE, "\n\n") # Ask for confirmation response <- readline("Continue? (y/N): ") if (tolower(substr(response, 1, 1)) == "y") { result <- run_package_manager() if (result) { cat("\n✅ Package management completed successfully!\n") } else { cat("\n❌ Package management completed with errors. Check the log.\n") } } else { cat("❌ Package management cancelled.\n") } }