Vadashuk commited on
Commit
633e14e
·
verified ·
1 Parent(s): 76eb57f

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +1 -273
main.py CHANGED
@@ -411,17 +411,6 @@ app = FastAPI(
411
  default_response_class=ORJSONResponse,
412
  )
413
 
414
- # In-memory model state
415
- @dataclass
416
- class ModelSlot:
417
- model: Optional["DATFIDModel"] = None
418
- result: Optional[Any] = None
419
-
420
- # Per-ID registry + silent internal stats (not returned)
421
- model_store: Dict[str, ModelSlot] = {}
422
- fit_stats: Dict[str, int] = {"fitted": 0, "failed": 0} # internal only
423
- fc_stats: Dict[str, int] = {"forecasted": 0, "skipped": 0} # internal only
424
-
425
  @app.get("/")
426
  def root():
427
  return {"message": "DATFID API is alive."}
@@ -615,265 +604,4 @@ async def modelforecast_file(
615
  except Exception as e:
616
  traceback.print_exc()
617
  raise HTTPException(status_code=500, detail=f"Error during model forecast (file): {str(e)}")
618
-
619
- @app.post("/modelfit_ind/")
620
- async def modelfit_ind(
621
- df: List[Dict] = Body(...),
622
- id_col: str = Body(...),
623
- time_col: str = Body(...),
624
- y: str = Body(...),
625
- lag_y: Any = Body(None),
626
- lagged_features: Any = Body({}),
627
- current_features: Any = Body([]),
628
- filter_by_significance: bool = Body(False),
629
- meanvar_test: bool = Body(False),
630
- signif: Any = Body(0.05),
631
- ):
632
- global model_store, fit_stats
633
- try:
634
- sig_val = float(signif)
635
- except (TypeError, ValueError):
636
- sig_val = 0.05
637
- df1 = pd.DataFrame(df)
638
-
639
- if id_col not in df1.columns:
640
- raise HTTPException(status_code=400, detail=f"id_col '{id_col}' not in data.")
641
-
642
- # reset registry + counters
643
- model_store = {}
644
- fit_stats = {"fitted": 0, "failed": 0}
645
-
646
- per_id_results: Dict[str, Dict[str, Any]] = {}
647
-
648
- for id_val, sub in df1.groupby(id_col, sort=False):
649
- key = str(id_val)
650
- try:
651
- mdl = DATFIDModel(
652
- df=sub,
653
- id_col=id_col,
654
- time_col=time_col,
655
- y=y,
656
- lag_y=lag_y,
657
- lagged_features=lagged_features,
658
- current_features=current_features,
659
- filter_by_significance=filter_by_significance,
660
- meanvar_test=meanvar_test,
661
- signif=sig_val,
662
- )
663
- rj = await run_in_threadpool(mdl.fit)
664
- model_store[key] = ModelSlot(model=mdl, result=rj)
665
- fit_stats["fitted"] += 1
666
- result = rj.result
667
- result_dict: Dict[str, Any] = {}
668
- for k, v in result.__dict__.items():
669
- if isinstance(v, pd.DataFrame):
670
- result_dict[k] = v.to_dict(orient="records")
671
- elif isinstance(v, pd.Series):
672
- result_dict[k] = v.to_dict()
673
- elif isinstance(v, (list, dict, str, int, float, bool, type(None))):
674
- result_dict[k] = v
675
- else:
676
- result_dict[k] = str(v)
677
- per_id_results[key] = result_dict
678
- except Exception:
679
- traceback.print_exc()
680
- fit_stats["failed"] += 1
681
-
682
- return per_id_results
683
-
684
- @app.post("/modelforecast_ind/")
685
- async def modelforecast_ind(
686
- df_forecast: List[Dict] = Body(...),
687
- ):
688
- # Use per-ID models created by /modelfit_ind/
689
- global model_store
690
- if not model_store:
691
- raise HTTPException(status_code=400, detail="No per-ID models. Call /modelfit_ind/ first.")
692
-
693
- dfF = pd.DataFrame(df_forecast)
694
-
695
- # Infer the id column name from any stored model (same as used in fit)
696
- try:
697
- any_slot = next(iter(model_store.values()))
698
- id_col_name = getattr(any_slot.model, "id_col")
699
- except StopIteration:
700
- raise HTTPException(status_code=400, detail="No per-ID models available.")
701
- except Exception:
702
- raise HTTPException(status_code=500, detail="Could not infer id column from stored models.")
703
-
704
- if id_col_name not in dfF.columns:
705
- raise HTTPException(status_code=400, detail=f"Forecast data must include id column '{id_col_name}'.")
706
-
707
- frames = []
708
- for id_val, sub in dfF.groupby(id_col_name, sort=False):
709
- key = str(id_val)
710
- slot = model_store.get(key)
711
- if not slot or not slot.model or not slot.result:
712
- continue
713
- try:
714
- fc = await run_in_threadpool(
715
- slot.model.forecast,
716
- extern_self=slot.result,
717
- df_forecast=sub,
718
- )
719
- if id_col_name not in fc.columns:
720
- fc[id_col_name] = key
721
- for col in list(fc.columns):
722
- if col and col.lower() == "product":
723
- fc = fc.drop(columns=[col])
724
- break
725
- frames.append(fc)
726
- except Exception:
727
- traceback.print_exc()
728
-
729
- out_df = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
730
- return out_df.to_dict(orient="records")
731
-
732
-
733
- @app.post("/modelfit-file_ind/")
734
- async def modelfit_file_ind(
735
- file: UploadFile = File(...),
736
- id_col: str = Form(...),
737
- time_col: str = Form(...),
738
- y: str = Form(...),
739
- lag_y: str = Form(""),
740
- lagged_features: str = Form(""),
741
- current_features: str = Form(""),
742
- filter_by_significance: str = Form("false"),
743
- meanvar_test: str = Form("false"),
744
- signif: str = Form("0.05"),
745
- ):
746
- global model_store, fit_stats
747
- try:
748
- df = _read_table_from_upload(file)
749
-
750
- for col in df.columns:
751
- if pd.api.types.is_datetime64_any_dtype(df[col]):
752
- df[col] = df[col].astype(str)
753
-
754
- if id_col not in df.columns:
755
- raise HTTPException(status_code=400, detail=f"id_col '{id_col}' not in data.")
756
-
757
- lagged = _maybe_json_dict(lagged_features)
758
- curr = _maybe_json_list(current_features)
759
- filt_sig = str(filter_by_significance).strip().lower() == "true"
760
- mv_test = str(meanvar_test).strip().lower() == "true"
761
- ly = None if (lag_y is None or lag_y.strip() == "") else lag_y.strip()
762
- try:
763
- sig_val = float(signif)
764
- except (TypeError, ValueError):
765
- sig_val = 0.05
766
-
767
- model_store = {}
768
- fit_stats = {"fitted": 0, "failed": 0}
769
- report_parts: List[str] = []
770
-
771
- for id_val, sub in df.groupby(id_col, sort=False):
772
- key = str(id_val)
773
- try:
774
- mdl = DATFIDModel(
775
- df=sub,
776
- id_col=id_col,
777
- time_col=time_col,
778
- y=y,
779
- lag_y=ly,
780
- lagged_features=lagged,
781
- current_features=curr,
782
- filter_by_significance=filt_sig,
783
- meanvar_test=mv_test,
784
- signif=sig_val,
785
- )
786
- rj = await run_in_threadpool(mdl.fit)
787
- model_store[key] = ModelSlot(model=mdl, result=rj)
788
- fit_stats["fitted"] += 1
789
- section = f"=== Entity ({id_col}): {key} ===\n\n" + _result_to_text(rj.result)
790
- report_parts.append(section)
791
- except Exception:
792
- traceback.print_exc()
793
- fit_stats["failed"] += 1
794
-
795
- report_text = ("\n\n" + "=" * 72 + "\n\n").join(report_parts)
796
- tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt")
797
- tmp.write(report_text.encode("utf-8"))
798
- tmp.flush()
799
- tmp.close()
800
- return FileResponse(
801
- tmp.name,
802
- media_type="text/plain; charset=utf-8",
803
- filename="result.txt",
804
- )
805
- except HTTPException:
806
- raise
807
- except Exception as e:
808
- traceback.print_exc()
809
- raise HTTPException(status_code=500, detail=f"Error during model fit (file_ind): {str(e)}")
810
-
811
-
812
- @app.post("/modelforecast-file_ind/")
813
- async def modelforecast_file_ind(
814
- df_forecast: UploadFile = File(...),
815
- ):
816
- global model_store
817
- if not model_store:
818
- raise HTTPException(status_code=400, detail="No per-ID models. Call /modelfit-file_ind/ or /modelfit_ind/ first.")
819
- try:
820
- df_fc = _read_table_from_upload(df_forecast)
821
- for col in df_fc.columns:
822
- if pd.api.types.is_datetime64_any_dtype(df_fc[col]):
823
- df_fc[col] = df_fc[col].astype(str)
824
-
825
- try:
826
- any_slot = next(iter(model_store.values()))
827
- id_col_name = getattr(any_slot.model, "id_col")
828
- except StopIteration:
829
- raise HTTPException(status_code=400, detail="No per-ID models available.")
830
- except Exception:
831
- raise HTTPException(status_code=500, detail="Could not infer id column from stored models.")
832
-
833
- if id_col_name not in df_fc.columns:
834
- raise HTTPException(status_code=400, detail=f"Forecast data must include id column '{id_col_name}'.")
835
-
836
- frames = []
837
- for id_val, sub in df_fc.groupby(id_col_name, sort=False):
838
- key = str(id_val)
839
- slot = model_store.get(key)
840
- if not slot or not slot.model or not slot.result:
841
- continue
842
- try:
843
- fc = await run_in_threadpool(
844
- slot.model.forecast,
845
- extern_self=slot.result,
846
- df_forecast=sub,
847
- )
848
- if id_col_name not in fc.columns:
849
- fc[id_col_name] = key
850
- for col in list(fc.columns):
851
- if col and col.lower() == "product":
852
- fc = fc.drop(columns=[col])
853
- break
854
- frames.append(fc)
855
- except Exception:
856
- traceback.print_exc()
857
-
858
- out_df = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
859
-
860
- tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
861
- tmp_path = tmp.name
862
- tmp.close()
863
- buf = io.StringIO()
864
- out_df.to_csv(buf, index=False)
865
- csv_text = buf.getvalue()
866
- csv_text = "sep=,\n" + csv_text
867
- with open(tmp_path, "w", encoding="utf-8-sig", newline="") as f:
868
- f.write(csv_text)
869
-
870
- return FileResponse(
871
- tmp_path,
872
- media_type="text/csv; charset=utf-8",
873
- filename="forecast.csv",
874
- )
875
- except HTTPException:
876
- raise
877
- except Exception as e:
878
- traceback.print_exc()
879
- raise HTTPException(status_code=500, detail=f"Error during model forecast (file_ind): {str(e)}")
 
411
  default_response_class=ORJSONResponse,
412
  )
413
 
 
 
 
 
 
 
 
 
 
 
 
414
  @app.get("/")
415
  def root():
416
  return {"message": "DATFID API is alive."}
 
604
  except Exception as e:
605
  traceback.print_exc()
606
  raise HTTPException(status_code=500, detail=f"Error during model forecast (file): {str(e)}")
607
+