This commit is contained in:
2026-04-28 19:00:38 +03:30
parent 8471d648a3
commit cb60254c81
8 changed files with 971 additions and 86 deletions
@@ -0,0 +1,363 @@
# Irrigation Recommendation API Fields
این فایل فقط فیلدهای API مربوط به `POST /api/irrigation/recommend/` را توضیح می‌دهد.
## Endpoint
`POST /api/irrigation/recommend/`
## کاربرد
این endpoint برای تولید recommendation آبیاری استفاده می‌شود و خروجی آن با UI فعلی صفحه
`src/views/dashboards/farm/smartIrrigation/SmartIrrigationRecommendation.tsx`
هماهنگ شده است.
## ساختار کلی پاسخ
```json
{
"code": 200,
"msg": "success",
"data": {
"plan": {},
"water_balance": {},
"timeline": [],
"sections": []
}
}
```
## Request
### حداقل payload پیشنهادی
```json
{
"farm_uuid": "11111111-1111-1111-1111-111111111111",
"plant_name": "گوجه‌فرنگی",
"growth_stage": "گلدهی",
"irrigation_type": "آبیاری قطره‌ای"
}
```
### فیلدهای Request
### `farm_uuid`
- نوع: `string`
- اجباری: بله
- توضیح: شناسه یکتای مزرعه.
### `sensor_uuid`
- نوع: `string`
- اجباری: خیر
- توضیح: نام قدیمی برای `farm_uuid`. اگر `farm_uuid` ارسال نشده باشد، این مقدار به جای آن استفاده می‌شود.
### `plant_name`
- نوع: `string`
- اجباری: خیر
- توضیح: نام گیاه هدف برای تولید توصیه. اگر ارسال نشود، سیستم در صورت امکان گیاه ثبت‌شده روی مزرعه را استفاده می‌کند.
### `growth_stage`
- نوع: `string`
- اجباری: خیر
- توضیح: مرحله رشد گیاه مثل `رویشی`، `گلدهی` یا `میوه‌دهی`.
### `irrigation_type`
- نوع: `string`
- اجباری: خیر
- توضیح: نوع یا نام روش آبیاری مورد نظر فرانت. این فیلد برای UI فعلی پیشنهاد می‌شود.
### `irrigation_method_name`
- نوع: `string`
- اجباری: خیر
- توضیح: نام روش آبیاری. این فیلد با `irrigation_type` هم‌ارز است و در بک‌اند به همان ورودی نهایی نرمال می‌شود.
## Response
## فیلدهای لایه اول Response
### `code`
- نوع: `number`
- توضیح: کد وضعیت پاسخ در قالب استاندارد API پروژه.
### `msg`
- نوع: `string`
- توضیح: پیام وضعیت پاسخ. در حالت موفق معمولاً `success` است.
### `data`
- نوع: `object`
- توضیح: بدنه اصلی recommendation آبیاری.
## فیلدهای `data`
### `plan`
- نوع: `object`
- توضیح: خلاصه اصلی recommendation برای نمایش در کارت بالای UI.
### `water_balance`
- نوع: `object`
- توضیح: تراز آب و خروجی محاسبات روزانه FAO-56.
### `timeline`
- نوع: `array`
- توضیح: مراحل اجرایی recommendation برای Stepper.
### `sections`
- نوع: `array`
- توضیح: نکات تکمیلی و هشدارها. در UI فعلی فقط `warning` و `tip` مصرف می‌شوند.
## فیلدهای `data.plan`
```json
{
"frequencyPerWeek": 4,
"durationMinutes": 38,
"bestTimeOfDay": "05:30 تا 08:00 صبح",
"moistureLevel": 72,
"warning": "در ساعات گرم روز آبیاری انجام نشود"
}
```
### `frequencyPerWeek`
- نوع: `number`
- توضیح: تعداد نوبت آبیاری در هفته.
### `durationMinutes`
- نوع: `number`
- توضیح: مدت هر نوبت آبیاری بر حسب دقیقه.
### `bestTimeOfDay`
- نوع: `string`
- توضیح: بهترین بازه زمانی اجرای آبیاری.
### `moistureLevel`
- نوع: `number`
- توضیح: سطح رطوبت فعلی یا هدف خاک برای نمایش در gauge.
### `warning`
- نوع: `string`
- توضیح: هشدار اصلی recommendation.
## فیلدهای `data.water_balance`
```json
{
"active_kc": 0.93,
"crop_profile": {
"kc_initial": 0.55,
"kc_mid": 1.05,
"kc_end": 0.78
},
"daily": [
{
"forecast_date": "2025-02-12",
"et0_mm": 5.4,
"etc_mm": 4.9,
"effective_rainfall_mm": 0,
"gross_irrigation_mm": 17,
"irrigation_timing": "05:30 - 07:00"
}
]
}
```
### `active_kc`
- نوع: `number`
- توضیح: ضریب Kc فعال برای مرحله رشد فعلی.
### `crop_profile`
- نوع: `object`
- توضیح: پروفایل Kc گیاه در مراحل مختلف.
### `daily`
- نوع: `array`
- توضیح: داده‌های روزانه مورد استفاده در جدول یا نمودار تراز آب.
## فیلدهای `data.water_balance.crop_profile`
### `kc_initial`
- نوع: `number`
- توضیح: Kc مرحله ابتدایی رشد.
### `kc_mid`
- نوع: `number`
- توضیح: Kc مرحله میانی رشد.
### `kc_end`
- نوع: `number`
- توضیح: Kc مرحله پایانی رشد.
## فیلدهای هر آیتم در `data.water_balance.daily[]`
### `forecast_date`
- نوع: `string`
- توضیح: تاریخ پیش‌بینی.
### `et0_mm`
- نوع: `number`
- توضیح: تبخیر و تعرق مرجع روزانه.
### `etc_mm`
- نوع: `number`
- توضیح: تبخیر و تعرق گیاه.
### `effective_rainfall_mm`
- نوع: `number`
- توضیح: بارش مؤثر محاسبه‌شده.
### `gross_irrigation_mm`
- نوع: `number`
- توضیح: مقدار آبیاری ناخالص پیشنهادی برای آن روز.
### `irrigation_timing`
- نوع: `string`
- توضیح: زمان پیشنهادی اجرای آبیاری برای آن روز.
## فیلدهای `data.timeline`
```json
[
{
"step_number": 1,
"title": "بررسی فشار",
"description": "فشار ابتدا و انتهای لاین کنترل شود"
}
]
```
### `step_number`
- نوع: `number`
- توضیح: شماره مرحله.
### `title`
- نوع: `string`
- توضیح: عنوان مرحله.
### `description`
- نوع: `string`
- توضیح: توضیح اجرایی مرحله.
## فیلدهای `data.sections`
```json
[
{
"title": "هشدار تبخیر بالا",
"icon": "tabler-alert-triangle",
"type": "warning",
"content": "در ساعات گرم روز آبیاری انجام نشود"
},
{
"title": "نکته بهره وری",
"icon": "tabler-bulb",
"type": "tip",
"content": "شست وشوی فیلترها به یکنواختی آبیاری کمک می کند"
}
]
```
### `title`
- نوع: `string`
- توضیح: عنوان کارت.
### `icon`
- نوع: `string`
- توضیح: نام آیکون مورد استفاده در UI.
### `type`
- نوع: `string`
- توضیح: نوع سکشن. در UI فعلی فقط این مقادیر مصرف می‌شوند:
- `warning`
- `tip`
### `content`
- نوع: `string`
- توضیح: متن هشدار یا نکته.
## حداقل پاسخ قابل استفاده برای UI فعلی
```json
{
"code": 200,
"msg": "success",
"data": {
"plan": {
"frequencyPerWeek": 4,
"durationMinutes": 38,
"bestTimeOfDay": "05:30 تا 08:00 صبح",
"moistureLevel": 72,
"warning": "در ساعات گرم روز آبیاری انجام نشود"
},
"water_balance": {
"active_kc": 0.93,
"crop_profile": {
"kc_initial": 0.55,
"kc_mid": 1.05,
"kc_end": 0.78
},
"daily": [
{
"forecast_date": "2025-02-12",
"et0_mm": 5.4,
"etc_mm": 4.9,
"effective_rainfall_mm": 0,
"gross_irrigation_mm": 17,
"irrigation_timing": "05:30 - 07:00"
}
]
},
"timeline": [
{
"step_number": 1,
"title": "بررسی فشار",
"description": "فشار ابتدا و انتهای لاین کنترل شود"
}
],
"sections": [
{
"title": "هشدار تبخیر بالا",
"icon": "tabler-alert-triangle",
"type": "warning",
"content": "در ساعات گرم روز آبیاری انجام نشود"
},
{
"title": "نکته بهره وری",
"icon": "tabler-bulb",
"type": "tip",
"content": "شست وشوی فیلترها به یکنواختی آبیاری کمک می کند"
}
]
}
}
```
## فیلدهایی که فرانت فعلی لازم ندارد
فیلدهای زیر برای UI فعلی recommendation لازم نیستند و نباید به عنوان dependency فرانت در نظر گرفته شوند:
- `raw_response`
- `status`
- `generated_at`
- `recommendation_title`
- `recommendation_subtitle`
- `final_verdict`
- `primary_method`
- `usage_summary`
- `alternative_plans`
- `sections[].type = schedule`
- `sections[].type = method`
## نمونه cURL
```bash
curl -s -X POST "http://localhost:8000/api/irrigation/recommend/" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"farm_uuid": "11111111-1111-1111-1111-111111111111",
"plant_name": "گوجه‌فرنگی",
"growth_stage": "گلدهی",
"irrigation_type": "آبیاری قطره‌ای"
}'
```
@@ -0,0 +1,64 @@
from __future__ import annotations
from unittest.mock import patch
from django.test import TestCase, override_settings
from rest_framework.test import APIClient
@override_settings(ROOT_URLCONF="irrigation.urls")
class IrrigationRecommendApiTests(TestCase):
def setUp(self):
self.client = APIClient()
@patch("rag.services.irrigation.get_irrigation_recommendation")
def test_recommend_api_returns_water_balance(self, mock_get_irrigation_recommendation):
mock_get_irrigation_recommendation.return_value = {
"plan": {
"frequencyPerWeek": 4,
"durationMinutes": 38,
"bestTimeOfDay": "05:30 تا 08:00 صبح",
"moistureLevel": 72,
"warning": "در ساعات گرم روز آبیاری انجام نشود",
},
"water_balance": {
"active_kc": 0.93,
"crop_profile": {
"kc_initial": 0.55,
"kc_mid": 1.05,
"kc_end": 0.78,
},
"daily": [
{
"forecast_date": "2025-02-12",
"et0_mm": 5.4,
"etc_mm": 4.9,
"effective_rainfall_mm": 0,
"gross_irrigation_mm": 17,
"irrigation_timing": "05:30 - 07:00",
}
],
},
"timeline": [],
"sections": [],
"simulation_optimizer": {"engine": "crop_simulation_heuristic"},
"selected_irrigation_method": {"name": "آبیاری قطره‌ای"},
}
response = self.client.post(
"/recommend/",
data={
"farm_uuid": "11111111-1111-1111-1111-111111111111",
"plant_name": "گوجه‌فرنگی",
"growth_stage": "گلدهی",
"irrigation_method_name": "آبیاری قطره‌ای",
},
format="json",
)
self.assertEqual(response.status_code, 200)
data = response.json()["data"]
self.assertIn("water_balance", data)
self.assertEqual(data["water_balance"]["active_kc"], 0.93)
self.assertNotIn("simulation_optimizer", data)
self.assertNotIn("selected_irrigation_method", data)
-1
View File
@@ -44,7 +44,6 @@ WaterStressEnvelopeSerializer = build_envelope_serializer(
IRRIGATION_RECOMMENDATION_INTERNAL_KEYS = {
"raw_response",
"water_balance",
"simulation_optimizer",
"selected_irrigation_method",
}