@@ -342,9 +342,9 @@ def _predict_ndvi_with_ml(history_data: List[Dict[str, Any]], weather_df: pd.Dat
342342 if training_df .empty :
343343 raise ValueError ("No valid training data available" )
344344
345- # Create future dates for prediction (next year )
346- current_year = datetime .now (). year
347- future_start = datetime ( target_year , 1 , 1 )
345+ # Create future dates for prediction (from current time forward )
346+ current_time = datetime .now ()
347+ future_start = current_time
348348 future_end = datetime (target_year , 12 , 31 )
349349
350350 # Create future dataframe with 16-day intervals (satellite revisit cycle)
@@ -723,7 +723,7 @@ def _generate_report(geojson_payload: Union[str, Dict[str, Any]], yield_profile:
723723 "yieldTph" : 0.0 ,
724724 "ndviPeak" : None ,
725725 "ndviPeakAt" : None ,
726- "model " : _DEFAULT_MODEL ,
726+ "ndviModel " : _DEFAULT_MODEL ,
727727 "confidence" : 0.0 ,
728728 "yieldType" : yield_profile ["name" ],
729729 }
@@ -749,7 +749,7 @@ def _generate_report(geojson_payload: Union[str, Dict[str, Any]], yield_profile:
749749 history : List [Dict [str , Any ]] = []
750750 report_records : List [Dict [str , Any ]] = []
751751
752- for row in rows :
752+ for i , row in enumerate ( rows ) :
753753 report = monitor .compute_index_for_row (row , index_types = index_types , bbox = bbox , token = token )
754754 if not report :
755755 continue
@@ -775,24 +775,32 @@ def _generate_report(geojson_payload: Union[str, Dict[str, Any]], yield_profile:
775775
776776 weather = weather_lookup .get (report_dt .date ())
777777
778- history .append (
779- {
780- "date" : _isoformat_utc (report_dt ),
781- "ndvi" : report .get ("mean_NDVI" ),
782- "cloud_cover" : float (row .cloud_cover ) if getattr (row , "cloud_cover" , None ) is not None else None ,
783- "collection" : getattr (row , "collection" , None ),
784- "temperature_deg_c" : weather ["temperature_deg_c" ] if weather else None ,
785- "humidity_pct" : weather ["humidity_pct" ] if weather else None ,
786- "cloudcover_pct" : weather ["cloudcover_pct" ] if weather else None ,
787- "wind_speed_mps" : weather ["wind_speed_mps" ] if weather else None ,
788- "clarity_pct" : weather ["clarity_pct" ] if weather else None ,
789- }
790- )
778+ # Add historical data point
779+ historical_entry = {
780+ "date" : _isoformat_utc (report_dt ),
781+ "ndvi" : report .get ("mean_NDVI" ),
782+ "cloud_cover" : float (row .cloud_cover ) if getattr (row , "cloud_cover" , None ) is not None else None ,
783+ "collection" : getattr (row , "collection" , None ),
784+ "temperature_deg_c" : weather ["temperature_deg_c" ] if weather else None ,
785+ "humidity_pct" : weather ["humidity_pct" ] if weather else None ,
786+ "cloudcover_pct" : weather ["cloudcover_pct" ] if weather else None ,
787+ "wind_speed_mps" : weather ["wind_speed_mps" ] if weather else None ,
788+ "clarity_pct" : weather ["clarity_pct" ] if weather else None ,
789+ "type" : 0 , # 0 = historic data, 1 = ML predicted data
790+ }
791+
792+ history .append (historical_entry )
793+
794+ # For the last row, append a copy with type 1
795+ if i == len (rows ) - 1 :
796+ transition_entry = historical_entry .copy ()
797+ transition_entry ["type" ] = 1
798+ history .append (transition_entry )
791799
792800 history .sort (key = lambda item : item ["date" ])
793801
794802 # Try ML prediction first, fall back to heuristic method
795- forecast_year = end_dt .year + 1
803+ forecast_year = end_dt .year
796804 ml_results = _predict_ndvi_with_ml (history , weather_df , forecast_year )
797805
798806 if ml_results .get ('model' ) == 'prophet_ml' :
@@ -804,6 +812,39 @@ def _generate_report(geojson_payload: Union[str, Dict[str, Any]], yield_profile:
804812 flowering_confidence = ml_results .get ('flowering_confidence' , 0 )
805813 model_name = "prophet_ml"
806814
815+ # Add ML predictions to history with type 1
816+ ml_predictions = ml_results .get ('predictions' , [])
817+ for prediction in ml_predictions :
818+ pred_date = prediction ['ds' ]
819+ if isinstance (pred_date , str ):
820+ try :
821+ pred_dt = pd .to_datetime (pred_date ).to_pydatetime ()
822+ except Exception :
823+ continue
824+ elif isinstance (pred_date , pd .Timestamp ):
825+ pred_dt = pred_date .to_pydatetime ()
826+ else :
827+ pred_dt = pred_date
828+
829+ if pred_dt .tzinfo is None :
830+ pred_dt = pred_dt .replace (tzinfo = timezone .utc )
831+
832+ history .append ({
833+ "date" : _isoformat_utc (pred_dt ),
834+ "ndvi" : round (float (prediction ['yhat' ]), 4 ),
835+ "cloud_cover" : None , # No cloud cover for predictions
836+ "collection" : "ML_Prediction" ,
837+ "temperature_deg_c" : None , # Weather data not included in predictions
838+ "humidity_pct" : None ,
839+ "cloudcover_pct" : None ,
840+ "wind_speed_mps" : None ,
841+ "clarity_pct" : None ,
842+ "type" : 1 , # 1 = ML predicted data
843+ })
844+
845+ # Re-sort history after adding predictions
846+ history .sort (key = lambda item : item ["date" ])
847+
807848 # Calculate yield based on ML predictions
808849 reference_ndvi = ndvi_peak if ndvi_peak is not None else 0.0
809850 tolerance = float (yield_profile .get ("ndvi_tolerance" ) or 0.25 )
@@ -872,11 +913,11 @@ def _generate_report(geojson_payload: Union[str, Dict[str, Any]], yield_profile:
872913 forecast_dict = {
873914 "year" : forecast_year ,
874915 "yieldTph" : round (yield_tph , 2 ),
916+ "yieldType" : yield_profile ["name" ],
917+ "yieldConfidence" : round (match_ratio , 2 ),
875918 "ndviPeak" : round (ndvi_peak , 2 ) if ndvi_peak is not None else None ,
876919 "ndviPeakAt" : ndvi_peak_at ,
877- "model" : model_name ,
878- "confidence" : round (match_ratio , 2 ),
879- "yieldType" : yield_profile ["name" ],
920+ "ndviModel" : model_name ,
880921 }
881922
882923 # Add flowering information if available
@@ -886,9 +927,9 @@ def _generate_report(geojson_payload: Union[str, Dict[str, Any]], yield_profile:
886927 if isinstance (flowering_start_date , datetime ):
887928 if flowering_start_date .tzinfo is None :
888929 flowering_start_date = flowering_start_date .replace (tzinfo = timezone .utc )
889- forecast_dict ["floweringStartDate " ] = _isoformat_utc (flowering_start_date )
890- forecast_dict ["floweringConfidence " ] = round (flowering_confidence , 2 )
891- forecast_dict ["floweringMethod " ] = ml_results .get ('flowering_method' )
930+ forecast_dict ["ndviStartAt " ] = _isoformat_utc (flowering_start_date )
931+ forecast_dict ["ndviStartConfidence " ] = round (flowering_confidence , 2 )
932+ forecast_dict ["ndviStartMethod " ] = ml_results .get ('flowering_method' )
892933
893934 return {"history" : history , "forecast" : forecast_dict }
894935
0 commit comments