safraeli commited on
Commit
14816bc
·
verified ·
1 Parent(s): 9852c26

Update backend/api/routes/biology.py

Browse files
Files changed (1) hide show
  1. backend/api/routes/biology.py +51 -1
backend/api/routes/biology.py CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
5
  import logging
6
  from datetime import date
7
 
8
- from fastapi import APIRouter, Depends, HTTPException
9
 
10
  from backend.api.deps import get_datahub
11
  from src.data.data_providers import DataHub
@@ -41,3 +41,53 @@ async def biology_rules(hub: DataHub = Depends(get_datahub)):
41
  async def biology_rule_detail(rule_name: str, hub: DataHub = Depends(get_datahub)):
42
  """Explain a specific biology rule."""
43
  return hub.biology.explain_rule(rule_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  import logging
6
  from datetime import date
7
 
8
+ from fastapi import APIRouter, Depends, HTTPException, Query
9
 
10
  from backend.api.deps import get_datahub
11
  from src.data.data_providers import DataHub
 
41
  async def biology_rule_detail(rule_name: str, hub: DataHub = Depends(get_datahub)):
42
  """Explain a specific biology rule."""
43
  return hub.biology.explain_rule(rule_name)
44
+
45
+
46
+ @router.get("/chill-units")
47
+ async def biology_chill_units(
48
+ season_start: str = Query("2025-11-01", description="Season start (YYYY-MM-DD)"),
49
+ hub: DataHub = Depends(get_datahub),
50
+ ):
51
+ """Accumulated chill units from IMS hourly temperature.
52
+
53
+ Model: T<=7 → +1 CU/h, 7<T<=10 → +0.5, 10<T<=18 → 0, T>18 → -1.
54
+ Daily totals clipped at 0.
55
+ """
56
+ import numpy as np
57
+ import pandas as pd
58
+
59
+ try:
60
+ df = hub.weather._load_df()
61
+ if df.empty:
62
+ return {"error": "No IMS data available"}
63
+ if "timestamp_utc" in df.columns:
64
+ df = df.set_index(pd.to_datetime(df["timestamp_utc"], utc=True))
65
+
66
+ start = pd.Timestamp(season_start, tz="UTC")
67
+ subset = df.loc[start:]
68
+ if subset.empty or "air_temperature_c" not in subset.columns:
69
+ return {"error": "No temperature data in season range"}
70
+
71
+ hourly = subset["air_temperature_c"].resample("1h").mean().dropna()
72
+ temps = hourly.values
73
+ chill_h = np.select(
74
+ [temps <= 7, (temps > 7) & (temps <= 10), (temps > 10) & (temps <= 18), temps > 18],
75
+ [1.0, 0.5, 0.0, -1.0],
76
+ )
77
+ daily = pd.Series(chill_h, index=hourly.index).resample("D").sum().clip(lower=0)
78
+ cumulative = daily.cumsum()
79
+
80
+ return {
81
+ "season_start": season_start,
82
+ "total_chill_units": round(float(cumulative.iloc[-1]), 1) if len(cumulative) else 0,
83
+ "days_counted": len(daily),
84
+ "daily": [
85
+ {"date": ts.strftime("%Y-%m-%d"),
86
+ "cumulative_cu": round(float(cumulative.loc[ts]), 1),
87
+ "daily_cu": round(float(daily.loc[ts]), 1)}
88
+ for ts in daily.index
89
+ ],
90
+ }
91
+ except Exception as exc:
92
+ log.error("Chill units failed: %s", exc)
93
+ return {"error": f"Chill units failed: {exc}"}