
    KCjP                         d Z ddlmZ ddlZddlZddlmZ ddl	m
Z
  ee      Z ed      Zddddddddddddd	Zg d
Z G d d      Z e       Zy)uC  
Feature vector builder.

Converts (district, crop, month) into the exact numeric feature vectors
each model was trained on. This is the single source of truth for
feature order and meaning — if you change this, you MUST retrain every
model that consumes it, because XGBoost/RandomForest/DecisionTree have
no concept of column names at inference time, only column position.

The historical climate stats CSV referenced here is produced by the
ml_pipeline/feature_engineering scripts and must be present in
data/processed/ before the API can serve real (non-fallback) features.
    )PathN)
get_logger)reference_dataz data/processed/climate_clean.csv      )         	   
         r   r            )rainfall_mmtemp_chumidityseason_indexrainfall_anomalyrainfall_ma3rainfall_lag1water_req_mm
min_temp_c
max_temp_cdrought_tolerantgrowth_daysc                   d    e Zd ZddZddZddZdededefdZ	ded	edede
j                  fd
Zy)FeatureBuilderreturnNc                      d | _         d| _        y )NF)_climate_history_loadedselfs    IC:\Crop_Prediction\Backend\crop-ai-system\app\services\feature_builder.py__init__zFeatureBuilder.__init__/   s    59    c                     t         j                         sAt        j                  t          d       t	        j
                  g d      | _        d| _        y t	        j                  t               | _        d| _        y )Nu    not found — feature builder will use fallback climate estimates. Real predictions require this file to be generated by the ml_pipeline before going to production.)districtmonthr   r   r   )columnsT)	CLIMATE_HISTORY_PATHexistsloggerwarningpd	DataFramer"   read_csvr#   r$   s    r&   loadzFeatureBuilder.load3   sf    #**,NN'( )Q R
 %'LLR%D!  %'KK0D$ED!r(   c                 2    | j                   st        d      y )NzQFeatureBuilder used before load() was called. This indicates a startup-order bug.)r#   RuntimeErrorr$   s    r&   _ensure_loadedzFeatureBuilder._ensure_loadedB   s     ||6  r(   r*   r+   c                    | j                   | j                   d   |k(  | j                   d   |k(  z     }|j                  r"t        j                  d| d| d       dddd	S t	        |d
   j                               d|v rt	        |d   j                               ndd|v rt	        |d   j                               d	S dd	S )zKReturns mean rainfall/temp/humidity for this district+month across history.r*   r+   z)No historical climate data for district='z' month=z, using generic fallback valuesg      Y@g      >@g     Q@)r   r   r   r   r   r   )r"   emptyr/   r0   floatmean)r%   r*   r+   hists       r&   _historical_climatez"FeatureBuilder._historical_climateI   s    $$"":.(:$$W-68
 ::NN;H:XeW U0 1 $)DdKK m!4!9!9!;<6>$6FeDN//12D:D:Ld:.3356
 	
 SW
 	
r(   cropc                 d   | j                          t        j                  |      }t        j                  |       | j	                  ||      }|dkD  r|dz
  nd}| j	                  ||      }|d   }|d   |z   dz  }	d}
t
        j                  |d      }t        j                  |d   |d   |d   ||
|	||d	   |d
   |d   t        |d         |d   gt        j                        }|j                  d   t        t              k7  r,t        d|j                  d    dt        t               d      |S )u  
        Builds the feature vector for a single (district, crop, month) query.
        Raises UnsupportedCropError / UnsupportedDistrictError via
        reference_data if either input is invalid — callers should let
        that propagate, not catch it here.
        r   r   r   r   g        r   r   r   r   r   r   r   r   )dtypezFeature vector length z does not match expected z columns)r7   r   get_cropget_districtr=   
SEASON_MAPgetnparrayr:   float64shapelenFEATURE_COLUMNSr6   )r%   r*   r>   r+   
crop_propscurrent
prev_monthprevr   r   r   r   vectors                r&   buildzFeatureBuilder.build\   s[    	#,,T2
##H-**8U;"'!)UQY
''*=]+.>!C !~~eQ/M"HJ~&|$|$*/01}%
  <<?c/22 (a(9 :01; 
 r(   )r    N)__name__
__module____qualname__r'   r4   r7   strintdictr=   rE   ndarrayrP    r(   r&   r   r   .   sP    
C 
 
 
&5c 5 5S 5RZZ 5r(   r   )__doc__pathlibr   numpyrE   pandasr1   app.core.logging_configr   app.services.reference_datar   rQ   r/   r-   rC   rJ   r   feature_builderrX   r(   r&   <module>r`      ss       . 6	H	>? 
 	Q1	qaAQ1
c cN !"r(   