import os import uuid from datetime import datetime from flask import Flask, render_template, request, flash, url_for from models.config import Config from models.data_loader import DataManager from models.temporal_forecaster import TemporalForecaster from models.rotation_optimizer import CropRotationOptimizer from models.image_generator import CropImageGenerator def create_app(): app = Flask(__name__) app.config["SECRET_KEY"] = os.environ.get("FLASK_SECRET_KEY", "dev-secret-key") app.config["UPLOAD_FOLDER"] = os.path.join("static", "uploads") app.config["GENERATED_FOLDER"] = os.path.join("static", "generated") os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) os.makedirs(app.config["GENERATED_FOLDER"], exist_ok=True) data_manager = DataManager(Config) forecaster = TemporalForecaster(Config) optimizer = CropRotationOptimizer(Config) image_generator = CropImageGenerator(Config) @app.route("/", methods=["GET", "POST"]) def index(): if request.method == "POST": try: region = request.form.get("region") or "Field-001" crop = request.form.get("crop") or "Maize" seasons = int(request.form.get("seasons") or 2) scenario = request.form.get("scenario") or "baseline" start_date_str = request.form.get("start_date") or datetime.today().strftime("%Y-%m-%d") start_date = datetime.strptime(start_date_str, "%Y-%m-%d") rotation_plan_text = (request.form.get("rotation_plan") or "").strip() rotation_file = request.files.get("rotation_file") rotation_file_path = None if rotation_file and rotation_file.filename: filename = f"{uuid.uuid4().hex}_{rotation_file.filename}" rotation_file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename) rotation_file.save(rotation_file_path) ndvi_data = data_manager.load_ndvi_series(region=region) weather_forecast = data_manager.load_weather_forecast(region=region) soil_terrain = data_manager.load_soil_and_terrain(region=region) commons_data = data_manager.load_ag_commons_metadata() growth_forecast = forecaster.forecast_growth( ndvi_data=ndvi_data, weather=weather_forecast, soil=soil_terrain, crop=crop, start_date=start_date, seasons=seasons, scenario=scenario, ) optimized_rotation, rl_metrics = optimizer.optimize_rotation( base_plan_text=rotation_plan_text, uploaded_plan_path=rotation_file_path, crop=crop, seasons=seasons, scenario=scenario, ) img_filename = f"crop_sim_{uuid.uuid4().hex}.png" img_path = os.path.join(app.config["GENERATED_FOLDER"], img_filename) rel_img_path = os.path.join("generated", img_filename) image_generator.generate_growth_visualization( ndvi_series=ndvi_data, forecast=growth_forecast, rotation_plan=optimized_rotation, output_path=img_path, scenario=scenario, ) metrics = { "yield_index": growth_forecast.get("yield_index", 0.0), "stress_risk": growth_forecast.get("stress_risk", 0.0), "weather_volatility": growth_forecast.get("weather_volatility", 0.0), "soil_score": growth_forecast.get("soil_score", 0.0), } return render_template( "results.html", region=region, crop=crop, seasons=seasons, scenario=scenario, start_date=start_date_str, metrics=metrics, rotation_plan=optimized_rotation, rl_metrics=rl_metrics, growth_forecast=growth_forecast, image_path=rel_img_path, commons_data=commons_data, ) except Exception as e: flash(f"Simulation failed: {e}", "danger") regions = ["Field-001", "Field-002", "Field-Highland", "Field-Lowland"] crops = ["Maize", "Wheat", "Rice", "Soybean", "Cotton"] scenarios = [ ("baseline", "Baseline"), ("wet_year", "Wet year (↑ rainfall)"), ("dry_year", "Dry year (↓ rainfall)"), ("heatwave", "Heatwave risk"), ("low_inputs", "Low fertilizer / low inputs"), ] return render_template( "index.html", regions=regions, crops=crops, scenarios=scenarios, today=datetime.today().strftime("%Y-%m-%d"), ) @app.route("/api/health") def health(): return {"status": "ok", "feature": "future_crop_visualization"} return app if __name__ == "__main__": app = create_app() app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5003)), debug=True)