873 lines
19 KiB
Markdown
873 lines
19 KiB
Markdown
# مستند فرمولهای `dashboard_data/cards`
|
||
|
||
این فایل توضیح میدهد هر کارت در `dashboard_data/cards` چطور دادههای خروجی خود را محاسبه میکند، از چه فیلدهایی استفاده میکند، و چه fallbackهایی دارد.
|
||
|
||
## منبع دادههای مشترک
|
||
|
||
کانتکست بیشتر کارتها از `dashboard_data/context.py` میآید:
|
||
|
||
- `sensor`: رکورد اصلی سنسور
|
||
- `location`: لوکیشن سنسور
|
||
- `depths`: دادههای عمق خاک از `SoilDepthData`
|
||
- `forecasts`: حداکثر ۷ پیشبینی آبوهوا از امروز به بعد
|
||
- `history`: حداکثر ۳۰ رکورد تاریخچه سنسور، مرتبشده از جدید به قدیم
|
||
- `plants`: گیاههای متصل به سنسور
|
||
- `irrigation_methods`: حداکثر ۵ روش آبیاری
|
||
|
||
## توابع کمکی مشترک
|
||
|
||
این توابع در `dashboard_data/card_utils.py` استفاده میشوند:
|
||
|
||
- `safe_number(value, default=0)`: اگر مقدار `None` باشد، `default` برمیگرداند.
|
||
- `average(values, default=0)`: میانگین مقادیر غیر `None` را میدهد؛ اگر هیچ مقداری نبود `default` برمیگرداند.
|
||
- `latest_history_value(history, field_name, default=None)`: مقدار `field_name` را از جدیدترین رکورد history میگیرد.
|
||
- `compute_trend(current, previous)`:
|
||
- `diff = round(current_value - previous_value, 1)`
|
||
- `trend = "positive"` اگر `diff >= 0`، وگرنه `"negative"`
|
||
|
||
---
|
||
|
||
## 1) `farm_overview_kpis.py`
|
||
|
||
تابع سازنده: `build_farm_overview_kpis`
|
||
|
||
### ورودیهای اصلی
|
||
|
||
- `sensor.soil_moisture` → `moisture`
|
||
- `sensor.soil_ph` → `ph`
|
||
- `sensor.electrical_conductivity` → `ec`
|
||
- `sensor.soil_temperature`
|
||
- میانگین `forecast.humidity_mean` برای ۳ پیشبینی اول → `humidity`
|
||
|
||
### فرمولها
|
||
|
||
#### 1. امتیاز سلامت مزرعه (`farm_health_score`)
|
||
|
||
```text
|
||
health_score = clamp(
|
||
round(
|
||
100
|
||
- abs(65 - moisture)
|
||
- (abs(6.8 - ph) * 10)
|
||
- (ec * 5)
|
||
),
|
||
0,
|
||
100
|
||
)
|
||
```
|
||
|
||
توضیح:
|
||
- رطوبت ایدهآل ۶۵٪ فرض شده.
|
||
- pH ایدهآل ۶.۸ فرض شده.
|
||
- EC بالاتر، امتیاز را کم میکند.
|
||
|
||
آستانههای نمایش:
|
||
- اگر `health_score >= 70` → وضعیت `خوب` و رنگ `success`
|
||
- در غیر این صورت → `متوسط` و رنگ `warning`
|
||
|
||
#### 2. شاخص تنش آبی (`water_stress_index`)
|
||
|
||
```text
|
||
water_stress = clamp(round(35 - (moisture / 2)), 0, 100)
|
||
```
|
||
|
||
توضیح:
|
||
- هرچه رطوبت خاک بیشتر شود، تنش آبی کمتر میشود.
|
||
|
||
آستانه نمایش:
|
||
- اگر `water_stress <= 20` → `پایین`
|
||
- در غیر این صورت → `متوسط`
|
||
|
||
#### 3. ریسک بیماری (`disease_risk`)
|
||
|
||
```text
|
||
disease_risk = clamp(
|
||
round((humidity * 0.4) + (soil_temperature * 0.6) - 20),
|
||
0,
|
||
100
|
||
)
|
||
```
|
||
|
||
توضیح:
|
||
- دمای خاک وزن ۶۰٪ دارد.
|
||
- رطوبت هوا وزن ۴۰٪ دارد.
|
||
|
||
آستانه نمایش:
|
||
- اگر `disease_risk < 30` → `پایین`
|
||
- در غیر این صورت → `متوسط`
|
||
|
||
#### 4. میانگین رطوبت خاک (`avg_soil_moisture`)
|
||
|
||
```text
|
||
avg_soil_moisture = round(moisture)
|
||
```
|
||
|
||
نکته:
|
||
- اینجا عملاً فقط از `sensor.soil_moisture` فعلی استفاده میشود و واقعاً میانگین چند سنسور یا چند ناحیه محاسبه نمیشود.
|
||
|
||
آستانه نمایش:
|
||
- اگر `45 <= moisture <= 75` → `بهینه`
|
||
- در غیر این صورت → `نیازمند بررسی`
|
||
|
||
#### 5. پیشبینی عملکرد (`yield_prediction`)
|
||
|
||
```text
|
||
yield_prediction = round(max(5, health_score / 2.1), 1)
|
||
```
|
||
|
||
فرمول متن chip:
|
||
|
||
```text
|
||
yield_chip = "+" + str(max(0, health_score - 50)) + "%"
|
||
```
|
||
|
||
#### 6. ریسک آفات (`pest_risk`)
|
||
|
||
```text
|
||
pest_risk = max(5, round(disease_risk * 0.7))
|
||
```
|
||
|
||
نکته:
|
||
- ریسک آفات بهصورت مستقیم از ۷۰٪ ریسک بیماری ساخته شده.
|
||
|
||
---
|
||
|
||
## 2) `farm_weather_card.py`
|
||
|
||
تابع سازنده: `build_farm_weather_card`
|
||
|
||
### منطق کلی
|
||
|
||
اگر `forecasts` خالی باشد:
|
||
- `condition = "نامشخص"`
|
||
- `temperature = 0`
|
||
- `humidity = 0`
|
||
- `windSpeed = 0`
|
||
- `chartData.labels = []`
|
||
- `chartData.series = [[]]`
|
||
|
||
در غیر این صورت:
|
||
|
||
### فرمولها
|
||
|
||
#### 1. وضعیت آبوهوا
|
||
|
||
```text
|
||
condition = weather_condition(current_forecast.weather_code)
|
||
```
|
||
|
||
که `weather_code` با جدول `WMO_CONDITIONS` به متن فارسی تبدیل میشود.
|
||
|
||
#### 2. دما
|
||
|
||
```text
|
||
temperature = round(
|
||
safe_number(current_forecast.temperature_mean, current_forecast.temperature_max)
|
||
)
|
||
```
|
||
|
||
یعنی:
|
||
- اول `temperature_mean`
|
||
- اگر `None` بود، `temperature_max`
|
||
|
||
#### 3. رطوبت
|
||
|
||
```text
|
||
humidity = round(average([current_forecast.humidity_mean], default=0))
|
||
```
|
||
|
||
نکته:
|
||
- چون فقط یک مقدار داخل `average` قرار میگیرد، عملاً همان `humidity_mean` فعلی است.
|
||
|
||
#### 4. سرعت باد
|
||
|
||
```text
|
||
windSpeed = round(safe_number(current_forecast.wind_speed_max, 0))
|
||
```
|
||
|
||
#### 5. نمودار دما
|
||
|
||
برای ۷ روز اول:
|
||
|
||
```text
|
||
labels = [str(forecast.forecast_date) for forecast in forecasts[:7]]
|
||
series = [[round(safe_number(forecast.temperature_mean, 0)) for forecast in forecasts[:7]]]
|
||
```
|
||
|
||
---
|
||
|
||
## 3) `farm_alerts_tracker.py`
|
||
|
||
تابع سازنده: `build_farm_alerts_tracker`
|
||
|
||
### ورودیها
|
||
|
||
- `sensor.soil_moisture` → `moisture`
|
||
- میانگین `humidity_mean` برای ۳ forecast اول → `humidity`
|
||
- `temperature_min` برای ۳ forecast اول
|
||
|
||
### فرمولها
|
||
|
||
#### 1. هشدار کمبود آب
|
||
|
||
```text
|
||
low_water_count = 2 if moisture < 45 else 0
|
||
```
|
||
|
||
#### 2. هشدار ریسک قارچی
|
||
|
||
```text
|
||
fungal_count = 1 if (humidity > 70 and moisture > 60) else 0
|
||
```
|
||
|
||
#### 3. هشدار یخبندان
|
||
|
||
```text
|
||
frost_count = count(
|
||
forecast for first 3 forecasts
|
||
if temperature_min <= 0
|
||
)
|
||
```
|
||
|
||
در کد:
|
||
|
||
```text
|
||
frost_count = sum(
|
||
1 for forecast in forecasts[:3]
|
||
if safe_number(forecast.temperature_min, 10) <= 0
|
||
)
|
||
```
|
||
|
||
#### 4. مجموع هشدارها
|
||
|
||
```text
|
||
totalAlerts = low_water_count + fungal_count + frost_count
|
||
```
|
||
|
||
#### 5. مقدار radial bar
|
||
|
||
```text
|
||
radialBarValue = min(100, totalAlerts * 10)
|
||
```
|
||
|
||
---
|
||
|
||
## 4) `sensor_values_list.py`
|
||
|
||
تابع سازنده: `build_sensor_values_list`
|
||
|
||
این کارت برای هر آیتم، مقدار فعلی و trend را میسازد.
|
||
|
||
### فرمول trend
|
||
|
||
برای هر سنسور که از `compute_trend` استفاده میکند:
|
||
|
||
```text
|
||
trendNumber = round(current - previous, 1)
|
||
trend = "positive" if trendNumber >= 0 else "negative"
|
||
```
|
||
|
||
### آیتمها
|
||
|
||
#### 1. دمای هوا
|
||
|
||
```text
|
||
title = round(current_weather.temperature_mean or 0) + "°C"
|
||
previous = latest_history_value(history, "soil_temperature", 0)
|
||
```
|
||
|
||
نکته مهم:
|
||
- trend دمای هوا با `soil_temperature` از history مقایسه میشود، نه با history دمای هوا.
|
||
|
||
#### 2. دمای خاک
|
||
|
||
```text
|
||
current = sensor.soil_temperature
|
||
previous = latest_history_value(history, "soil_temperature", 0)
|
||
```
|
||
|
||
#### 3. رطوبت هوا
|
||
|
||
```text
|
||
current = current_weather.humidity_mean or 0
|
||
previous = 0
|
||
```
|
||
|
||
نکته:
|
||
- همیشه نسبت به صفر trend میگیرد، نه history.
|
||
|
||
#### 4. رطوبت خاک
|
||
|
||
```text
|
||
current = sensor.soil_moisture
|
||
previous = latest_history_value(history, "soil_moisture", 0)
|
||
```
|
||
|
||
#### 5. pH خاک
|
||
|
||
```text
|
||
current = sensor.soil_ph
|
||
previous = latest_history_value(history, "soil_ph", 0)
|
||
```
|
||
|
||
#### 6. هدایت الکتریکی
|
||
|
||
```text
|
||
current = sensor.electrical_conductivity
|
||
previous = latest_history_value(history, "electrical_conductivity", 0)
|
||
```
|
||
|
||
#### 7. شدت نور
|
||
|
||
```text
|
||
title = "850"
|
||
trendNumber = 0
|
||
trend = "positive"
|
||
```
|
||
|
||
نکته:
|
||
- این مقدار کاملاً ثابت (hard-coded) است و فرمولی ندارد.
|
||
|
||
#### 8. سرعت باد
|
||
|
||
```text
|
||
current = current_weather.wind_speed_max or 0
|
||
previous = 0
|
||
```
|
||
|
||
نکته:
|
||
- trend سرعت باد هم نسبت به صفر محاسبه میشود.
|
||
|
||
---
|
||
|
||
## 5) `sensor_radar_chart.py`
|
||
|
||
تابع سازنده: `build_sensor_radar_chart`
|
||
|
||
### تابع نرمالسازی
|
||
|
||
```text
|
||
to_score(value, lower, upper):
|
||
if value is None -> 0
|
||
if value <= lower -> 0
|
||
if value >= upper -> 100
|
||
else -> round(((value - lower) / (upper - lower)) * 100)
|
||
```
|
||
|
||
### سری «امروز»
|
||
|
||
بهترتیب:
|
||
|
||
```text
|
||
soil_temperature_score = to_score(sensor.soil_temperature, 0, 40)
|
||
soil_moisture_score = to_score(sensor.soil_moisture, 0, 100)
|
||
soil_ph_score = to_score(sensor.soil_ph, 0, 14)
|
||
ec_score = to_score(sensor.electrical_conductivity, 0, 5)
|
||
light_score = 85
|
||
wind_score = to_score(current_weather.wind_speed_max if current_weather else 0, 0, 30)
|
||
```
|
||
|
||
خروجی:
|
||
|
||
```text
|
||
series[0].data = [
|
||
soil_temperature_score,
|
||
soil_moisture_score,
|
||
soil_ph_score,
|
||
ec_score,
|
||
85,
|
||
wind_score
|
||
]
|
||
```
|
||
|
||
### سری «ایدهآل»
|
||
|
||
```text
|
||
[80, 70, 75, 75, 90, 50]
|
||
```
|
||
|
||
نکته:
|
||
- این مقادیر ثابت هستند و از دیتابیس محاسبه نمیشوند.
|
||
|
||
---
|
||
|
||
## 6) `sensor_comparison_chart.py`
|
||
|
||
تابع سازنده: `build_sensor_comparison_chart`
|
||
|
||
### ورودیها
|
||
|
||
- `current_sensor.soil_moisture` → `current_value`
|
||
- `history[:7]` → دادههای هفته جاری
|
||
- `history[7:14]` → دادههای هفته قبل
|
||
|
||
### فرمولها
|
||
|
||
#### 1. مقدار فعلی
|
||
|
||
```text
|
||
currentValue = round(sensor.soil_moisture)
|
||
```
|
||
|
||
#### 2. سری هفته جاری
|
||
|
||
```text
|
||
recent = reversed(history[:7])
|
||
this_week = [round(item.soil_moisture or current_value) for item in recent]
|
||
```
|
||
|
||
اگر کمتر از ۷ مقدار باشد:
|
||
|
||
```text
|
||
while len(this_week) < 7:
|
||
this_week.append(current_value)
|
||
```
|
||
|
||
#### 3. سری هفته قبل
|
||
|
||
```text
|
||
previous = reversed(history[7:14])
|
||
last_week = [round(item.soil_moisture or (current_value - 5)) for item in previous]
|
||
```
|
||
|
||
اگر کمتر از ۷ مقدار باشد:
|
||
|
||
```text
|
||
while len(last_week) < 7:
|
||
last_week.append(max(0, current_value - 5))
|
||
```
|
||
|
||
#### 4. درصد تغییر نسبت به هفته قبل
|
||
|
||
```text
|
||
avg_this = sum(this_week) / len(this_week)
|
||
avg_last = sum(last_week) / len(last_week)
|
||
delta = round(((avg_this - avg_last) / avg_last) * 100) if avg_last else 0
|
||
```
|
||
|
||
نمایش متن:
|
||
|
||
```text
|
||
vsLastWeek = f"{'+' if delta >= 0 else ''}{delta}%"
|
||
vsLastWeekValue = delta
|
||
```
|
||
|
||
#### 5. دستهبندی روزها
|
||
|
||
روزهای ۷ روز اخیر با نام فارسی weekday ساخته میشوند:
|
||
|
||
```text
|
||
categories = [
|
||
PERSIAN_WEEKDAYS[(today - offset_days).weekday()]
|
||
for offset_days in range(6, -1, -1)
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 7) `anomaly_detection_card.py`
|
||
|
||
تابع سازنده: `build_anomaly_detection_card`
|
||
|
||
این کارت anomalyها را فقط برای دو شاخص تولید میکند: رطوبت خاک و pH خاک.
|
||
|
||
### 1. anomaly رطوبت خاک
|
||
|
||
فقط وقتی ساخته میشود که:
|
||
|
||
```text
|
||
moisture < 45
|
||
```
|
||
|
||
ساختار خروجی:
|
||
|
||
```text
|
||
value = round(moisture) + "%"
|
||
expected = "45-65%"
|
||
deviation = round(moisture - 55) + "%"
|
||
severity = "warning"
|
||
```
|
||
|
||
نکته:
|
||
- deviation نسبت به نقطه مرجع ۵۵٪ محاسبه میشود، نه مرز ۴۵٪.
|
||
|
||
### 2. anomaly pH خاک
|
||
|
||
فقط وقتی ساخته میشود که:
|
||
|
||
```text
|
||
soil_ph < 6 or soil_ph > 7
|
||
```
|
||
|
||
ساختار خروجی:
|
||
|
||
```text
|
||
value = format(soil_ph, ".1f")
|
||
expected = "6.0-7.0"
|
||
deviation = round(soil_ph - 6.5, 1)
|
||
severity = "error" if (soil_ph < 5.5 or soil_ph > 7.5) else "warning"
|
||
```
|
||
|
||
---
|
||
|
||
## 8) `farm_alerts_timeline.py`
|
||
|
||
تابع سازنده: `build_farm_alerts_timeline`
|
||
|
||
### منطق
|
||
|
||
این کارت هیچ فرمول داخلی ندارد و داده را مستقیماً از `ai_bundle` برمیدارد:
|
||
|
||
```text
|
||
alerts = ai_bundle.get("timeline", [])
|
||
```
|
||
|
||
---
|
||
|
||
## 9) `water_need_prediction.py`
|
||
|
||
تابع سازنده: `build_water_need_prediction`
|
||
|
||
برای ۷ forecast اول:
|
||
|
||
### فرمول نیاز آبی روزانه
|
||
|
||
```text
|
||
et0 = safe_number(forecast.et0, 4)
|
||
rain = safe_number(forecast.precipitation, 0)
|
||
need = max(0, round((et0 * 100) - (rain * 20)))
|
||
```
|
||
|
||
توضیح:
|
||
- `ET0` در ۱۰۰ ضرب میشود.
|
||
- بارش در ۲۰ ضرب و از آن کم میشود.
|
||
- مقدار منفی به صفر clamp میشود.
|
||
|
||
### خروجی نهایی
|
||
|
||
```text
|
||
daily_needs = [need for first 7 forecasts]
|
||
totalNext7Days = sum(daily_needs)
|
||
categories = ["روز 1", "روز 2", ...]
|
||
series = [{"name": "نیاز آبی", "data": daily_needs}]
|
||
```
|
||
|
||
واحد خروجی:
|
||
|
||
```text
|
||
unit = "m³"
|
||
```
|
||
|
||
---
|
||
|
||
## 10) `harvest_prediction_card.py`
|
||
|
||
تابع سازنده: `build_harvest_prediction_card`
|
||
|
||
### ورودیها
|
||
|
||
- میانگین `temperature_mean` تمام forecastها → `avg_temp`
|
||
- `sensor.soil_moisture` → `moisture_factor`
|
||
- نام اولین گیاه → `plant_name`
|
||
|
||
### فرمولها
|
||
|
||
#### 1. میانگین دما
|
||
|
||
```text
|
||
avg_temp = average([forecast.temperature_mean for forecast in forecasts], default=24)
|
||
```
|
||
|
||
#### 2. فاکتور رطوبت
|
||
|
||
```text
|
||
moisture_factor = sensor.soil_moisture if available else 50
|
||
```
|
||
|
||
#### 3. روز باقیمانده تا برداشت
|
||
|
||
```text
|
||
days_until = max(10, int(90 - avg_temp - (moisture_factor / 5)))
|
||
```
|
||
|
||
توضیح:
|
||
- هرچه دمای متوسط بیشتر باشد، `days_until` کمتر میشود.
|
||
- هرچه رطوبت خاک بیشتر باشد، `days_until` کمتر میشود.
|
||
- حداقل ۱۰ روز است.
|
||
|
||
#### 4. تاریخ برداشت و بازه بهینه
|
||
|
||
```text
|
||
target_date = today + days_until
|
||
optimalWindowStart = target_date - 3 days
|
||
optimalWindowEnd = target_date + 3 days
|
||
```
|
||
|
||
#### 5. توضیح متنی
|
||
|
||
اگر گیاه وجود داشته باشد:
|
||
|
||
```text
|
||
description = "بر اساس دمای فعلی، رطوبت خاک و اطلاعات <plant_name>. بازه بهینه برداشت محاسبه شده است."
|
||
```
|
||
|
||
اگر گیاهی وجود نداشته باشد:
|
||
|
||
```text
|
||
plant_name = "محصول"
|
||
```
|
||
|
||
---
|
||
|
||
## 11) `yield_prediction_chart.py`
|
||
|
||
تابع سازنده: `build_yield_prediction_chart`
|
||
|
||
### فرمولها
|
||
|
||
#### 1. مقدار پایه
|
||
|
||
```text
|
||
base = max(10, round(sensor.soil_moisture * 0.6))
|
||
```
|
||
|
||
#### 2. سری سال جاری
|
||
|
||
```text
|
||
current_year = [
|
||
base + 0,
|
||
base + 2,
|
||
base + 4,
|
||
base + 6,
|
||
base + 8,
|
||
base + 10,
|
||
base + 12,
|
||
base + 11,
|
||
base + 9,
|
||
base + 7,
|
||
base + 5,
|
||
base + 4
|
||
]
|
||
```
|
||
|
||
#### 3. سری سال گذشته
|
||
|
||
```text
|
||
last_year = [value - 3 for value in current_year]
|
||
```
|
||
|
||
#### 4. خلاصه کارت
|
||
|
||
عملکرد پیشبینیشده:
|
||
|
||
```text
|
||
summary[0].amount = current_year[9] + " تن"
|
||
```
|
||
|
||
یعنی مقدار ماه دهم لیست (اندیس ۹).
|
||
|
||
تاریخ برداشت:
|
||
|
||
```text
|
||
harvest_month = "حدود " + str(today.month)
|
||
summary[1].amount = "+8%"
|
||
```
|
||
|
||
نکته:
|
||
- `+8%` مقدار ثابت است و از فرمول نیامده.
|
||
|
||
---
|
||
|
||
## 12) `soil_moisture_heatmap.py`
|
||
|
||
تابع سازنده: `build_soil_moisture_heatmap`
|
||
|
||
### ورودیها
|
||
|
||
- `sensor.soil_moisture` → `base_moisture`
|
||
- `depth.wv0033` برای هر لایه عمق خاک
|
||
|
||
### منطق اولیه
|
||
|
||
```text
|
||
hours = ["۶ ص", "۸ ص", "۱۰ ص", "۱۲ ظ", "۱۴ ع", "۱۶ ع", "۱۸ ع"]
|
||
```
|
||
|
||
اگر `depths` خالی باشد:
|
||
|
||
```text
|
||
depths = [None, None]
|
||
```
|
||
|
||
### فرمول zone offset
|
||
|
||
برای هر depth:
|
||
|
||
```text
|
||
depth_offset = 0 if depth is None else round(depth.wv0033 / 10)
|
||
```
|
||
|
||
### فرمول هر خانه heatmap
|
||
|
||
برای هر zone و هر ساعت:
|
||
|
||
```text
|
||
value = clamp(
|
||
round(base_moisture + depth_offset - abs(3 - hour_index) * 2),
|
||
0,
|
||
100
|
||
)
|
||
```
|
||
|
||
توضیح:
|
||
- `hour_index = 3` مرکز نمودار است و بیشترین مقدار را میدهد.
|
||
- هرچه از مرکز دورتر شویم، به ازای هر پله ۲ واحد کم میشود.
|
||
|
||
ساختار خروجی هر نقطه:
|
||
|
||
```text
|
||
{"x": hour, "y": value}
|
||
```
|
||
|
||
---
|
||
|
||
## 13) `ndvi_health_card.py`
|
||
|
||
تابع سازنده: `build_ndvi_health_card`
|
||
|
||
### ورودیها
|
||
|
||
- `sensor.nitrogen` → `nitrogen`
|
||
- `sensor.soil_moisture` → `moisture`
|
||
|
||
### فرمول NDVI
|
||
|
||
```text
|
||
ndvi = round(
|
||
clamp(((nitrogen / 100) * 0.4) + ((moisture / 100) * 0.6), 0.1, 0.95),
|
||
2
|
||
)
|
||
```
|
||
|
||
توضیح:
|
||
- نیتروژن ۴۰٪ وزن دارد.
|
||
- رطوبت خاک ۶۰٪ وزن دارد.
|
||
- خروجی بین `0.1` و `0.95` محدود میشود.
|
||
|
||
### وضعیتهای متنی
|
||
|
||
#### 1. تنش نیتروژن
|
||
|
||
```text
|
||
"پایین" if nitrogen >= 30 else "بالا"
|
||
```
|
||
|
||
#### 2. سلامت محصول
|
||
|
||
```text
|
||
"خوب" if ndvi >= 0.65 else "متوسط"
|
||
```
|
||
|
||
---
|
||
|
||
## 14) `recommendations_list.py`
|
||
|
||
تابع سازنده: `build_recommendations_list`
|
||
|
||
### منطق
|
||
|
||
این کارت فرمول داخلی ندارد:
|
||
|
||
```text
|
||
recommendations = ai_bundle.get("recommendations", [])
|
||
```
|
||
|
||
---
|
||
|
||
## 15) `economic_overview.py`
|
||
|
||
تابع سازنده: `build_economic_overview`
|
||
|
||
### ورودیها
|
||
|
||
- `forecast.et0` برای ۶ forecast اول
|
||
- `sensor.nitrogen`
|
||
- `sensor.phosphorus`
|
||
- `sensor.potassium`
|
||
|
||
### فرمولها
|
||
|
||
#### 1. هزینه آب
|
||
|
||
```text
|
||
water_cost = round(sum(max(0, forecast.et0 * 20) for first 6 forecasts))
|
||
```
|
||
|
||
در کد با fallback:
|
||
|
||
```text
|
||
water_cost = round(
|
||
sum(max(0, safe_number(forecast.et0, 0) * 20) for forecast in forecasts[:6])
|
||
)
|
||
```
|
||
|
||
#### 2. نیاز کودی
|
||
|
||
```text
|
||
fertilizer_need = round((nitrogen + phosphorus + potassium) / 3)
|
||
```
|
||
|
||
با fallback صفر برای هر کدام.
|
||
|
||
#### 3. پیشبینی درآمد
|
||
|
||
```text
|
||
revenue = round(max(1000, water_cost * 4.5))
|
||
```
|
||
|
||
#### 4. صرفهجویی آب هوشمند
|
||
|
||
```text
|
||
smart_saving = round(water_cost * 0.18)
|
||
```
|
||
|
||
### chartSeries
|
||
|
||
#### سری هزینه آب
|
||
|
||
```text
|
||
water_series = [max(1, round(water_cost / 6)) for _ in range(6)]
|
||
```
|
||
|
||
#### سری کود
|
||
|
||
```text
|
||
fertilizer_series = [max(1, round(fertilizer_need / 6)) for _ in range(6)]
|
||
```
|
||
|
||
نکته:
|
||
- هر دو سری، ۶ مقدار تکراری یکسان تولید میکنند و روند ماهانه واقعی ندارند.
|
||
|
||
---
|
||
|
||
## کارتهای بدون فرمول محاسباتی
|
||
|
||
این کارتها فقط داده را از `ai_bundle` میخوانند:
|
||
|
||
- `farm_alerts_timeline.py`
|
||
- `recommendations_list.py`
|
||
|
||
---
|
||
|
||
## نکات مهم برای تیم
|
||
|
||
- چند اسم کارت با واقعیت محاسبهشان دقیقاً منطبق نیست؛ مثلاً `avg_soil_moisture` واقعاً average چند منبع نیست.
|
||
- بعضی trendها نسبت به history درستِ همان شاخص محاسبه نمیشوند؛ مخصوصاً در `sensor_values_list.py`.
|
||
- چند مقدار hard-coded هستند، مثل:
|
||
- `light_score = 85`
|
||
- `sensor_values_list` برای نور = `850`
|
||
- `yield_prediction_chart` برای برداشت = `+8%`
|
||
- سری ایدهآل در `sensor_radar_chart`
|
||
- چند کارت بهجای مدل تحلیلی واقعی، از فرمولهای heuristic ساده استفاده میکنند.
|
||
|