+
    j7                         R t ^ RIt^ RIt^ RIt^ RIHtHtHtHtH	t	 ^ RI
H
t
 ^ RIt^ RIt^ RIt^ RIHt ^ RIHt  ! R R4      tR# )z
Regression/Classification Service
Handles XGBoost model training, persistence, and prediction.
Mirrors ProphetService pattern: 3-layer cache, .joblib disk, ml_regression_models DB table.
N)DictListOptionalAnyTuple)datetime)
get_logger)
MySQLModelc            
         a  ] tR t^t o Rt/ t]P                  ! 4       tRt	]P                  ! 4       t
R^R^RRRRR	^*/tR^R^RRR
RR	^*/tRRRR/tRRRR/tR tV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R  lR! ltV 3R" lR# ltV 3R$ lR% ltR9V 3R' lR( lltV 3R) lR* ltV 3R+ lR, ltV 3R- lR. ltV 3R/ lR0 ltV 3R1 lR2 lt]V 3R3 lR4 l4       t ]V 3R5 lR6 l4       t!V 3R7 lt"R8t#V t$R&# ):RegressionServicez<Unified service for XGBoost classifier and regressor models.Fn_estimators	max_depthlearning_rateg?eval_metricloglossrandom_state	objectivezreg:squarederrorHIGHg333333?MEDIUMgffffff?LOWg     R@g     V@c                    \        R 4      V n        \        4       V n        V P	                  4        V P                  4        R# )regression_serviceN)r   loggerr	   _db_ensure_table_ensure_model_dirselfs   &LD:\Projects\python\client-ideal-oms_pyapi\app\services\regression_service.py__init__RegressionService.__init__/   s0     !56<     c                    < V ^8  d   QhRS[ /#    returnstr)format__classdict__s   "r   __annotate__RegressionService.__annotate__9   s     g g gr!   c           	         ^ RI Hp VP                  P                  R\        P
                  P                  VP                  P                  RR4      R4      4      # )    )current_appREGRESSION_MODEL_DIRPROPHET_MODEL_DIRzstorage/app
regression)flaskr.   configgetospathjoin)r   r.   s   & r   _get_model_dir RegressionService._get_model_dir9   sJ    %!!%%&<ww||K..223FVXdeg 	gr!   c                   < V ^8  d   QhRR/# r$   r%   N )r(   r)   s   "r   r*   r+   >   s     : :4 :r!   c                R    \         P                  ! V P                  4       R R7       R# )T)exist_okN)r5   makedirsr8   r   s   &r   r   #RegressionService._ensure_model_dir>   s    
D'')D9r!   c                   < V ^8  d   QhRR/# r;   r<   )r(   r)   s   "r   r*   r+   A   s     4 4t 4r!   c                2   \         P                  '       d   R # \         P                  ;_uu_ 4        \         P                  '       d    R R R 4       R # RpV P                  P	                  V4       R\         n        R R R 4       R #   + '       g   i     R # ; i)Na  
                CREATE TABLE IF NOT EXISTS `ml_regression_models` (
                    `id`          INT UNSIGNED NOT NULL AUTO_INCREMENT,
                    `model_id`    VARCHAR(120) NOT NULL,
                    `file_path`   VARCHAR(512) NOT NULL,
                    `model_type`  ENUM('classifier','regressor') NOT NULL,
                    `config`      JSON NOT NULL,
                    `features`    JSON NOT NULL,
                    `data_points` INT UNSIGNED NOT NULL DEFAULT 0,
                    `created_at`  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    `updated_at`  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                    PRIMARY KEY (`id`),
                    UNIQUE KEY `uq_model_id` (`model_id`),
                    KEY `idx_model_type` (`model_type`),
                    KEY `idx_created_at` (`created_at`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
            T)r   _table_ensured_table_lockr   execute_query)r   ddls   & r   r   RegressionService._ensure_tableA   sg    +++*** /// +*C" HH""3'/3,+ +***s   B(BB	c                &   < V ^8  d   QhRS[ RS[ /# r$   model_idr%   r&   )r(   r)   s   "r   r*   r+   [   s     H H H Hr!   c                    R P                  R V 4       4      p\        P                  P                  V P                  4       V R24      # ) c              3   b   "   T F%  qP                  4       '       g   VR9   d   TMRx  K'  	  R# 5i)-_N)rN   rO   )isalnum).0cs   & r   	<genexpr>5RegressionService._model_file_path.<locals>.<genexpr>\   s%     YPX1yy{{a:o!3FPXs   -/z.joblib)r7   r5   r6   r8   )r   rJ   safe_ids   && r   _model_file_path"RegressionService._model_file_path[   s<    ''YPXYYww||D//1gYg3FGGr!   c                ,   < V ^8  d   QhRS[ RS[RS[ /# )r$   rJ   payloadr%   r'   r   )r(   r)   s   "r   r*   r+   _   s"      c D S r!   c                X    V P                  V4      p\        P                  ! W#^R7       V# )   )compress)rV   joblibdump)r   rJ   rY   	file_paths   &&& r   _save_to_diskRegressionService._save_to_disk_   s&    ))(3	G3r!   c                ,   < V ^8  d   QhRS[ RS[ RS[/# )r$   rJ   r`   r%   rZ   )r(   r)   s   "r   r*   r+   d   s"     & & & & &r!   c                    \         P                  P                  V4      '       g   \        R V RV R24      h\        P
                  ! V4      # )zModel file for 'z' not found at '')r5   r6   isfileFileNotFoundErrorr^   load)r   rJ   r`   s   &&&r   _load_from_disk!RegressionService._load_from_diskd   sC    ww~~i((#&6xj@PQZP[[\$]^^{{9%%r!   c                R   < V ^8  d   QhRS[ RS[ RS[ RS[RS[S[ ,          RS[RR/# )	r$   rJ   r`   
model_typer3   featuresdata_pointsr%   N)r'   dictr   int)r(   r)   s   "r   r*   r+   i   sF        # !%15cILQUr!   c           
         R pV P                   P                  VWV\        P                  ! V4      \        P                  ! V4      V34       R# )a  
            INSERT INTO ml_regression_models
                (model_id, file_path, model_type, config, features, data_points)
            VALUES (%s, %s, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
                file_path=VALUES(file_path), model_type=VALUES(model_type),
                config=VALUES(config), features=VALUES(features),
                data_points=VALUES(data_points), updated_at=CURRENT_TIMESTAMP
        N)r   rE   jsondumps)r   rJ   r`   rl   r3   rm   rn   sqls   &&&&&&& r   _upsert_metadata"RegressionService._upsert_metadatai   sB     	sJJv

8 4k%
 	r!   c                6   < V ^8  d   QhRS[ RS[S[,          /# rI   r'   r   r   )r(   r)   s   "r   r*   r+   y   s        r!   c                z   V P                   P                  R V34      pV'       g   R# V^ ,          p\        VP                  R4      \        4      '       d!   \
        P                  ! VR,          4      VR&   \        VP                  R4      \        4      '       d!   \
        P                  ! VR,          4      VR&   V# )z>SELECT * FROM ml_regression_models WHERE model_id = %s LIMIT 1Nr3   rm   )r   rE   
isinstancer4   r'   rr   loads)r   rJ   rowsrows   &&  r   _fetch_metadata!RegressionService._fetch_metadatay   s    xx%%Lxk
 1gcggh'-- JJs8}5CMcggj)3//"jjZ9C
O
r!   c                &   < V ^8  d   QhRS[ RS[/# rI   rZ   )r(   r)   s   "r   r*   r+      s      s t r!   c                \   \         P                  ;_uu_ 4        V\         P                  9   d"   \         P                  V,          uuRRR4       #  RRR4       V P                  V4      pVf   \	        RV R24      h V P                  WR,          4      p\         P                  ;_uu_ 4        T\         P                  T&   RRR4       T#   + '       g   i     L; i  \         d.    T P                  P                  RT34       \        RT R24      hi ; i  + '       g   i     T# ; i)u8   Three-layer fetch: process cache → DB metadata + disk.NModel 'z' not foundr`   4DELETE FROM ml_regression_models WHERE model_id = %sz;' file missing. Orphaned record removed. Re-train required.)
r   _cache_lock_cacher~   
ValueErrorri   rg   r   rE   RuntimeError)r   rJ   metarY   s   &&  r   _resolve_model RegressionService._resolve_model   s   ***,333(//9 +*3 + ##H-<wxj<==	**8+5FGG ***18$$X. +) +* ! 	HH""F (#^_ 		 +*s#   ,C>C .DC	8DD+	Nc                   < V ^8  d   QhRS[ RS[P                  RS[P                  RS[ RS[S[,          RS[S[ S[3,          /# )r$   rJ   Xyrl   r3   r%   )r'   pd	DataFrameSeriesr   r   r   )r(   r)   s   "r   r*   r+      sc     a aa <<a 99	a
 a a 
c3har!   c                `    ^ RI HpHp ^ RIHp ^ RIHp	 ^ RIHp
H	pH
pHp \        V4      ^8  d   \        R\        V4       R24      h\        VP                  4      pVR8X  d   \!        V P"                  4      pTP%                  T;'       g    / 4       \'        VP)                  4       4      p\        V4      V,
          pV^ 8  d   V^ 8  d   VV,          VR&   T! R!/ VP+                  4        UUu/ uF  w  ppVR	8w  g   K  VVbK  	  uppB pMe\!        V P,                  4      pTP%                  T;'       g    / 4       T! R!/ VP+                  4        UUu/ uF  w  ppVR	8w  g   K  VVbK  	  uppB pV! 4       pVP/                  VP1                  ^ 4      4      p/ p\        V4      ^
8  d   V	! VVR
^*R7      w  ppppVP3                  VV4       VR8X  d2   VP5                  V4      p\7        \9        V! VV4      4      ^4      VR&   MOVP5                  V4      p\7        \9        V! VV4      4      ^4      VR&   \7        \9        V! VV4      4      ^4      VR&   VP3                  VV4       MVP3                  VV4       RVRVRVRVRVR\        V4      R\:        P<                  ! 4       /p\>        P@                  ;_uu_ 4        V PC                  VV4      pV PE                  VVWOV\        V4      4       V\>        PF                  V&   RRR4       V PH                  PK                  RV RV R\        V4       R24       RVRRRVR\        V4      RVRV/# u uppi u uppi   + '       g   i     La; i  \L         d1   pT PH                  PO                  RT R \Q        T4       24       h Rp?ii ; i)"a+  
Train an XGBoost model.

Args:
    model_id: Unique identifier for this model
    X: Feature DataFrame
    y: Target Series
    model_type: 'classifier' or 'regressor'
    config: Override default XGBoost hyperparameters

Returns:
    {model_id, status, data_points, features, model_type, metrics}
)XGBClassifierXGBRegressor)StandardScaler)train_test_split)classification_reportmean_absolute_errorr2_scoreaccuracy_scorezInsufficient data (z rows, minimum 5 required)
classifierscale_pos_weightr   g?)	test_sizer   accuracymaer2modelscalerrm   rl   r3   rn   
created_atNzTrained z model 'z' on z rowsrJ   statustrainedmetricszError training model 'z': r<   ))xgboostr   r   sklearn.preprocessingr   sklearn.model_selectionr   sklearn.metricsr   r   r   r   lenr   listcolumnsro   CLASSIFIER_DEFAULTSupdaterp   sumitemsREGRESSOR_DEFAULTSfit_transformfillnafitpredictroundfloatr   nowr   r   ra   ru   r   r   info	Exceptionerrorr'   ) r   rJ   r   r   rl   r3   r   r   r   r   r   r   r   r   rm   defaultsposnegkvr   r   X_scaledr   X_trX_tey_try_tepredsrY   r`   es    &&&&&&                          r   trainRegressionService.train   s   *L	;<@C C 1vz #6s1vh>X!YZZAIIH\) 8 89"-!%%'l!fsl7sQw369H/0% B(..:J )A:J$!Q,-,? *.A:J )A B   7 78"-$ A9I (@9IA+,+> )-19I (@ A $%F++AHHQK8H G1v|)9a3R*&dD$ 		$%-!MM$/E*/nT56Q0RTU*VGJ'!MM$/E%*51DT51Q+RTU%VGEN$)%u0E*F$JGDM		(A&		(A& &Hj(s1vhllnG #... ..xA	%%h	:QY[^_`[ab5<!((2 /
 KKx
|8H:U3q6(RWXYH)js1vH7 ])A
(@F /.  	KK 6xjCF8LM	sj   BM2 A#M2 2MM
/M2 :M2 M)M0EM2 AMAM2 M2 M/	*M2 2N-=+N((N-c                P   < V ^8  d   QhRS[ RS[P                  RS[S[,          /# r$   rJ   r   r%   r'   r   r   r   r   )r(   r)   s   "r   r*   r+     s/     G Gc Gbll GtE{ Gr!   c                    V P                  V4      pVR,          R8w  d   \        RV R24      hVR,          P                  W#R,          ,          P                  ^ 4      4      pVR,          P	                  V4      R,          P                  4       # )	z4Return class-1 probabilities for a classifier model.rl   r   r   z' is not a classifierr   rm   r   ):NNN   )r   r   	transformr   predict_probatolistr   rJ   r   rY   r   s   &&&  r   r   RegressionService.predict_proba  s    %%h/< L0wxj0EFGG8$..q1D/E/L/LQ/OPw--h7=DDFFr!   c                P   < V ^8  d   QhRS[ RS[P                  RS[S[,          /# r   r   )r(   r)   s   "r   r*   r+     s*     ; ; ; ;e ;r!   c                   V P                  V4      pVR,          R8w  d   \        RV R24      hVR,          P                  W#R,          ,          P                  ^ 4      4      pVR,          P	                  V4      P                  4       # )zReturn regression predictions.rl   	regressorr   z' is not a regressorr   rm   r   )r   r   r   r   r   r   r   s   &&&  r   r   RegressionService.predict  s{    %%h/< K/wxj0DEFF8$..q1D/E/L/LQ/OPw''188::r!   c                6   < V ^8  d   QhRS[ RS[S[,          /# rI   rx   )r(   r)   s   "r   r*   r+     s     
 
s 
x~ 
r!   c                *   \         P                  ;_uu_ 4        V\         P                  9   da   \         P                  V,          pR VRVR,          RVR,          RVR,          RVR,          RVR,          P                  4       /uuRRR4       #  RRR4       V P	                  V4      pVf   R# R VR ,          RVR,          RVR,          RVR,          RVR,          R\        VR,          4      R\        VR,          4      /#   + '       g   i     Lz; i)rJ   rl   rn   rm   r3   r   N
updated_at)r   r   r   	isoformatr~   r'   )r   rJ   rR   r   s   &&  r   get_model_info RegressionService.get_model_info  s    ***,333%,,X6 !L/!1]#3*ak !L/";";"= +*3 + ##H-<Z($|,4.Z(d8n#d<01#d<01
 	
 +*s   A+DD	c                0   < V ^8  d   QhRS[ S[,          /# r#   )r   r   )r(   r)   s   "r   r*   r+   .  s      T$Z r!   c                |   V P                   P                  R 4      p. pV F  p\        VR,          \        4      '       d
   VR,          M\        P
                  ! VR,          4      pVP                  RVR,          RVR,          RVR,          RVR\        VR,          4      R\        VR,          4      /4       K  	  V# )z}SELECT model_id, model_type, data_points, features, created_at, updated_at FROM ml_regression_models ORDER BY created_at DESCrm   rJ   rl   rn   r   r   )r   rE   rz   r   rr   r{   appendr'   )r   r|   resultr}   rm   s   &    r   list_modelsRegressionService.list_models.  s    xx%%A
 C*4S_d*K*Ks:QUQ[Q[\_`j\kQlHMMC
Oc,/s=1Hc#l"34c#l"34   r!   c                &   < V ^8  d   QhRS[ RS[/# rI   )r'   bool)r(   r)   s   "r   r*   r+   @  s      S T r!   c                   V P                  V4      p\        P                  ;_uu_ 4        V\        P                  9   pR R R 4       Vf   X'       g   R# \        P                  ;_uu_ 4        \        P                  P	                  VR 4       R R R 4       V'       dK    \
        P                  P                  VR,          4      '       d   \
        P                  ! VR,          4       V P                  P                  RV34       R#   + '       g   i     L; i  + '       g   i     L; i  \         d)   pT P                  P                  RT 24        R p?LsR p?ii ; i)NFr`   zCould not delete model file: r   T)r~   r   r   r   popr5   r6   rf   removeOSErrorr   warningr   rE   )r   rJ   r   in_cacher   s   &&   r   delete_modelRegressionService.delete_model@  s    ##H-***#4#;#;;H +<***$$((48 + I77>>${"344IId;/0 	BXK	
 % +*
 +*  I##&CA3$GHHIs5   D,!D+D, 
D, D	D)	,E7EEc                &   < V ^8  d   QhRS[ RS[/# )r$   probabilityr%   r   r'   )r(   r)   s   "r   r*   r+   [  s      5 S r!   c                ~    V \         P                  R ,          8  d   R # V \         P                  R,          8  d   R# R# )r   r   r   )r   RISK_THRESHOLDS_COLLECTION)r   s   &r   collection_risk_level'RegressionService.collection_risk_levelZ  s3    +FFvNN+FFxPPr!   c                &   < V ^8  d   QhRS[ RS[/# )r$   attainment_pctr%   r   )r(   r)   s   "r   r*   r+   c  s       # r!   c                ~    V \         P                  R ,          8:  d   R # V \         P                  R,          8:  d   R# R# )r   r   r   )r   RISK_THRESHOLDS_SALES)r   s   &r   sales_target_risk_level)RegressionService.sales_target_risk_levelb  s3    .DDUKK.DDXNNr!   c                <   < V ^8  d   Qh/ S[ S[S[3,          ;R&   # )r$   r   )r   r'   r   )r(   r)   s   "r   r*   r+      s      cN r!   )r   r   )r   N)%__name__
__module____qualname____firstlineno____doc__r   	threadingLockr   rC   rD   r   r   r   r   r   r8   r   r   rV   ra   ri   ru   r~   r   r   r   r   r   r   r   staticmethodr   r   __annotate_func____static_attributes____classdictcell__)r)   s   @r   r   r      se    FF.."KN.."K 	Qy 	Q' #)$$!?"D(D9!g g
: :4 44H H 
& &
    8a aFG G; ;
 
2 $ 4    _
  r!   r   )r   r5   rr   r  typingr   r   r   r   r   r   pandasr   numpynpr^   app.utils.helpersr   app.models.mysql_modelr	   r   r<   r!   r   <module>r     s9    
   3 3     ( -T Tr!   