safraeli commited on
Commit
ca60893
·
verified ·
1 Parent(s): 0fbf77a

Update backend/api/routes/biology.py

Browse files
Files changed (1) hide show
  1. backend/api/routes/biology.py +53 -24
backend/api/routes/biology.py CHANGED
@@ -46,29 +46,23 @@ async def biology_rule_detail(rule_name: str, hub: DataHub = Depends(get_datahub
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],
@@ -76,17 +70,52 @@ async def biology_chill_units(
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)
 
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
  ):
50
+ """Accumulated chill units from ThingsBoard on-site temperature.
51
 
52
+ Uses Air2 (under panels) and Air1 (open field/ambient) hourly averages.
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
+ from datetime import datetime, timezone
59
+
60
+ def _compute_chill(df, temp_col="airTemperature"):
61
+ if df.empty or temp_col not in df.columns:
62
+ return [], 0.0
63
+ hourly = df[temp_col].resample("1h").mean().dropna()
64
+ if hourly.empty:
65
+ return [], 0.0
 
 
 
 
 
 
66
  temps = hourly.values
67
  chill_h = np.select(
68
  [temps <= 7, (temps > 7) & (temps <= 10), (temps > 10) & (temps <= 18), temps > 18],
 
70
  )
71
  daily = pd.Series(chill_h, index=hourly.index).resample("D").sum().clip(lower=0)
72
  cumulative = daily.cumsum()
73
+ points = [
74
+ {"date": ts.strftime("%Y-%m-%d"), "cu": round(float(cumulative.loc[ts]), 1)}
75
+ for ts in daily.index
76
+ ]
77
+ return points, round(float(cumulative.iloc[-1]), 1) if len(cumulative) else 0.0
78
+
79
+ try:
80
+ from src.data.thingsboard_client import ThingsBoardClient
81
+
82
+ client = ThingsBoardClient()
83
+ start = pd.Timestamp(season_start, tz="UTC")
84
+ end = pd.Timestamp(datetime.now(timezone.utc))
85
+
86
+ # Fetch hourly avg temp from Air2 (under panels) and Air1 (open field)
87
+ df_panels = client.get_timeseries(
88
+ "Air2", ["airTemperature"], start=start, end=end,
89
+ interval_ms=3_600_000, agg="AVG", limit=10000,
90
+ )
91
+ df_open = client.get_timeseries(
92
+ "Air1", ["airTemperature"], start=start, end=end,
93
+ interval_ms=3_600_000, agg="AVG", limit=10000,
94
+ )
95
+
96
+ panels_data, panels_total = _compute_chill(df_panels)
97
+ open_data, open_total = _compute_chill(df_open)
98
+
99
+ # Merge into paired daily data for chart
100
+ panels_by_date = {p["date"]: p["cu"] for p in panels_data}
101
+ open_by_date = {p["date"]: p["cu"] for p in open_data}
102
+ all_dates = sorted(set(list(panels_by_date.keys()) + list(open_by_date.keys())))
103
+
104
+ daily = [
105
+ {
106
+ "date": d,
107
+ "under_panels": panels_by_date.get(d, None),
108
+ "open_field": open_by_date.get(d, None),
109
+ }
110
+ for d in all_dates
111
+ ]
112
 
113
  return {
114
  "season_start": season_start,
115
+ "latest_under_panels": panels_total,
116
+ "latest_open_field": open_total,
117
+ "days_counted": len(all_dates),
118
+ "daily": daily,
 
 
 
 
119
  }
120
  except Exception as exc:
121
  log.error("Chill units failed: %s", exc)