ags / utils /config_manager.py
Mark Cuizon
Use latest Gemini 3.1 Pro as default analysis model
b4bd144
Raw History Blame Contribute Delete
9.94 kB
# Configuration manager
import json
import os
from typing import Dict, Any, Optional
from dataclasses import dataclass
@dataclass
class AIConfig:
"""AI configuration settings"""
model: str = "gemini-3.1-pro-preview" # Latest Gemini Pro for agronomic analysis
embedding_model: str = "text-embedding-004"
temperature: float = 0.0
max_tokens: int = 1000
top_p: float = 0.9
frequency_penalty: float = 0.0
presence_penalty: float = 0.0
enable_rag: bool = True
enable_caching: bool = True
retry_attempts: int = 3
timeout_seconds: int = 30
confidence_threshold: float = 0.8
@dataclass
class MPOBStandard:
"""MPOB standard definition"""
parameter: str
min_value: float
max_value: float
unit: str
optimal_value: float = None
description: str = ""
critical: bool = False
@dataclass
class MPOBStandards:
"""MPOB standards collection"""
standards: Dict[str, MPOBStandard] = None
soil_standards: Dict[str, MPOBStandard] = None
leaf_standards: Dict[str, MPOBStandard] = None
def __post_init__(self):
if self.standards is None:
self.standards = {}
if self.soil_standards is None:
self.soil_standards = {}
if self.leaf_standards is None:
self.leaf_standards = {}
@dataclass
class EconomicConfig:
"""Economic configuration"""
currency: str = "MYR"
yield_price_per_ton: float = 2500.0
inflation_rate: float = 0.03
region: str = "Malaysia"
discount_rate: float = 0.08
fertilizer_costs: Dict[str, float] = None
application_costs: Dict[str, float] = None
labor_costs: Dict[str, float] = None
equipment_costs: Dict[str, float] = None
def __post_init__(self):
if self.fertilizer_costs is None:
self.fertilizer_costs = {
"NPK": 1200.0,
"Urea": 800.0,
"Rock Phosphate": 600.0,
"Potash": 1000.0
}
if self.application_costs is None:
self.application_costs = {
"Manual": 50.0,
"Mechanical": 100.0,
"Aerial": 200.0
}
if self.labor_costs is None:
self.labor_costs = {
"Skilled": 80.0,
"Unskilled": 40.0,
"Supervisor": 120.0
}
if self.equipment_costs is None:
self.equipment_costs = {
"Tractor": 200.0,
"Sprayer": 80.0,
"Fertilizer Spreader": 150.0
}
@dataclass
class OCRConfig:
"""OCR configuration"""
confidence_threshold: float = 0.8
language: str = "en"
preprocessing: bool = True
psm_modes: list = None
character_whitelist: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,-:()%"
scale_factor_min: float = 1.0
scale_factor_max: float = 2.0
contrast_enhancement: float = 1.2
sharpness_enhancement: float = 1.1
bilateral_filter_d: int = 9
bilateral_filter_sigma_color: float = 75.0
bilateral_filter_sigma_space: float = 75.0
adaptive_threshold_block_size: int = 11
adaptive_threshold_c: float = 2.0
def __post_init__(self):
if self.psm_modes is None:
self.psm_modes = [6, 7, 8, 13]
@dataclass
class UIConfig:
"""UI configuration"""
theme: str = "light"
sidebar_expanded: bool = True
show_advanced: bool = False
primary_color: str = "#4CAF50"
secondary_color: str = "#2196F3"
accent_color: str = "#FF9800"
language: str = "en"
date_format: str = "%Y-%m-%d"
number_format: str = "%.2f"
units: Dict[str, list] = None
display_preferences: Dict[str, Any] = None
def __post_init__(self):
if self.units is None:
self.units = {
"land_size": ["hectares", "acres"],
"yield": ["tonnes/hectare", "kg/hectare"]
}
if self.display_preferences is None:
self.display_preferences = {
"show_icons": True,
"show_colors": True,
"compact_mode": False,
"auto_refresh": True,
"default_chart_type": "line"
}
class ConfigManager:
"""Configuration manager for the application"""
def __init__(self):
self.config_dir = "config"
self._cache = {} # Initialize cache
self.ensure_config_dir()
def ensure_config_dir(self):
"""Ensure config directory exists"""
if not os.path.exists(self.config_dir):
os.makedirs(self.config_dir)
def get_ai_config(self) -> AIConfig:
"""Get AI configuration"""
return AIConfig()
def get_mpob_standards(self) -> MPOBStandards:
"""Get MPOB standards"""
# Create default MPOB standards based on official MPOB oil palm optimal values
# 9 Soil parameters - matching analysis file parameter names
default_soil_standards = {
"pH": MPOBStandard("pH", 4.5, 5.5, "pH units", 5.0, "Soil pH level for optimal oil palm growth", True),
"N (%)": MPOBStandard("N (%)", 0.15, 0.25, "%", 0.20, "Total nitrogen content in soil", True),
"Org. C (%)": MPOBStandard("Org. C (%)", 2.0, 4.0, "%", 3.0, "Organic carbon content in soil", True),
"Total P (mg/kg)": MPOBStandard("Total P (mg/kg)", 15.0, 30.0, "mg/kg", 22.5, "Total phosphorus content", True),
"Avail P (mg/kg)": MPOBStandard("Avail P (mg/kg)", 15.0, 30.0, "mg/kg", 22.5, "Available phosphorus (Bray-1)", True),
"Exch. K (meq%)": MPOBStandard("Exch. K (meq%)", 0.15, 0.30, "meq%", 0.225, "Exchangeable potassium", True),
"Exch. Ca (meq%)": MPOBStandard("Exch. Ca (meq%)", 2.0, 8.0, "meq%", 5.0, "Exchangeable calcium", False),
"Exch. Mg (meq%)": MPOBStandard("Exch. Mg (meq%)", 0.5, 2.0, "meq%", 1.25, "Exchangeable magnesium", False),
"CEC (meq%)": MPOBStandard("CEC (meq%)", 8.0, 20.0, "meq%", 14.0, "Soil cation exchange capacity", False)
}
# 8 Leaf parameters - matching analysis file parameter names
default_leaf_standards = {
"N (%)": MPOBStandard("N (%)", 2.4, 2.8, "%", 2.6, "Leaf nitrogen content (frond 17)", True),
"P (%)": MPOBStandard("P (%)", 0.15, 0.25, "%", 0.20, "Leaf phosphorus content (frond 17)", True),
"K (%)": MPOBStandard("K (%)", 1.0, 1.3, "%", 1.15, "Leaf potassium content (frond 17)", True),
"Ca (%)": MPOBStandard("Ca (%)", 0.5, 1.0, "%", 0.75, "Leaf calcium content (frond 17)", False),
"Mg (%)": MPOBStandard("Mg (%)", 0.25, 0.50, "%", 0.375, "Leaf magnesium content (frond 17)", False),
"B (mg/kg)": MPOBStandard("B (mg/kg)", 10.0, 25.0, "mg/kg", 17.5, "Leaf boron content (frond 17)", False),
"Cu (mg/kg)": MPOBStandard("Cu (mg/kg)", 5.0, 15.0, "mg/kg", 10.0, "Leaf copper content (frond 17)", False),
"Zn (mg/kg)": MPOBStandard("Zn (mg/kg)", 15.0, 30.0, "mg/kg", 22.5, "Leaf zinc content (frond 17)", False)
}
return MPOBStandards(
standards={},
soil_standards=default_soil_standards,
leaf_standards=default_leaf_standards
)
def get_economic_config(self) -> EconomicConfig:
"""Get economic configuration"""
return EconomicConfig()
def get_ocr_config(self) -> OCRConfig:
"""Get OCR configuration"""
return OCRConfig()
def get_ui_config(self) -> UIConfig:
"""Get UI configuration"""
return UIConfig()
def save_config(self, config_type: str, config_data: Dict[str, Any]) -> bool:
"""Save configuration"""
try:
config_path = os.path.join(self.config_dir, f"{config_type}.json")
with open(config_path, 'w') as f:
json.dump(config_data, f, indent=2)
return True
except Exception:
return False
def load_config(self, config_type: str) -> Optional[Dict[str, Any]]:
"""Load configuration"""
try:
config_path = os.path.join(self.config_dir, f"{config_type}.json")
if os.path.exists(config_path):
with open(config_path, 'r') as f:
return json.load(f)
return None
except Exception:
return None
def reset_to_defaults(self, config_type: str) -> bool:
"""Reset configuration to defaults"""
try:
config_path = os.path.join(self.config_dir, f"{config_type}.json")
if os.path.exists(config_path):
os.remove(config_path)
return True
except Exception:
return False
def get_all_configs(self) -> Dict[str, Any]:
"""Get all configuration objects"""
return {
'ai_config': self.get_ai_config(),
'mpob_standards': self.get_mpob_standards(),
'economic_config': self.get_economic_config(),
'ocr_config': self.get_ocr_config(),
'ui_config': self.get_ui_config()
}
def clear_cache(self) -> bool:
"""Clear the configuration cache"""
try:
self._cache.clear()
return True
except Exception:
return False
# Global config manager instance
config_manager = ConfigManager()
def get_ui_config() -> UIConfig:
"""Get UI configuration"""
return config_manager.get_ui_config()
def get_ai_config() -> AIConfig:
"""Get AI configuration"""
return config_manager.get_ai_config()
def get_mpob_standards() -> MPOBStandards:
"""Get MPOB standards"""
return config_manager.get_mpob_standards()
def get_economic_config() -> EconomicConfig:
"""Get economic configuration"""
return config_manager.get_economic_config()