UPDATE
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
||||
# راهنمای استفاده از API هشدارهای مزرعه
|
||||
|
||||
این سند نحوه کار با API فعال هشدارهای مزرعه را توضیح میدهد.
|
||||
|
||||
## Endpoint فعال
|
||||
|
||||
- `POST /api/farm-alerts/tracker/`
|
||||
|
||||
نکته:
|
||||
- endpoint `POST /api/farm-alerts/timeline/` حذف شده و دیگر قابل استفاده نیست.
|
||||
|
||||
## کاربرد API
|
||||
|
||||
این API با دریافت `farm_uuid` و یک لیست از `alerts`:
|
||||
|
||||
- وضعیت فعلی هشدارهای مزرعه را تحلیل میکند
|
||||
- context مزرعه را همراه با alertهای ارسالی به RAG میفرستد
|
||||
- فقط notificationهای مهم را تولید میکند
|
||||
- notificationهای تولیدشده را در دیتابیس ذخیره میکند
|
||||
|
||||
## ساختار درخواست
|
||||
|
||||
فیلدهای ورودی:
|
||||
|
||||
- `farm_uuid`: شناسه مزرعه
|
||||
- `alerts`: لیست alertهای ورودی برای تحلیل
|
||||
|
||||
فیلد `farm_uuid` الزامی است.
|
||||
|
||||
## ساختار هر alert
|
||||
|
||||
هر آیتم داخل `alerts` میتواند این فیلدها را داشته باشد:
|
||||
|
||||
- `alert_id`: شناسه هشدار
|
||||
- `level`: سطح هشدار مثل `info` یا `warning` یا `danger`
|
||||
- `title`: عنوان هشدار
|
||||
- `message`: توضیح هشدار
|
||||
- `suggested_action`: اقدام پیشنهادی
|
||||
- `source_metric_type`: نوع شاخص مثل `moisture`
|
||||
- `timestamp`: زمان هشدار با فرمت datetime
|
||||
- `payload`: داده تکمیلی به صورت JSON object
|
||||
|
||||
همه فیلدهای داخل هر alert اختیاری هستند، ولی بهتر است برای تحلیل دقیقتر حداقل `title` یا `message` و در صورت امکان `level` و `source_metric_type` ارسال شوند.
|
||||
|
||||
## نمونه درخواست
|
||||
|
||||
```json
|
||||
{
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"alerts": [
|
||||
{
|
||||
"alert_id": "soil-moisture-001",
|
||||
"level": "warning",
|
||||
"title": "افت رطوبت خاک",
|
||||
"message": "رطوبت خاک کمتر از حد مطلوب گزارش شده است.",
|
||||
"suggested_action": "آبیاری اصلاحی بررسی شود.",
|
||||
"source_metric_type": "moisture",
|
||||
"timestamp": "2025-02-14T09:30:00Z",
|
||||
"payload": {
|
||||
"window": "3d",
|
||||
"current_value": 38.5,
|
||||
"threshold": 45
|
||||
}
|
||||
},
|
||||
{
|
||||
"alert_id": "fungal-risk-002",
|
||||
"level": "danger",
|
||||
"title": "ریسک قارچی بالا",
|
||||
"message": "شرایط محیطی برای بیماری قارچی شدید شده است.",
|
||||
"suggested_action": "بازدید و اقدام پیشگیرانه فوری انجام شود.",
|
||||
"source_metric_type": "fungal_risk",
|
||||
"timestamp": "2025-02-14T10:00:00Z",
|
||||
"payload": {
|
||||
"humidity": 89,
|
||||
"duration_hours": 18
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## نمونه درخواست با curl
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/farm-alerts/tracker/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"alerts": [
|
||||
{
|
||||
"alert_id": "soil-moisture-001",
|
||||
"level": "warning",
|
||||
"title": "افت رطوبت خاک",
|
||||
"message": "رطوبت خاک کمتر از حد مطلوب گزارش شده است.",
|
||||
"suggested_action": "آبیاری اصلاحی بررسی شود.",
|
||||
"source_metric_type": "moisture"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## ساختار پاسخ موفق
|
||||
|
||||
پاسخ HTTP با envelope زیر برمیگردد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"service_id": "farm_alerts",
|
||||
"tracker": {},
|
||||
"headline": "جمع بندی کوتاه وضعیت هشدارها",
|
||||
"overview": "توضیح کوتاه و اجرایی از مهم ترین وضعیت مزرعه",
|
||||
"status_level": "warning",
|
||||
"notifications": [
|
||||
{
|
||||
"id": 12,
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"endpoint": "tracker",
|
||||
"level": "warning",
|
||||
"title": "افت رطوبت خاک",
|
||||
"message": "تنش رطوبتی در مزرعه ادامه دارد.",
|
||||
"suggested_action": "آبیاری جبرانی کوتاه مدت اجرا شود.",
|
||||
"source_alert_id": "soil-moisture-001",
|
||||
"source_metric_type": "moisture",
|
||||
"payload": {},
|
||||
"created_at": "2025-02-14T10:15:00+00:00",
|
||||
"updated_at": "2025-02-14T10:15:00+00:00"
|
||||
}
|
||||
],
|
||||
"raw_llm_response": "{\"headline\":\"...\"}",
|
||||
"structured_context": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## وضعیتهای خطا
|
||||
|
||||
### خطای ورودی نامعتبر
|
||||
|
||||
اگر `farm_uuid` ارسال نشود:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"msg": "داده نامعتبر.",
|
||||
"data": {
|
||||
"farm_uuid": [
|
||||
"farm_uuid الزامی است."
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### خطای داخلی
|
||||
|
||||
اگر در مرحله تحلیل RAG یا تولید پاسخ خطایی رخ دهد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "خطا در تولید tracker هشدارها: ...",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## نکات رفتاری API
|
||||
|
||||
- اگر `alerts` ارسال نشود، API آن را به صورت آرایه خالی در نظر میگیرد.
|
||||
- notificationهای ساختهشده برای endpoint `tracker` در دیتابیس ذخیره میشوند.
|
||||
- مدل باید notification تکراری نسازد و اگر مورد مهمی وجود نداشته باشد، خروجی notification میتواند خالی باشد.
|
||||
- تحلیل فقط روی endpoint `tracker` فعال است.
|
||||
|
||||
## پیشنهاد برای مصرفکننده API
|
||||
|
||||
- برای هر alert یک `alert_id` پایدار بفرستید تا ردیابی و جلوگیری از تکرار بهتر انجام شود.
|
||||
- برای alertهای حساس، `timestamp` و `source_metric_type` را حتما ارسال کنید.
|
||||
- اگر داده تکمیلی دارید، آن را داخل `payload` بفرستید تا RAG context کاملتر شود.
|
||||
@@ -0,0 +1,492 @@
|
||||
# توضیح فیلدهای پاسخ API هشدارهای مزرعه
|
||||
|
||||
این سند فیلدهای JSON خروجی `POST /api/farm-alerts/tracker/` را توضیح میدهد.
|
||||
|
||||
## ساختار کلی پاسخ
|
||||
|
||||
پاسخ API به شکل envelope برمیگردد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
## فیلدهای سطح اول
|
||||
|
||||
### `code`
|
||||
|
||||
- نوع: `number`
|
||||
- معنی: کد وضعیت داخلی API
|
||||
- مقدار رایج:
|
||||
- `200`: موفق
|
||||
- `400`: ورودی نامعتبر
|
||||
- `500`: خطای داخلی
|
||||
|
||||
### `msg`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: پیام خلاصه وضعیت پاسخ
|
||||
- نمونه:
|
||||
- `success`
|
||||
- `داده نامعتبر.`
|
||||
- `خطا در تولید tracker هشدارها: ...`
|
||||
|
||||
### `data`
|
||||
|
||||
- نوع: `object | null`
|
||||
- معنی: بدنه اصلی پاسخ
|
||||
- در موفقیت شامل جزئیات تحلیل مزرعه است
|
||||
- در بعضی خطاها ممکن است `null` باشد
|
||||
|
||||
## فیلدهای داخل `data`
|
||||
|
||||
### `farm_uuid`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: شناسه مزرعهای که تحلیل روی آن انجام شده
|
||||
|
||||
### `service_id`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: شناسه سرویس تولیدکننده پاسخ
|
||||
- مقدار فعلی: `farm_alerts`
|
||||
|
||||
### `tracker`
|
||||
|
||||
- نوع: `object`
|
||||
- معنی: خروجی ساختاریافتهی موتور tracker قبل از خلاصهسازی نهایی AI
|
||||
- شامل لیست alertها، آمار، خوشهبندی و مهمترین مسئله است
|
||||
|
||||
### `headline`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: تیتر کوتاه و سریع برای وضعیت فعلی مزرعه
|
||||
- برای نمایش در کارت، header یا لیست اعلان مناسب است
|
||||
|
||||
### `overview`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: جمعبندی کوتاه و اجرایی از مهمترین وضعیت فعلی
|
||||
- معمولا نسخهی خلاصهشدهی مهمترین alert یا نتیجه کلی تحلیل است
|
||||
|
||||
### `status_level`
|
||||
|
||||
- نوع: `string`
|
||||
- مقادیر مجاز:
|
||||
- `info`
|
||||
- `warning`
|
||||
- `danger`
|
||||
- معنی: سطح کلی وضعیت مزرعه از دید AI
|
||||
|
||||
### `notifications`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: notificationهای نهایی که پس از تحلیل AI ساخته و در دیتابیس ذخیره شدهاند
|
||||
- اگر مورد مهمی وجود نداشته باشد، میتواند خالی باشد
|
||||
|
||||
### `raw_llm_response`
|
||||
|
||||
- نوع: `string | null`
|
||||
- معنی: پاسخ خام JSON که مدل زبانی تولید کرده است
|
||||
- بیشتر برای debug، audit یا بررسی رفتار AI مفید است
|
||||
|
||||
### `structured_context`
|
||||
|
||||
- نوع: `object`
|
||||
- معنی: کانتکست ساختاریافتهای که به مدل داده شده است
|
||||
- شامل اطلاعات مزرعه، tracker، forecastها و alertهای ورودی است
|
||||
|
||||
## فیلدهای `tracker`
|
||||
|
||||
### `tracker.totalAlerts`
|
||||
|
||||
- نوع: `number`
|
||||
- معنی: تعداد کل alertهای شناساییشده توسط tracker
|
||||
|
||||
### `tracker.alerts`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: لیست خام alertهای تشخیصدادهشده
|
||||
|
||||
هر آیتم در `tracker.alerts`:
|
||||
|
||||
#### `metric_type`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: نوع شاخصی که alert از آن آمده
|
||||
- نمونه:
|
||||
- `moisture`
|
||||
- `temperature`
|
||||
- `ph`
|
||||
- `ec`
|
||||
- `fungal_risk`
|
||||
|
||||
#### `title`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: عنوان انسانی alert
|
||||
|
||||
#### `current_value`
|
||||
|
||||
- نوع: `number`
|
||||
- معنی: مقدار فعلی شاخص
|
||||
|
||||
#### `threshold_value`
|
||||
|
||||
- نوع: `number | string`
|
||||
- معنی: آستانه مرجع برای تشخیص alert
|
||||
|
||||
#### `severity`
|
||||
|
||||
- نوع: `string`
|
||||
- مقادیر رایج:
|
||||
- `low`
|
||||
- `medium`
|
||||
- `high`
|
||||
- `critical`
|
||||
- معنی: شدت alert در لایه tracker
|
||||
|
||||
#### `duration_hours`
|
||||
|
||||
- نوع: `number`
|
||||
- معنی: مدت تداوم وضعیت هشدار به ساعت
|
||||
|
||||
#### `duration`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: نسخه خوانای `duration_hours`
|
||||
- نمونه: `3 ساعت`
|
||||
|
||||
#### `timestamp`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: زمان مرجع alert با فرمت ISO datetime
|
||||
|
||||
#### `sensor_id`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: شناسه مزرعه/سنسوری که alert برای آن محاسبه شده
|
||||
|
||||
#### `zone_id`
|
||||
|
||||
- نوع: `string | null`
|
||||
- معنی: شناسه ناحیه، اگر alert مربوط به ناحیه خاصی باشد
|
||||
|
||||
#### `domain`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: دامنه عملیاتی alert
|
||||
- نمونه:
|
||||
- `water_balance`
|
||||
- `temperature_stress`
|
||||
- `root_chemistry`
|
||||
- `disease_pressure`
|
||||
|
||||
#### `direction`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: جهت عبور از آستانه
|
||||
- نمونه:
|
||||
- `below`: مقدار از حد مجاز کمتر شده
|
||||
- `above`: مقدار از حد مجاز بیشتر شده
|
||||
|
||||
#### `unit`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: واحد شاخص
|
||||
- نمونه:
|
||||
- `%`
|
||||
- `°C`
|
||||
- `pH`
|
||||
- `dS/m`
|
||||
|
||||
#### `icon`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: نام آیکن پیشنهادی برای UI
|
||||
|
||||
#### `summary`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: خلاصه انسانی و کوتاه alert
|
||||
|
||||
#### `recommended_action`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: اقدام عملیاتی پیشنهادی tracker
|
||||
|
||||
#### `explanation`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: توضیح قابلفهم درباره چرایی ایجاد alert
|
||||
|
||||
#### `metadata`
|
||||
|
||||
- نوع: `object`
|
||||
- معنی: داده تکمیلی برای توسعههای بعدی
|
||||
|
||||
## فیلدهای `tracker.alertStats`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: خلاصه آماری alertها برای نمایش سریع در UI
|
||||
|
||||
هر آیتم:
|
||||
|
||||
### `title`
|
||||
|
||||
- عنوان دسته alert
|
||||
|
||||
### `count`
|
||||
|
||||
- نوع: `string`
|
||||
- تعداد alertها در آن دسته
|
||||
|
||||
### `avatarColor`
|
||||
|
||||
- نوع: `string`
|
||||
- رنگ پیشنهادی UI
|
||||
|
||||
### `avatarIcon`
|
||||
|
||||
- نوع: `string`
|
||||
- آیکن پیشنهادی UI
|
||||
|
||||
### `severity`
|
||||
|
||||
- نوع: `string`
|
||||
- بالاترین شدت در آن دسته
|
||||
|
||||
### `topSummary`
|
||||
|
||||
- نوع: `string`
|
||||
- مهمترین خلاصه در آن دسته
|
||||
|
||||
## فیلدهای `tracker.alertClusters`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: گروهبندی alertها بر اساس domain
|
||||
|
||||
هر آیتم:
|
||||
|
||||
### `domain`
|
||||
|
||||
- نوع: `string`
|
||||
- نام دامنه خوشه
|
||||
|
||||
### `title`
|
||||
|
||||
- نوع: `string`
|
||||
- عنوان انسانی خوشه
|
||||
|
||||
### `alert_count`
|
||||
|
||||
- نوع: `number`
|
||||
- تعداد alertهای این خوشه
|
||||
|
||||
### `highest_severity`
|
||||
|
||||
- نوع: `string`
|
||||
- بیشترین شدت بین alertهای این خوشه
|
||||
|
||||
### `primary_metric`
|
||||
|
||||
- نوع: `string`
|
||||
- مهمترین metric در آن خوشه
|
||||
|
||||
### `summary`
|
||||
|
||||
- نوع: `string`
|
||||
- خلاصه وضعیت خوشه
|
||||
|
||||
### `alert_ids`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: شناسههای alertهای عضو خوشه
|
||||
|
||||
## فیلد `tracker.mostCriticalIssue`
|
||||
|
||||
- نوع: `object | null`
|
||||
- معنی: مهمترین مسئلهای که tracker پیدا کرده
|
||||
- ساختار آن مشابه هر آیتم در `tracker.alerts` است
|
||||
|
||||
## فیلدهای خلاصهای `tracker`
|
||||
|
||||
### `tracker.prioritizedAlertSummaries`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: لیستی از summaryهای مهم به ترتیب اولویت
|
||||
|
||||
### `tracker.recommendedOperationalActions`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: لیستی از اقدامهای عملیاتی پیشنهادی
|
||||
|
||||
### `tracker.humanReadableExplanations`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: توضیحهای متنی قابلخواندن برای نمایش یا گزارش
|
||||
|
||||
## فیلدهای `notifications`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: اعلانهای نهایی ذخیرهشده در سیستم
|
||||
|
||||
هر آیتم در `notifications`:
|
||||
|
||||
### `id`
|
||||
|
||||
- نوع: `number`
|
||||
- معنی: شناسه داخلی notification
|
||||
|
||||
### `uuid`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: شناسه یکتای notification
|
||||
|
||||
### `farm_uuid`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: شناسه مزرعه مربوط به notification
|
||||
|
||||
### `since_id`
|
||||
|
||||
- نوع: `number | null`
|
||||
- معنی: شناسه مرجع برای زنجیره یا گروهبندی notificationها در سیستم
|
||||
|
||||
### `endpoint`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: endpoint تولیدکننده notification
|
||||
- مقدار فعلی: `tracker`
|
||||
|
||||
### `title`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: عنوان notification
|
||||
|
||||
### `message`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: متن اصلی notification
|
||||
|
||||
### `level`
|
||||
|
||||
- نوع: `string`
|
||||
- مقادیر مجاز:
|
||||
- `info`
|
||||
- `warning`
|
||||
- `danger`
|
||||
- معنی: سطح notification
|
||||
|
||||
### `suggested_action`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: اقدام پیشنهادی نهایی برای کاربر
|
||||
|
||||
### `source_alert_id`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: شناسه alert مبنا که notification از آن ساخته شده
|
||||
|
||||
### `source_metric_type`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: نوع metric مبنا
|
||||
|
||||
### `payload`
|
||||
|
||||
- نوع: `object`
|
||||
- معنی: داده تکمیلی notification
|
||||
|
||||
### `is_read`
|
||||
|
||||
- نوع: `boolean`
|
||||
- معنی: آیا notification توسط کاربر خوانده شده یا نه
|
||||
|
||||
### `metadata`
|
||||
|
||||
- نوع: `object`
|
||||
- معنی: اطلاعات جانبی درباره منبع یا نحوه تولید notification
|
||||
- نمونه:
|
||||
- `source: farm_alerts_tracker_ai`
|
||||
|
||||
### `created_at`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: زمان ایجاد notification
|
||||
|
||||
### `updated_at`
|
||||
|
||||
- نوع: `string`
|
||||
- معنی: زمان آخرین بهروزرسانی notification
|
||||
|
||||
## فیلدهای `structured_context`
|
||||
|
||||
این بخش برای debug و audit مفید است و نشان میدهد چه دادهای به مدل داده شده است.
|
||||
|
||||
### `structured_context.farm_profile`
|
||||
|
||||
- اطلاعات پایه مزرعه
|
||||
|
||||
فیلدهای مهم:
|
||||
- `farm_uuid`: شناسه مزرعه
|
||||
- `location.latitude`: عرض جغرافیایی
|
||||
- `location.longitude`: طول جغرافیایی
|
||||
- `plant_names`: لیست گیاههای ثبتشده
|
||||
- `irrigation_method`: روش آبیاری یا `null`
|
||||
- `last_sensor_update`: زمان آخرین داده سنسور
|
||||
|
||||
### `structured_context.tracker`
|
||||
|
||||
- همان داده tracker که به AI داده شده است
|
||||
|
||||
### `structured_context.forecasts`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: پیشبینیهای هواشناسی کوتاهمدت
|
||||
|
||||
هر آیتم:
|
||||
- `date`: تاریخ پیشبینی
|
||||
- `temperature_min`: کمینه دما
|
||||
- `temperature_max`: بیشینه دما
|
||||
- `humidity_mean`: میانگین رطوبت
|
||||
- `precipitation`: بارش
|
||||
- `et0`: تبخیر-تعرق مرجع
|
||||
|
||||
### `structured_context.incoming_alerts`
|
||||
|
||||
- نوع: `array`
|
||||
- معنی: alertهایی که از request به API ارسال شده و در تحلیل استفاده شدهاند
|
||||
- اگر چیزی ارسال نشده باشد، آرایه خالی است
|
||||
|
||||
## تفاوت `severity` و `level`
|
||||
|
||||
این دو فیلد شبیه هم هستند ولی یکسان نیستند:
|
||||
|
||||
- `severity`: شدت داخلی alert در tracker با مقادیر `low/medium/high/critical`
|
||||
- `level`: سطح نهایی notification یا status کلی با مقادیر `info/warning/danger`
|
||||
|
||||
به طور معمول:
|
||||
|
||||
- `low` معمولا به `info` نزدیک است
|
||||
- `medium` معمولا به `warning` نزدیک است
|
||||
- `high` و `critical` معمولا به `danger` نزدیک هستند
|
||||
|
||||
## نکته عملی
|
||||
|
||||
اگر میخواهید در frontend فقط پیام نهایی را نمایش دهید، معمولا این فیلدها کافی هستند:
|
||||
|
||||
- `data.headline`
|
||||
- `data.overview`
|
||||
- `data.status_level`
|
||||
- `data.notifications`
|
||||
|
||||
اگر میخواهید صفحه تحلیلی یا داشبورد کامل بسازید، از این بخشها هم استفاده کنید:
|
||||
|
||||
- `data.tracker.alerts`
|
||||
- `data.tracker.alertStats`
|
||||
- `data.tracker.alertClusters`
|
||||
- `data.structured_context.forecasts`
|
||||
@@ -0,0 +1,619 @@
|
||||
# Free-Text Plan Parser APIs
|
||||
|
||||
این فایل برای تیم فرانتاند آماده شده و دو API جدید زیر را توضیح میدهد:
|
||||
|
||||
- `POST /api/irrigation/plan-from-text/`
|
||||
- `POST /api/fertilization/plan-from-text/`
|
||||
|
||||
هدف هر دو API:
|
||||
|
||||
- کاربر یک متن آزاد مینویسد
|
||||
- بکاند تلاش میکند برنامه آبیاری یا کودهی را به JSON ساختاریافته تبدیل کند
|
||||
- اگر اطلاعات کامل باشد، JSON نهایی برمیگردد
|
||||
- اگر اطلاعات ناقص باشد، API سوالهای تکمیلی برمیگرداند
|
||||
- فرانتاند سوالها را از کاربر میپرسد و پاسخها را دوباره برای API میفرستد
|
||||
|
||||
---
|
||||
|
||||
## رفتار کلی هر دو API
|
||||
|
||||
هر دو endpoint یک flow یکسان دارند:
|
||||
|
||||
1. کاربر متن آزاد اولیه را میفرستد
|
||||
2. اگر متن کامل باشد:
|
||||
- `status = "completed"`
|
||||
- `final_plan` برمیگردد
|
||||
3. اگر متن ناقص باشد:
|
||||
- `status = "needs_clarification"`
|
||||
- `missing_fields` برمیگردد
|
||||
- `questions` برمیگردد
|
||||
4. فرانتاند پاسخ کاربر به سوالها را جمع میکند
|
||||
5. دوباره همان endpoint را با `answers` و `partial_plan` صدا میزند
|
||||
6. این روند تا ساخته شدن `final_plan` ادامه پیدا میکند
|
||||
|
||||
---
|
||||
|
||||
## الگوی کلی response
|
||||
|
||||
هر دو API از envelope استاندارد استفاده میکنند:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
### معنی فیلدهای envelope
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `code` | number | کد منطقی پاسخ |
|
||||
| `msg` | string | پیام کوتاه پاسخ |
|
||||
| `data` | object | داده اصلی API |
|
||||
|
||||
---
|
||||
|
||||
## 1) API استخراج برنامه آبیاری
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
POST /api/irrigation/plan-from-text/
|
||||
```
|
||||
|
||||
### کاربرد
|
||||
|
||||
این API متن آزاد کاربر درباره برنامه آبیاری را به JSON ساختاریافته تبدیل میکند.
|
||||
|
||||
### Request Body
|
||||
|
||||
هر سه فیلد زیر اختیاری هستند، اما حداقل یکی از اینها باید ارسال شود:
|
||||
|
||||
- `message`
|
||||
- `answers`
|
||||
- `partial_plan`
|
||||
|
||||
#### ساختار request
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "برای گوجه فرنگی با آبیاری قطره ای هر سه روز یک بار صبح زود 25 دقیقه آبیاری می کنم و حدود 18 لیتر برای هر بوته می دهم.",
|
||||
"answers": {
|
||||
"growth_stage": "گلدهی"
|
||||
},
|
||||
"partial_plan": {
|
||||
"crop_name": "گوجه فرنگی",
|
||||
"irrigation_method": "قطره ای"
|
||||
},
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111"
|
||||
}
|
||||
```
|
||||
|
||||
### فیلدهای request
|
||||
|
||||
| فیلد | نوع | اجباری | توضیح |
|
||||
|---|---|---:|---|
|
||||
| `message` | string | خیر | متن آزاد کاربر |
|
||||
| `answers` | object | خیر | پاسخهای تکمیلی کاربر به سوالهایی که قبلا API داده |
|
||||
| `partial_plan` | object | خیر | خروجی مرحله قبل برای ادامه تکمیل |
|
||||
| `farm_uuid` | string | خیر | برای غنیسازی context مزرعه در RAG |
|
||||
|
||||
### قانون validation
|
||||
|
||||
اگر هیچکدام از `message`، `answers` یا `partial_plan` ارسال نشوند:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"msg": "داده نامعتبر.",
|
||||
"data": {
|
||||
"non_field_errors": [
|
||||
"حداقل یکی از message، answers یا partial_plan باید ارسال شود."
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## پاسخ موفق - حالت تکمیل شده
|
||||
|
||||
وقتی همه اطلاعات لازم موجود باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"status": "completed",
|
||||
"status_fa": "تکمیل شد",
|
||||
"summary": "برنامه آبیاری برای گوجهفرنگی به روش قطرهای هر سه روز یکبار صبح زود به مدت 25 دقیقه اجرا میشود.",
|
||||
"missing_fields": [],
|
||||
"questions": [],
|
||||
"collected_data": {
|
||||
"crop_name": "گوجهفرنگی",
|
||||
"growth_stage": "گلدهی",
|
||||
"irrigation_method": "قطرهای",
|
||||
"water_amount_per_event": "18 لیتر برای هر بوته",
|
||||
"duration_minutes": 25,
|
||||
"frequency_text": "هر سه روز یکبار",
|
||||
"interval_days": 3,
|
||||
"preferred_time_of_day": "صبح زود",
|
||||
"start_date": "از امروز",
|
||||
"target_area": "کل مزرعه",
|
||||
"trigger_conditions": [],
|
||||
"notes": []
|
||||
},
|
||||
"final_plan": {
|
||||
"crop_name": "گوجهفرنگی",
|
||||
"growth_stage": "گلدهی",
|
||||
"irrigation_method": "قطرهای",
|
||||
"water_amount_per_event": "18 لیتر برای هر بوته",
|
||||
"duration_minutes": 25,
|
||||
"frequency_text": "هر سه روز یکبار",
|
||||
"interval_days": 3,
|
||||
"preferred_time_of_day": "صبح زود",
|
||||
"start_date": "از امروز",
|
||||
"target_area": "کل مزرعه",
|
||||
"trigger_conditions": [],
|
||||
"notes": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### فیلدهای `data`
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `status` | string | یکی از `completed` یا `needs_clarification` |
|
||||
| `status_fa` | string | نسخه فارسی وضعیت |
|
||||
| `summary` | string | خلاصه قابل نمایش برای کاربر |
|
||||
| `missing_fields` | array[string] | فیلدهای ناقص |
|
||||
| `questions` | array[object] | سوالهای تکمیلی |
|
||||
| `collected_data` | object | دادهای که تا الان از متن و جوابها استخراج شده |
|
||||
| `final_plan` | object/null | برنامه نهایی؛ فقط در حالت `completed` |
|
||||
|
||||
### فیلدهای `collected_data` و `final_plan`
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `crop_name` | string | نام محصول |
|
||||
| `growth_stage` | string | مرحله رشد محصول |
|
||||
| `irrigation_method` | string | روش آبیاری |
|
||||
| `water_amount_per_event` | string | مقدار آب هر نوبت |
|
||||
| `duration_minutes` | number | مدت هر نوبت آبیاری به دقیقه |
|
||||
| `frequency_text` | string | توصیف متنی فاصله آبیاری |
|
||||
| `interval_days` | number | فاصله آبیاری بر حسب روز |
|
||||
| `preferred_time_of_day` | string | زمان مناسب اجرای آبیاری |
|
||||
| `start_date` | string | زمان یا تاریخ شروع برنامه |
|
||||
| `target_area` | string | محدوده هدف برنامه |
|
||||
| `trigger_conditions` | array[string] | شرایط تریگر اختیاری |
|
||||
| `notes` | array[string] | نکات تکمیلی |
|
||||
|
||||
---
|
||||
|
||||
## پاسخ موفق - حالت نیاز به سوال تکمیلی
|
||||
|
||||
اگر اطلاعات کامل نباشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"status": "needs_clarification",
|
||||
"status_fa": "نیازمند پرسش تکمیلی",
|
||||
"summary": "اطلاعات برنامه آبیاری برای ساخت JSON نهایی کافی نیست و به چند پاسخ تکمیلی نیاز است.",
|
||||
"missing_fields": [
|
||||
"growth_stage",
|
||||
"start_date",
|
||||
"target_area"
|
||||
],
|
||||
"questions": [
|
||||
{
|
||||
"id": "growth_stage",
|
||||
"field": "growth_stage",
|
||||
"question": "محصول الان در چه مرحله رشدی قرار دارد؟",
|
||||
"rationale": "مرحله رشد برای کامل شدن برنامه لازم است."
|
||||
},
|
||||
{
|
||||
"id": "start_date",
|
||||
"field": "start_date",
|
||||
"question": "این برنامه از چه تاریخی یا از چه زمانی باید شروع شود؟",
|
||||
"rationale": "زمان شروع برنامه هنوز مشخص نشده است."
|
||||
},
|
||||
{
|
||||
"id": "target_area",
|
||||
"field": "target_area",
|
||||
"question": "این برنامه برای کل مزرعه است یا بخش/ناحیه خاصی از مزرعه؟",
|
||||
"rationale": "محدوده اجرای برنامه باید مشخص باشد."
|
||||
}
|
||||
],
|
||||
"collected_data": {
|
||||
"crop_name": "گوجهفرنگی",
|
||||
"growth_stage": null,
|
||||
"irrigation_method": "قطرهای",
|
||||
"water_amount_per_event": "18 لیتر برای هر بوته",
|
||||
"duration_minutes": 25,
|
||||
"frequency_text": "هر سه روز یکبار",
|
||||
"interval_days": 3,
|
||||
"preferred_time_of_day": "صبح زود",
|
||||
"start_date": null,
|
||||
"target_area": null,
|
||||
"trigger_conditions": [],
|
||||
"notes": []
|
||||
},
|
||||
"final_plan": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ساختار `questions`
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `id` | string | شناسه سوال |
|
||||
| `field` | string | فیلدی که این سوال برای آن پرسیده شده |
|
||||
| `question` | string | متن سوال برای نمایش به کاربر |
|
||||
| `rationale` | string | توضیح کوتاه برای اینکه چرا این سوال لازم است |
|
||||
|
||||
---
|
||||
|
||||
## flow پیشنهادی فرانتاند برای آبیاری
|
||||
|
||||
### مرحله 1
|
||||
|
||||
کاربر متن آزاد میفرستد:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "برای گوجه فرنگی هر سه روز یک بار آبیاری می کنم."
|
||||
}
|
||||
```
|
||||
|
||||
### مرحله 2
|
||||
|
||||
اگر `status = needs_clarification` بود:
|
||||
|
||||
- سوالها را از `data.questions` به کاربر نمایش بده
|
||||
- پاسخها را جمع کن
|
||||
|
||||
### مرحله 3
|
||||
|
||||
درخواست تکمیلی بزن:
|
||||
|
||||
```json
|
||||
{
|
||||
"partial_plan": {
|
||||
"crop_name": "گوجه فرنگی",
|
||||
"growth_stage": null,
|
||||
"irrigation_method": null,
|
||||
"water_amount_per_event": null,
|
||||
"duration_minutes": null,
|
||||
"frequency_text": "هر سه روز یک بار",
|
||||
"interval_days": 3,
|
||||
"preferred_time_of_day": null,
|
||||
"start_date": null,
|
||||
"target_area": null,
|
||||
"trigger_conditions": [],
|
||||
"notes": []
|
||||
},
|
||||
"answers": {
|
||||
"growth_stage": "گلدهی",
|
||||
"irrigation_method": "قطره ای",
|
||||
"water_amount_per_event": "18 لیتر برای هر بوته",
|
||||
"duration_minutes": 25,
|
||||
"preferred_time_of_day": "صبح زود",
|
||||
"start_date": "از امروز",
|
||||
"target_area": "کل مزرعه"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### مرحله 4
|
||||
|
||||
اگر `status = completed` شد:
|
||||
|
||||
- از `data.final_plan` به عنوان JSON نهایی استفاده کن
|
||||
|
||||
---
|
||||
|
||||
## 2) API استخراج برنامه کودهی
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
POST /api/fertilization/plan-from-text/
|
||||
```
|
||||
|
||||
### کاربرد
|
||||
|
||||
این API متن آزاد کاربر درباره برنامه کودهی را به JSON ساختاریافته تبدیل میکند.
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "برای گندم در مرحله پنجه زنی هر 12 روز یک بار 20-20-20 به مقدار 35 کیلوگرم در هکتار از طریق کودآبیاری می دهم.",
|
||||
"answers": {
|
||||
"timing": "هر 12 روز یک بار"
|
||||
},
|
||||
"partial_plan": {
|
||||
"crop_name": "گندم"
|
||||
},
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111"
|
||||
}
|
||||
```
|
||||
|
||||
### فیلدهای request
|
||||
|
||||
| فیلد | نوع | اجباری | توضیح |
|
||||
|---|---|---:|---|
|
||||
| `message` | string | خیر | متن آزاد کاربر |
|
||||
| `answers` | object | خیر | پاسخهای تکمیلی کاربر |
|
||||
| `partial_plan` | object | خیر | داده استخراج شده مرحله قبل |
|
||||
| `farm_uuid` | string | خیر | برای context مزرعه |
|
||||
|
||||
### validation error
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"msg": "داده نامعتبر.",
|
||||
"data": {
|
||||
"non_field_errors": [
|
||||
"حداقل یکی از message، answers یا partial_plan باید ارسال شود."
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## پاسخ موفق - حالت تکمیل شده
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"status": "completed",
|
||||
"status_fa": "تکمیل شد",
|
||||
"summary": "برنامه کودهی برای گندم در مرحله پنجه زنی با کود 20-20-20 به صورت کودآبیاری هر 12 روز یک بار اجرا می شود.",
|
||||
"missing_fields": [],
|
||||
"questions": [],
|
||||
"collected_data": {
|
||||
"crop_name": "گندم",
|
||||
"growth_stage": "پنجه زنی",
|
||||
"objective": "تقویت رشد رویشی",
|
||||
"applications": [
|
||||
{
|
||||
"fertilizer_name": "کود کامل 20-20-20",
|
||||
"formula": "20-20-20",
|
||||
"amount": "35 کیلوگرم در هکتار",
|
||||
"application_method": "کودآبیاری",
|
||||
"timing": "هر 12 روز یک بار",
|
||||
"interval_days": 12,
|
||||
"purpose": "تقویت رشد رویشی"
|
||||
}
|
||||
],
|
||||
"notes": []
|
||||
},
|
||||
"final_plan": {
|
||||
"crop_name": "گندم",
|
||||
"growth_stage": "پنجه زنی",
|
||||
"objective": "تقویت رشد رویشی",
|
||||
"applications": [
|
||||
{
|
||||
"fertilizer_name": "کود کامل 20-20-20",
|
||||
"formula": "20-20-20",
|
||||
"amount": "35 کیلوگرم در هکتار",
|
||||
"application_method": "کودآبیاری",
|
||||
"timing": "هر 12 روز یک بار",
|
||||
"interval_days": 12,
|
||||
"purpose": "تقویت رشد رویشی"
|
||||
}
|
||||
],
|
||||
"notes": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### فیلدهای `collected_data` و `final_plan`
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `crop_name` | string | نام محصول |
|
||||
| `growth_stage` | string | مرحله رشد |
|
||||
| `objective` | string/null | هدف برنامه |
|
||||
| `applications` | array[object] | لیست نوبتها یا اقلام کودی |
|
||||
| `notes` | array[string] | نکات تکمیلی |
|
||||
|
||||
### ساختار هر application
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `fertilizer_name` | string | نام کود |
|
||||
| `formula` | string | فرمول یا آنالیز کود |
|
||||
| `amount` | string | مقدار مصرف |
|
||||
| `application_method` | string | روش مصرف |
|
||||
| `timing` | string | زمانبندی مصرف |
|
||||
| `interval_days` | number | فاصله بین نوبتها |
|
||||
| `purpose` | string/null | هدف آن نوبت |
|
||||
|
||||
---
|
||||
|
||||
## پاسخ موفق - حالت نیاز به سوال تکمیلی
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"status": "needs_clarification",
|
||||
"status_fa": "نیازمند پرسش تکمیلی",
|
||||
"summary": "اطلاعات برنامه کودهی برای ساخت JSON نهایی کافی نیست و به چند پاسخ تکمیلی نیاز است.",
|
||||
"missing_fields": [
|
||||
"growth_stage",
|
||||
"formula",
|
||||
"interval_days"
|
||||
],
|
||||
"questions": [
|
||||
{
|
||||
"id": "growth_stage",
|
||||
"field": "growth_stage",
|
||||
"question": "محصول الان در چه مرحله رشدی قرار دارد؟",
|
||||
"rationale": "مرحله رشد برای تکمیل برنامه لازم است."
|
||||
},
|
||||
{
|
||||
"id": "formula",
|
||||
"field": "formula",
|
||||
"question": "فرمول یا آنالیز کود چیست؟ مثلا 20-20-20.",
|
||||
"rationale": "ترکیب دقیق کود هنوز مشخص نشده است."
|
||||
},
|
||||
{
|
||||
"id": "interval_days",
|
||||
"field": "interval_days",
|
||||
"question": "فاصله بین نوبت های مصرف کود چند روز است؟",
|
||||
"rationale": "عدد فاصله بین نوبت ها برای JSON نهایی لازم است."
|
||||
}
|
||||
],
|
||||
"collected_data": {
|
||||
"crop_name": "گندم",
|
||||
"growth_stage": null,
|
||||
"objective": null,
|
||||
"applications": [
|
||||
{
|
||||
"fertilizer_name": "کود کامل",
|
||||
"formula": null,
|
||||
"amount": "35 کیلوگرم در هکتار",
|
||||
"application_method": "کودآبیاری",
|
||||
"timing": "هر چند وقت یک بار",
|
||||
"interval_days": null,
|
||||
"purpose": null
|
||||
}
|
||||
],
|
||||
"notes": []
|
||||
},
|
||||
"final_plan": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## flow پیشنهادی فرانتاند برای کودهی
|
||||
|
||||
### درخواست اولیه
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "برای گندم از کود کامل استفاده می کنم."
|
||||
}
|
||||
```
|
||||
|
||||
### اگر incomplete بود
|
||||
|
||||
- از `questions` سوالها را بگیر
|
||||
- در UI نمایش بده
|
||||
- پاسخها را جمع کن
|
||||
|
||||
### درخواست تکمیلی
|
||||
|
||||
```json
|
||||
{
|
||||
"partial_plan": {
|
||||
"crop_name": "گندم",
|
||||
"growth_stage": null,
|
||||
"objective": null,
|
||||
"applications": [
|
||||
{
|
||||
"fertilizer_name": "کود کامل",
|
||||
"formula": null,
|
||||
"amount": null,
|
||||
"application_method": null,
|
||||
"timing": null,
|
||||
"interval_days": null,
|
||||
"purpose": null
|
||||
}
|
||||
],
|
||||
"notes": []
|
||||
},
|
||||
"answers": {
|
||||
"growth_stage": "پنجه زنی",
|
||||
"formula": "20-20-20",
|
||||
"amount": "35 کیلوگرم در هکتار",
|
||||
"application_method": "کودآبیاری",
|
||||
"timing": "هر 12 روز یک بار",
|
||||
"interval_days": 12
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### اگر complete شد
|
||||
|
||||
- از `final_plan` استفاده کن
|
||||
|
||||
---
|
||||
|
||||
## نکات مهم برای فرانتاند
|
||||
|
||||
### 1. به `status` تکیه کنید
|
||||
|
||||
مهمترین فیلد برای کنترل flow:
|
||||
|
||||
- `completed`
|
||||
- `needs_clarification`
|
||||
|
||||
### 2. اگر `needs_clarification` بود
|
||||
|
||||
باید:
|
||||
|
||||
- `questions` را به کاربر نمایش دهید
|
||||
- `partial_plan` را نگه دارید
|
||||
- پاسخهای کاربر را در `answers` ارسال کنید
|
||||
|
||||
### 3. اگر `completed` بود
|
||||
|
||||
باید:
|
||||
|
||||
- `final_plan` را به عنوان نسخه نهایی برنامه ذخیره یا نمایش دهید
|
||||
|
||||
### 4. `collected_data` همیشه مهم است
|
||||
|
||||
حتی اگر برنامه ناقص باشد، `collected_data` نشان میدهد سیستم تا این لحظه چه چیزهایی را فهمیده است.
|
||||
|
||||
### 5. null در حالت ناقص طبیعی است
|
||||
|
||||
در حالت `needs_clarification` ممکن است بعضی فیلدهای `collected_data` `null` باشند.
|
||||
اما در حالت `completed` نباید فیلدهای اصلی ناقص باشند.
|
||||
|
||||
### 6. فرانتاند بهتر است سوالها را به صورت step-by-step بپرسد
|
||||
|
||||
پیشنهاد:
|
||||
|
||||
- سوال اول را نشان بده
|
||||
- جواب را بگیر
|
||||
- همه جوابها را در `answers` جمع کن
|
||||
- دوباره API را صدا بزن
|
||||
|
||||
---
|
||||
|
||||
## جمعبندی تفاوت دو API
|
||||
|
||||
| API | موضوع | خروجی نهایی |
|
||||
|---|---|---|
|
||||
| `/api/irrigation/plan-from-text/` | استخراج برنامه آبیاری | `final_plan` با ساختار آبیاری |
|
||||
| `/api/fertilization/plan-from-text/` | استخراج برنامه کودهی | `final_plan` با ساختار کودهی |
|
||||
|
||||
---
|
||||
|
||||
## مسیر فایل
|
||||
|
||||
این داکیومنت در این مسیر ذخیره شده:
|
||||
|
||||
`docs/irrigation_fertilization_plan_parser_apis.md`
|
||||
@@ -0,0 +1,512 @@
|
||||
# توضیح `location_data/apps.py` و `farm_data/apps.py`
|
||||
|
||||
این فایل یک توضیح کوتاه ولی کاربردی از دو فایل تنظیمات اپ Django در پروژه میدهد:
|
||||
|
||||
- `location_data/apps.py`
|
||||
- `farm_data/apps.py`
|
||||
|
||||
همچنین برای فهم بهتر، به فیلدهای مهم مدلهای مرتبط هم اشاره میکند تا معلوم شود این دو app در عمل چه دادههایی را مدیریت میکنند.
|
||||
|
||||
---
|
||||
|
||||
## 1) فایل `location_data/apps.py`
|
||||
|
||||
این فایل AppConfig مربوط به اپ `location_data` را تعریف میکند.
|
||||
|
||||
کلاس اصلی:
|
||||
|
||||
```python
|
||||
class SoilDataConfig(AppConfig):
|
||||
```
|
||||
|
||||
### فیلدها و بخشها
|
||||
|
||||
#### `default_auto_field = "django.db.models.BigAutoField"`
|
||||
|
||||
- مشخص میکند اگر در مدلهای این اپ برای primary key چیزی تعریف نشده باشد، Django بهصورت پیشفرض از `BigAutoField` استفاده کند.
|
||||
- `BigAutoField` یک شناسه عددی auto-increment بزرگ است.
|
||||
- این گزینه بیشتر برای مدلهایی مفید است که قرار است رکوردهای زیادی داشته باشند.
|
||||
|
||||
#### `name = "location_data"`
|
||||
|
||||
- نام کامل اپ Django است.
|
||||
- Django با این مقدار اپ را register میکند.
|
||||
- این مقدار باید با مسیر ماژول اپ یکی باشد.
|
||||
|
||||
#### `verbose_name = "Soil Data (SoilGrids)"`
|
||||
|
||||
- نام نمایشی اپ در Django admin یا جاهایی است که Django نام انسانی اپ را نشان میدهد.
|
||||
- این مقدار بیشتر جنبه نمایشی دارد و روی منطق برنامه اثر مستقیم ندارد.
|
||||
|
||||
---
|
||||
|
||||
## propertyها و سرویسها در `location_data/apps.py`
|
||||
|
||||
این فایل فقط metadata اپ را نگه نمیدارد؛ دو سرویس reusable هم از طریق AppConfig در اختیار بقیه پروژه میگذارد.
|
||||
|
||||
### `@cached_property def ndvi_health_service(self)`
|
||||
|
||||
- این property یک نمونه از `NdviHealthService` میسازد.
|
||||
- import آن از فایل `.ndvi` انجام میشود.
|
||||
- به دلیل `cached_property` فقط یک بار ساخته میشود و بعد همان instance دوباره استفاده میشود.
|
||||
|
||||
کاربرد:
|
||||
|
||||
- برای تحلیل یا سرویسهای مرتبط با NDVI
|
||||
- جلوگیری از ساخت مکرر object
|
||||
|
||||
### `@cached_property def soil_data_adapter(self)`
|
||||
|
||||
این property adapter مناسب برای داده خاک را بر اساس تنظیمات پروژه انتخاب میکند.
|
||||
|
||||
دو adapter پشتیبانی میشوند:
|
||||
|
||||
- `SoilGridsAdapter`
|
||||
- `MockSoilDataAdapter`
|
||||
|
||||
#### منطق انتخاب provider
|
||||
|
||||
مقدار provider از این setting خوانده میشود:
|
||||
|
||||
```python
|
||||
settings.SOIL_DATA_PROVIDER
|
||||
```
|
||||
|
||||
اگر وجود نداشته باشد، مقدار پیشفرض:
|
||||
|
||||
```python
|
||||
"mock"
|
||||
```
|
||||
|
||||
#### حالت اول: `provider == "soilgrids"`
|
||||
|
||||
در این حالت:
|
||||
|
||||
- از `SoilGridsAdapter` استفاده میشود
|
||||
- timeout آن از این setting میآید:
|
||||
|
||||
```python
|
||||
settings.SOILGRIDS_TIMEOUT_SECONDS
|
||||
```
|
||||
|
||||
اگر این setting هم نباشد، مقدار پیشفرض:
|
||||
|
||||
```python
|
||||
60
|
||||
```
|
||||
|
||||
یعنی درخواست به provider واقعی SoilGrids حداکثر 60 ثانیه صبر میکند.
|
||||
|
||||
#### حالت دوم: `provider == "mock"`
|
||||
|
||||
در این حالت:
|
||||
|
||||
- از `MockSoilDataAdapter` استفاده میشود
|
||||
- delay آن از این setting میآید:
|
||||
|
||||
```python
|
||||
settings.SOIL_MOCK_DELAY_SECONDS
|
||||
```
|
||||
|
||||
اگر این setting هم نباشد، مقدار پیشفرض:
|
||||
|
||||
```python
|
||||
0.8
|
||||
```
|
||||
|
||||
یعنی adapter تستی/نمایشی با تاخیر مصنوعی 0.8 ثانیه کار میکند.
|
||||
|
||||
#### حالت نامعتبر
|
||||
|
||||
اگر `SOIL_DATA_PROVIDER` چیزی غیر از `soilgrids` یا `mock` باشد:
|
||||
|
||||
- `ValueError` رخ میدهد
|
||||
- یعنی config پروژه اشتباه است و provider شناخته نشده
|
||||
|
||||
---
|
||||
|
||||
## ارتباط `location_data/apps.py` با فیلدهای واقعی داده
|
||||
|
||||
این فایل خودش مدل تعریف نمیکند، اما بهصورت مستقیم برای کار با مدلهای اپ `location_data` استفاده میشود؛ مهمترین آنها اینها هستند:
|
||||
|
||||
- `location_data.models.SoilLocation`
|
||||
- `location_data.models.SoilDepthData`
|
||||
- `location_data.models.NdviObservation`
|
||||
|
||||
### فیلدهای مهم `SoilLocation`
|
||||
|
||||
#### `latitude`
|
||||
|
||||
- عرض جغرافیایی مرکز زمین
|
||||
- نوع آن `DecimalField` است
|
||||
- روی آن index وجود دارد
|
||||
- این نقطه معمولاً مرکز هندسی مزرعه است، نه لزوماً یکی از گوشههای مرز
|
||||
|
||||
#### `longitude`
|
||||
|
||||
- طول جغرافیایی مرکز زمین
|
||||
- مثل `latitude` برای lookup و resolve کردن دادههای خاک استفاده میشود
|
||||
|
||||
#### `task_id`
|
||||
|
||||
- شناسه تسک Celery برای پردازشهای async
|
||||
- وقتی fetch داده خاک یا پردازش مرتبط در صف باشد، میتوان با این فیلد وضعیت را track کرد
|
||||
|
||||
#### `farm_boundary`
|
||||
|
||||
- مرز مزرعه را بهصورت JSON نگه میدارد
|
||||
- معمولاً بهشکل `Polygon` یا ساختار corner-based ذخیره میشود
|
||||
- این فیلد خیلی مهم است چون فقط یک نقطه center نگه نمیدارید، بلکه شکل کلی زمین هم ثبت میشود
|
||||
|
||||
#### `created_at` / `updated_at`
|
||||
|
||||
- زمان ایجاد و آخرین بهروزرسانی رکورد
|
||||
|
||||
### propertyهای مهم `SoilLocation`
|
||||
|
||||
#### `center_latitude`
|
||||
|
||||
- فقط alias برای `latitude` است
|
||||
|
||||
#### `center_longitude`
|
||||
|
||||
- فقط alias برای `longitude` است
|
||||
|
||||
#### `is_complete`
|
||||
|
||||
- بررسی میکند آیا هر سه لایه خاک برای این location ثبت شدهاند یا نه
|
||||
- شرط آن این است که تعداد `depths` دقیقاً 3 باشد
|
||||
|
||||
### فیلدهای مهم `SoilDepthData`
|
||||
|
||||
این مدل برای هر location سه رکورد عمق خاک نگه میدارد:
|
||||
|
||||
- `0-5cm`
|
||||
- `5-15cm`
|
||||
- `15-30cm`
|
||||
|
||||
فیلدهای اصلی:
|
||||
|
||||
- `soil_location`: ارتباط به `SoilLocation`
|
||||
- `depth_label`: مشخص میکند داده برای کدام عمق است
|
||||
- `bdod`: چگالی ظاهری خاک
|
||||
- `cec`: ظرفیت تبادل کاتیونی
|
||||
- `cfvo`: حجم قطعات درشت خاک
|
||||
- `clay`: درصد رس
|
||||
- `nitrogen`: مقدار نیتروژن
|
||||
- `ocd` و `ocs`: شاخصهای کربن آلی
|
||||
- `phh2o`: pH خاک
|
||||
- `sand`: درصد شن
|
||||
- `silt`: درصد سیلت
|
||||
- `soc`: کربن آلی خاک
|
||||
- `wv0010`: رطوبت حجمی در فشار 10 kPa
|
||||
- `wv0033`: رطوبت در حدود ظرفیت زراعی
|
||||
- `wv1500`: رطوبت در نقطه پژمردگی دائم
|
||||
|
||||
این فیلدها برای شبیهسازی، آبیاری، و تخمین وضعیت واقعی خاک مهم هستند.
|
||||
|
||||
### فیلدهای مهم `NdviObservation`
|
||||
|
||||
- `location`: ارتباط با `SoilLocation`
|
||||
- `observation_date`: تاریخ مشاهده
|
||||
- `mean_ndvi`: میانگین NDVI
|
||||
- `ndvi_map`: داده مکانی NDVI
|
||||
- `vegetation_health_class`: کلاس سلامت پوشش گیاهی
|
||||
- `satellite_source`: منبع تصویر مثل `sentinel-2`
|
||||
- `cloud_cover`: درصد پوشش ابر
|
||||
- `metadata`: داده تکمیلی
|
||||
|
||||
---
|
||||
|
||||
## نکته مهم: grid بندی زمین انجام میشود
|
||||
|
||||
بله، در لایه داده و سنجش از دور، مفهوم grid بندی وجود دارد.
|
||||
|
||||
اما این grid بندی در پروژه بیشتر در این دو سطح دیده میشود:
|
||||
|
||||
### 1) grid در NDVI map
|
||||
|
||||
در `location_data/remote_sensing.py` داده NDVI بهصورت grid محاسبه و ذخیره میشود.
|
||||
|
||||
یعنی:
|
||||
|
||||
- تصویر ماهوارهای به خانههای کوچکتر تقسیم میشود
|
||||
- برای هر خانه مقدار NDVI محاسبه میشود
|
||||
- خروجی در `ndvi_map` معمولاً بهشکل grid نگهداری میشود
|
||||
|
||||
این یعنی وضعیت سلامت گیاه فقط بهصورت یک عدد کلی نیست، بلکه میتواند روی بخشهای مختلف زمین map شود.
|
||||
|
||||
### 2) grid/cell در adapter خاک
|
||||
|
||||
در `location_data/soil_adapters.py` هم منطق cell/grid دیده میشود، مخصوصاً در adapterهای mock یا interpolation-based.
|
||||
|
||||
یعنی:
|
||||
|
||||
- مختصات lat/lon به cellهای شبکهای نگاشت میشود
|
||||
- در بعضی محاسبات از `grid_x` و `grid_y` استفاده میشود
|
||||
- این کمک میکند داده خاک برای ناحیههای نزدیک، رفتار مکانی منطقیتری داشته باشد
|
||||
|
||||
### نتیجه مهم
|
||||
|
||||
خود مدل `SoilLocation` یک مرکز زمین را نگه میدارد، ولی مرز مزرعه و NDVI grid باعث میشوند سیستم فقط point-based نباشد.
|
||||
|
||||
یعنی:
|
||||
|
||||
- مرکز زمین برای lookup سریع و اتصال به داده خاک/هوا استفاده میشود
|
||||
- مرز مزرعه برای شکل واقعی زمین ذخیره میشود
|
||||
- grid بندی برای تحلیل مکانی، مخصوصاً در NDVI، انجام میشود
|
||||
|
||||
---
|
||||
|
||||
## مرکز زمین چطور از مرز مزرعه بهدست میآید
|
||||
|
||||
در `farm_data/services.py` از روی `farm_boundary`، مرکز هندسی polygon محاسبه میشود.
|
||||
|
||||
پس flow کلی اینطور است:
|
||||
|
||||
1. مرز مزرعه ارسال میشود
|
||||
2. polygon نرمال میشود
|
||||
3. centroid هندسی آن محاسبه میشود
|
||||
4. یک `SoilLocation` برای center ساخته یا پیدا میشود
|
||||
5. بعد داده خاک، NDVI و هوا به این location متصل میشوند
|
||||
|
||||
پس زمین فقط با یک نقطه خام ثبت نمیشود؛ اول مرز دارد، بعد center از روی آن بهدست میآید.
|
||||
|
||||
---
|
||||
|
||||
## متدهای کمکی `location_data/apps.py`
|
||||
|
||||
### `get_ndvi_health_service()`
|
||||
|
||||
- خروجی `self.ndvi_health_service` را برمیگرداند
|
||||
- یک accessor ساده برای گرفتن سرویس NDVI است
|
||||
|
||||
### `get_soil_data_adapter()`
|
||||
|
||||
- خروجی `self.soil_data_adapter` را برمیگرداند
|
||||
- بقیه بخشهای پروژه از این متد برای گرفتن adapter فعال استفاده میکنند
|
||||
/
|
||||
---
|
||||
|
||||
## فیلدها و settingهای مهم مرتبط با `location_data/apps.py`
|
||||
|
||||
### فیلدهای AppConfig
|
||||
|
||||
- `default_auto_field`: نوع primary key پیشفرض مدلها
|
||||
- `name`: نام داخلی اپ
|
||||
- `verbose_name`: نام نمایشی اپ
|
||||
|
||||
### settingهای استفادهشده
|
||||
|
||||
- `SOIL_DATA_PROVIDER`: انتخاب provider فعال خاک
|
||||
- `SOILGRIDS_TIMEOUT_SECONDS`: timeout برای provider واقعی SoilGrids
|
||||
- `SOIL_MOCK_DELAY_SECONDS`: تاخیر مصنوعی برای provider mock
|
||||
|
||||
---
|
||||
|
||||
## 2) فایل `farm_data/apps.py`
|
||||
|
||||
این فایل AppConfig مربوط به اپ `farm_data` را تعریف میکند.
|
||||
|
||||
کلاس اصلی:
|
||||
|
||||
```python
|
||||
class FarmDataConfig(AppConfig):
|
||||
```
|
||||
|
||||
### فیلدها
|
||||
|
||||
#### `default_auto_field = "django.db.models.BigAutoField"`
|
||||
|
||||
- مثل اپ قبلی، تعیین میکند primary key پیشفرض مدلهای این اپ از نوع `BigAutoField` باشد.
|
||||
|
||||
#### `name = "farm_data"`
|
||||
|
||||
- نام داخلی و ماژول اپ Django است.
|
||||
- برای شناسایی اپ در `INSTALLED_APPS` و registry داخلی Django استفاده میشود.
|
||||
|
||||
#### `label = "sensor_data"`
|
||||
|
||||
- label داخلی اپ در registry Django است.
|
||||
- این فیلد زمانی مهم میشود که:
|
||||
- بخواهید نام registry اپ با `name` فرق داشته باشد
|
||||
- یا از تداخل نام اپها جلوگیری کنید
|
||||
- در این پروژه، اپ `farm_data` با label داخلی `sensor_data` شناخته میشود.
|
||||
|
||||
نکته:
|
||||
|
||||
- `label` باید در کل پروژه یکتا باشد.
|
||||
- این مقدار ممکن است در migrationها، relationها یا lookupهای app registry اثر داشته باشد.
|
||||
|
||||
#### `verbose_name = "farm-data"`
|
||||
|
||||
- نام نمایشی اپ است.
|
||||
- بیشتر برای admin و نمایش انسانی استفاده میشود.
|
||||
|
||||
---
|
||||
|
||||
## نکته مهم درباره `farm_data/apps.py`
|
||||
|
||||
برخلاف `location_data/apps.py`، این فایل:
|
||||
|
||||
- `cached_property` ندارد
|
||||
- service locator ندارد
|
||||
- adapter یا provider انتخاب نمیکند
|
||||
|
||||
یعنی فعلاً فقط نقش config پایه اپ را دارد.
|
||||
|
||||
---
|
||||
|
||||
## ارتباط `farm_data/apps.py` با فیلدهای واقعی داده
|
||||
|
||||
این app بیشتر دادههای farm-level و sensor-level را نگه میدارد. مهمترین مدلهایش:
|
||||
|
||||
- `farm_data.models.SensorData`
|
||||
- `farm_data.models.SensorParameter`
|
||||
- `farm_data.models.ParameterUpdateLog`
|
||||
|
||||
### فیلدهای مهم `SensorData`
|
||||
|
||||
#### `farm_uuid`
|
||||
|
||||
- شناسه یکتای مزرعه
|
||||
- primary key این مدل است
|
||||
- هر رکورد `SensorData` نماینده یک مزرعه است
|
||||
|
||||
#### `center_location`
|
||||
|
||||
- ارتباط به `location_data.SoilLocation`
|
||||
- یعنی این مزرعه به یک location مرکزی وصل است
|
||||
- از همین نقطه مرکزی برای weather/soil/simulation استفاده میشود
|
||||
|
||||
#### `weather_forecast`
|
||||
|
||||
- ارتباط اختیاری با `weather.WeatherForecast`
|
||||
- اگر موجود باشد، forecast منتخب یا آخرین forecast به مزرعه وصل میشود
|
||||
|
||||
#### `sensor_payload`
|
||||
|
||||
- مهمترین فیلد این مدل است
|
||||
- داده سنسورها بهصورت JSON نگهداری میشود
|
||||
- ساختار معمول آن شبیه این است:
|
||||
|
||||
```json
|
||||
{
|
||||
"sensor-7-1": {
|
||||
"soil_moisture": 25.5,
|
||||
"soil_temperature": 22.3,
|
||||
"soil_ph": 7.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
مزیت این ساختار:
|
||||
|
||||
- چند سنسور در یک مزرعه پشتیبانی میشود
|
||||
- هر سنسور میتواند فیلدهای خاص خودش را داشته باشد
|
||||
- schema سنسورها rigid نیست
|
||||
|
||||
#### `plants`
|
||||
|
||||
- رابطه چندبهچند با `plant.Plant`
|
||||
- یعنی یک farm میتواند چند گیاه مرتبط داشته باشد
|
||||
|
||||
#### `irrigation_method`
|
||||
|
||||
- روش آبیاری انتخابشده برای مزرعه
|
||||
- برای recommendation و planning مهم است
|
||||
|
||||
#### `created_at` / `updated_at`
|
||||
|
||||
- زمان ایجاد و آخرین ویرایش رکورد
|
||||
|
||||
### propertyهای مهم `SensorPayloadMixin`
|
||||
|
||||
مدل `SensorData` از `SensorPayloadMixin` ارث میگیرد و این helperها را دارد:
|
||||
|
||||
#### `_payload()`
|
||||
|
||||
- payload را فقط وقتی dict معتبر باشد برمیگرداند
|
||||
|
||||
#### `get_sensor_block(sensor_key=None)`
|
||||
|
||||
- اگر `sensor_key` بدهید، همان بلوک سنسور را برمیگرداند
|
||||
- اگر ندهید، اولین بلوک معتبر را برمیگرداند
|
||||
|
||||
#### `get_metric(metric_name, sensor_key=None)`
|
||||
|
||||
- یک metric خاص را از payload پیدا میکند
|
||||
- اول در sensor مشخصشده میگردد
|
||||
- اگر پیدا نشد، در بقیه blockها جستجو میکند
|
||||
|
||||
#### propertyهای آماده
|
||||
|
||||
این propertyها shortcut هستند:
|
||||
|
||||
- `soil_moisture`
|
||||
- `soil_temperature`
|
||||
- `soil_ph`
|
||||
- `electrical_conductivity`
|
||||
- `nitrogen`
|
||||
- `phosphorus`
|
||||
- `potassium`
|
||||
|
||||
یعنی بهجای parse دستی JSON، مستقیم میتوان این متریکها را خواند.
|
||||
|
||||
### فیلدهای مهم `SensorParameter`
|
||||
|
||||
این مدل dictionary پارامترهای سنسور را نگه میدارد.
|
||||
|
||||
#### `sensor_key`
|
||||
|
||||
- کلید سنسور مثل `sensor-7-1`
|
||||
|
||||
#### `code`
|
||||
|
||||
- کد پارامتر مثل `soil_moisture`
|
||||
|
||||
#### `name_fa`
|
||||
|
||||
- نام فارسی پارامتر
|
||||
|
||||
#### `unit`
|
||||
|
||||
- واحد پارامتر مثل `%` یا `dS/m`
|
||||
|
||||
#### `data_type`
|
||||
|
||||
- نوع داده مثل `float`, `int`, `string`, `bool`
|
||||
|
||||
#### `metadata`
|
||||
|
||||
- داده تکمیلی برای UI یا validation
|
||||
- مثلاً:
|
||||
- بازه مجاز
|
||||
- توضیح
|
||||
- تنظیمات نمایش
|
||||
|
||||
### فیلدهای مهم `ParameterUpdateLog`
|
||||
|
||||
- `parameter`: ارتباط به `SensorParameter`
|
||||
- `action`: نوع عملیات مثل `added` یا `modified`
|
||||
- `payload`: خلاصه تغییرات
|
||||
- `updated_at`: زمان ثبت لاگ
|
||||
|
||||
این مدل برای audit و پیگیری تغییرات پارامترها مفید است.
|
||||
|
||||
---
|
||||
|
||||
## جمعبندی
|
||||
|
||||
### `location_data/apps.py`
|
||||
|
||||
- هم metadata اپ را نگه میدارد
|
||||
- هم سرویس و adapter در اختیار پروژه میگذارد
|
||||
- هم از settingها برای انتخاب provider واقعی یا mock استفاده میکند
|
||||
- و در عمل با location center، مرز مزرعه، داده لایههای خاک و gridهای NDVI کار میکند
|
||||
|
||||
### `farm_data/apps.py`
|
||||
|
||||
- فقط config پایه AppConfig را تعریف میکند
|
||||
- نقش آن بیشتر register کردن اپ با نام و label مشخص است
|
||||
- اما دادههای اصلی مزرعه مثل `farm_uuid`، `sensor_payload`، گیاه، روش آبیاری و اتصال به center location در مدلهای همین app نگهداری میشوند
|
||||
@@ -0,0 +1,371 @@
|
||||
# ساختار فعلی `location_data`
|
||||
|
||||
این فایل وضعیت فعلی اپ `location_data` را توضیح میدهد؛ هم از نظر ساختار فایلها و هم از نظر مدلها، APIها و جریان داده.
|
||||
|
||||
## هدف فعلی اپ
|
||||
|
||||
اپ `location_data` فعلاً این مسئولیتها را دارد:
|
||||
|
||||
- نگهداری موقعیت زمین با `lat` و `lon`
|
||||
- نگهداری مرز مزرعه در `farm_boundary`
|
||||
- نگهداری ساختار بلوکهای زمین در `block_layout`
|
||||
- نگهداری دادههای خاک برای عمقهای مختلف در `SoilDepthData`
|
||||
- نگهداری مشاهدات NDVI در `NdviObservation`
|
||||
- برگرداندن ساختار بلوکهای زمین از API محلی بدون نیاز به API خارجی در فاز فعلی
|
||||
|
||||
نکته مهم:
|
||||
|
||||
- در فاز فعلی، endpoint اصلی `location_data` برای ساختار زمین، فقط داده را در دیتابیس میخواند/ذخیره میکند.
|
||||
- فعلاً برای بلوکها، زیربلوکها و دادههای ماهوارهای هیچ درخواست خارجی زده نمیشود.
|
||||
|
||||
## ساختار فایلها
|
||||
|
||||
```text
|
||||
location_data/
|
||||
├── admin.py
|
||||
├── apps.py
|
||||
├── models.py
|
||||
├── serializers.py
|
||||
├── views.py
|
||||
├── urls.py
|
||||
├── tasks.py
|
||||
├── soil_adapters.py
|
||||
├── remote_sensing.py
|
||||
├── ndvi.py
|
||||
├── test_soil_api.py
|
||||
├── test_soil_adapters.py
|
||||
├── test_ndvi_health_api.py
|
||||
├── postman/
|
||||
│ └── soil_data.json
|
||||
├── management/
|
||||
│ └── commands/
|
||||
└── migrations/
|
||||
├── 0001_initial.py
|
||||
├── 0002_soildepthdata_refactor.py
|
||||
├── 0002_soillocation_ideal_sensor_profile.py
|
||||
├── 0003_rename_app_label.py
|
||||
├── 0004_soillocation_farm_boundary.py
|
||||
├── 0005_merge_20260327_0840.py
|
||||
├── 0006_remove_soillocation_ideal_sensor_profile.py
|
||||
├── 0007_ndviobservation.py
|
||||
└── 0008_soillocation_block_layout.py
|
||||
```
|
||||
|
||||
## مدلها و جدولها
|
||||
|
||||
### 1) `SoilLocation`
|
||||
|
||||
مدل اصلی اپ است و نماینده یک موقعیت یکتا برای زمین یا مرکز زمین محسوب میشود.
|
||||
|
||||
فیلدهای اصلی:
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `id` | `BigAutoField` | شناسه داخلی رکورد |
|
||||
| `latitude` | `DecimalField(9,6)` | عرض جغرافیایی |
|
||||
| `longitude` | `DecimalField(9,6)` | طول جغرافیایی |
|
||||
| `task_id` | `CharField` | شناسه تسک Celery برای جریان قدیمی واکشی خاک |
|
||||
| `farm_boundary` | `JSONField` | مرز مزرعه به شکل Polygon یا corners |
|
||||
| `input_block_count` | `PositiveIntegerField` | تعداد بلوک اولیهای که از ورودی کشاورز میآید |
|
||||
| `block_layout` | `JSONField` | ساختار بلوکها و زیربلوکهای زمین |
|
||||
| `created_at` | `DateTimeField` | زمان ایجاد |
|
||||
| `updated_at` | `DateTimeField` | زمان آخرین تغییر |
|
||||
|
||||
قیدها:
|
||||
|
||||
- روی ترکیب `latitude` و `longitude` یکتا است.
|
||||
|
||||
رفتار مهم:
|
||||
|
||||
- اگر `input_block_count` ارسال نشود، مقدار پیشفرض `1` است.
|
||||
- اگر `block_layout` خالی باشد، به صورت خودکار با یک بلوک کامل ساخته میشود.
|
||||
- متد `set_input_block_count()` ساختار اولیه بلوکها را میسازد.
|
||||
|
||||
### 2) `SoilDepthData`
|
||||
|
||||
این مدل دادههای خاک را برای هر عمق نگه میدارد و به `SoilLocation` وصل است.
|
||||
|
||||
عمقهای فعلی:
|
||||
|
||||
- `0-5cm`
|
||||
- `5-15cm`
|
||||
- `15-30cm`
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `soil_location` | `ForeignKey` | ارتباط با `SoilLocation` |
|
||||
| `depth_label` | `CharField` | برچسب عمق |
|
||||
| `bdod` تا `wv1500` | `FloatField` | پارامترهای مختلف خاک |
|
||||
| `created_at` | `DateTimeField` | زمان ثبت رکورد |
|
||||
|
||||
قیدها:
|
||||
|
||||
- برای هر `soil_location` و هر `depth_label` فقط یک رکورد وجود دارد.
|
||||
|
||||
### 3) `NdviObservation`
|
||||
|
||||
این مدل برای ذخیره مشاهدههای NDVI استفاده میشود.
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `location` | `ForeignKey` | ارتباط با `SoilLocation` |
|
||||
| `observation_date` | `DateField` | تاریخ مشاهده |
|
||||
| `mean_ndvi` | `FloatField` | میانگین NDVI |
|
||||
| `ndvi_map` | `JSONField` | داده مکانی NDVI |
|
||||
| `vegetation_health_class` | `CharField` | کلاس سلامت پوشش گیاهی |
|
||||
| `satellite_source` | `CharField` | منبع تصویر ماهوارهای |
|
||||
| `cloud_cover` | `FloatField` | درصد ابر |
|
||||
| `metadata` | `JSONField` | داده تکمیلی |
|
||||
|
||||
## ساختار `block_layout`
|
||||
|
||||
فیلد `block_layout` فعلاً ساختار پایه تقسیم زمین را نگه میدارد.
|
||||
|
||||
نمونه پیشفرض وقتی کل زمین یک بلوک باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_block_count": 1,
|
||||
"default_full_farm": true,
|
||||
"algorithm_status": "pending",
|
||||
"blocks": [
|
||||
{
|
||||
"block_code": "block-1",
|
||||
"order": 1,
|
||||
"source": "default",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
نمونه وقتی ورودی مثلاً `block_count = 3` باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_block_count": 3,
|
||||
"default_full_farm": false,
|
||||
"algorithm_status": "pending",
|
||||
"blocks": [
|
||||
{
|
||||
"block_code": "block-1",
|
||||
"order": 1,
|
||||
"source": "input",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
},
|
||||
{
|
||||
"block_code": "block-2",
|
||||
"order": 2,
|
||||
"source": "input",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
},
|
||||
{
|
||||
"block_code": "block-3",
|
||||
"order": 3,
|
||||
"source": "input",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
معنای فیلدها:
|
||||
|
||||
| فیلد | توضیح |
|
||||
|---|---|
|
||||
| `input_block_count` | تعداد بلوک اولیه |
|
||||
| `default_full_farm` | آیا کل زمین هنوز یک بلوک کامل است یا نه |
|
||||
| `algorithm_status` | وضعیت اجرای الگوریتم تقسیمبندی |
|
||||
| `blocks` | لیست بلوکهای فعلی |
|
||||
| `block_code` | کد بلوک |
|
||||
| `order` | ترتیب بلوک |
|
||||
| `source` | منشأ بلوک: `default` یا `input` |
|
||||
| `needs_subdivision` | آیا الگوریتم تشخیص داده که این بلوک باید خردتر شود یا نه |
|
||||
| `sub_blocks` | لیست زیربلوکها |
|
||||
|
||||
## Serializerها
|
||||
|
||||
### `SoilDataRequestSerializer`
|
||||
|
||||
ورودی endpoint اصلی `location_data`:
|
||||
|
||||
| فیلد | اجباری | توضیح |
|
||||
|---|---|---|
|
||||
| `lat` | بله | عرض جغرافیایی |
|
||||
| `lon` | بله | طول جغرافیایی |
|
||||
| `block_count` | خیر | تعداد بلوک اولیه، پیشفرض `1` |
|
||||
|
||||
### `SoilLocationResponseSerializer`
|
||||
|
||||
خروجی اصلی برای یک location:
|
||||
|
||||
- `id`
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `input_block_count`
|
||||
- `block_layout`
|
||||
- `depths`
|
||||
|
||||
### `SoilDepthDataSerializer`
|
||||
|
||||
لیست پارامترهای خاک برای هر عمق را برمیگرداند.
|
||||
|
||||
### `NdviHealthRequestSerializer` و `NdviHealthResponseSerializer`
|
||||
|
||||
برای endpoint مربوط به NDVI استفاده میشوند.
|
||||
|
||||
## Viewها و APIها
|
||||
|
||||
### 1) `SoilDataView`
|
||||
|
||||
مسیر:
|
||||
|
||||
- `GET /api/soil-data/`
|
||||
- `POST /api/soil-data/`
|
||||
|
||||
وظیفه فعلی:
|
||||
|
||||
- گرفتن `lat` و `lon`
|
||||
- گرفتن `block_count` در صورت وجود
|
||||
- ساخت یا پیدا کردن `SoilLocation`
|
||||
- ذخیره `input_block_count`
|
||||
- ساخت `block_layout`
|
||||
- برگرداندن پاسخ با `source = local`
|
||||
|
||||
رفتار فعلی:
|
||||
|
||||
- اگر location وجود نداشته باشد، ساخته میشود.
|
||||
- اگر `block_count` تغییر کند، ساختار `block_layout` دوباره ساخته میشود.
|
||||
- فعلاً هیچ fetch خارجی برای اطلاعات خاک یا ماهوارهای انجام نمیشود.
|
||||
|
||||
### 2) `SoilDataTaskStatusView`
|
||||
|
||||
مسیر:
|
||||
|
||||
- `GET /api/soil-data/tasks/<task_id>/status/`
|
||||
|
||||
وضعیت فعلی:
|
||||
|
||||
- هنوز در کد وجود دارد.
|
||||
- برای جریان قدیمی مبتنی بر Celery طراحی شده است.
|
||||
- با تغییر اخیر، endpoint اصلی `location_data` دیگر بهطور پیشفرض task جدیدی صف نمیکند.
|
||||
|
||||
### 3) `NdviHealthView`
|
||||
|
||||
مسیر:
|
||||
|
||||
- `POST /api/soil-data/ndvi-health/`
|
||||
|
||||
وظیفه:
|
||||
|
||||
- دریافت `farm_uuid`
|
||||
- خواندن داده NDVI از سرویس داخلی NDVI
|
||||
- برگرداندن اطلاعات سلامت پوشش گیاهی
|
||||
|
||||
## فایل `tasks.py`
|
||||
|
||||
این فایل هنوز منطق قدیمی واکشی داده خاک را نگه میدارد.
|
||||
|
||||
اجزای اصلی:
|
||||
|
||||
- `fetch_soil_data_for_coordinates()`
|
||||
- `fetch_soil_data_task()`
|
||||
|
||||
نکته:
|
||||
|
||||
- این بخش هنوز برای سازگاری و جریانهای قدیمی در پروژه باقی مانده است.
|
||||
- ولی در فاز فعلی تقسیم بلوکها، از این task برای endpoint اصلی `location_data` استفاده نمیشود.
|
||||
|
||||
## فایل `soil_adapters.py`
|
||||
|
||||
این فایل abstraction مربوط به تامین داده خاک را نگه میدارد.
|
||||
|
||||
کاربرد آن:
|
||||
|
||||
- mock provider
|
||||
- live soil provider
|
||||
- ساختار depth-based data fetch
|
||||
|
||||
در وضعیت فعلی:
|
||||
|
||||
- برای منطق بلوکبندی فعلی لازم نیست.
|
||||
- اما برای جریان قدیمی یا مراحل بعدی میتواند دوباره استفاده شود.
|
||||
|
||||
## فایل `remote_sensing.py`
|
||||
|
||||
این فایل مربوط به منطق سنجشازدور و دادههای ماهوارهای است.
|
||||
|
||||
در وضعیت فعلی:
|
||||
|
||||
- برای block layout فعلاً استفاده فعال ندارد.
|
||||
- بعداً میتواند برای تحلیل هر بلوک یا زیربلوک استفاده شود.
|
||||
|
||||
## فایل `ndvi.py`
|
||||
|
||||
این فایل سرویس/منطق NDVI را نگه میدارد و برای endpoint NDVI استفاده میشود.
|
||||
|
||||
## migrationها
|
||||
|
||||
مهمترین migrationهای فعلی:
|
||||
|
||||
| migration | توضیح |
|
||||
|---|---|
|
||||
| `0001_initial.py` | ساختار اولیه `SoilLocation` |
|
||||
| `0002_soildepthdata_refactor.py` | جداسازی دادههای عمقی در `SoilDepthData` |
|
||||
| `0004_soillocation_farm_boundary.py` | اضافه شدن `farm_boundary` |
|
||||
| `0007_ndviobservation.py` | اضافه شدن `NdviObservation` |
|
||||
| `0008_soillocation_block_layout.py` | اضافه شدن `input_block_count` و `block_layout` |
|
||||
|
||||
## تستها
|
||||
|
||||
فایلهای تست اصلی:
|
||||
|
||||
- `location_data/test_soil_api.py`
|
||||
- تست ساختار محلی بلوکها
|
||||
- تست پیشفرض یک بلوک
|
||||
- تست تغییر `block_count`
|
||||
|
||||
- `location_data/test_soil_adapters.py`
|
||||
- تست adapterهای خاک
|
||||
- تست ذخیره depth data
|
||||
|
||||
- `location_data/test_ndvi_health_api.py`
|
||||
- تست endpoint NDVI
|
||||
|
||||
## ارتباط با `farm_data`
|
||||
|
||||
`location_data` مستقیماً توسط `farm_data` استفاده میشود.
|
||||
|
||||
نمونه وابستگیها:
|
||||
|
||||
- `farm_data` از `SoilLocation` به عنوان `center_location` استفاده میکند.
|
||||
- `farm_boundary` از سمت `farm_data` میآید.
|
||||
- `block_count` هم از ورودی `farm_data` قابل ثبت است.
|
||||
- `farm_data` فعلاً فقط location و block layout را ذخیره میکند و برای این بخش sync خارجی انجام نمیدهد.
|
||||
|
||||
## جمعبندی ساختار فعلی
|
||||
|
||||
الان `location_data` دو لایه دارد:
|
||||
|
||||
1. لایه فعلی فعال برای بلوکبندی زمین
|
||||
- محلی
|
||||
- ساده
|
||||
- بدون API خارجی
|
||||
- با `input_block_count` و `block_layout`
|
||||
|
||||
2. لایه قدیمی/جانبی برای خاک و NDVI
|
||||
- `SoilDepthData`
|
||||
- `tasks.py`
|
||||
- `soil_adapters.py`
|
||||
- `NdviObservation`
|
||||
- `remote_sensing.py`
|
||||
|
||||
یعنی از نظر معماری، اپ الان هم داده مکانی زمین را نگه میدارد و هم زیرساختی برای تحلیل خاک/NDVI دارد، ولی منطق جدید بلوکها فعلاً مستقل و محلی پیاده شده است.
|
||||
@@ -0,0 +1,685 @@
|
||||
# مستند کامل عملکرد فعلی `location_data`
|
||||
|
||||
این فایل شرح میدهد که اپ `location_data` در وضعیت فعلی دقیقاً چه کاری انجام میدهد، چه مدلهایی دارد، جریان درخواستها چگونه است، منطق تقسیمبندی بلوکها چگونه اجرا میشود و چه بخشهایی فقط داده ذخیرهشده را برمیگردانند.
|
||||
|
||||
---
|
||||
|
||||
## 1) هدف فعلی اپ `location_data`
|
||||
|
||||
اپ `location_data` در وضعیت فعلی چند مسئولیت اصلی دارد:
|
||||
|
||||
- نگهداری موقعیت جغرافیایی زمین با `lat` و `lon`
|
||||
- نگهداری مرز زمین یا بلوک در `farm_boundary`
|
||||
- نگهداری ساختار بلوکهای اصلی زمین در `block_layout`
|
||||
- نگهداری نتیجه خردسازی هوشمند هر بلوک در مدل `BlockSubdivision`
|
||||
- تولید نقاط شبکهای 100 متری یا هر اندازهای که با `SUBDIVISION_CHUNK_SQM` تنظیم شود
|
||||
- اجرای خوشهبندی `KMeans` روی نقاط شبکهای
|
||||
- پیدا کردن تعداد بهینه خوشهها با روش `Elbow`
|
||||
- ذخیره centroidهای نهایی هر بخش خردشده
|
||||
- تولید و ذخیره تصویر نمودار `K-SSE` برای هر subdivision
|
||||
- نگهداری دادههای خاک در `SoilDepthData`
|
||||
- نگهداری دادههای NDVI در `NdviObservation`
|
||||
|
||||
نکته مهم:
|
||||
|
||||
- در فاز فعلی، `GET` هیچ پردازش جدیدی انجام نمیدهد.
|
||||
- تمام پردازش subdivision فقط در زمان `POST` و فقط اگر subdivision آن بلوک قبلاً ساخته نشده باشد اجرا میشود.
|
||||
|
||||
---
|
||||
|
||||
## 2) تنظیمات محیطی
|
||||
|
||||
### `SUBDIVISION_CHUNK_SQM`
|
||||
|
||||
در `config/settings.py` یک متغیر جدید اضافه شده است:
|
||||
|
||||
- `SUBDIVISION_CHUNK_SQM`
|
||||
- مقدار پیشفرض: `100`
|
||||
- واحد: متر مربع
|
||||
|
||||
کاربرد:
|
||||
|
||||
- تعیین میکند شبکه اولیه برای subdivision با چه اندازهای ساخته شود.
|
||||
- اگر مقدار `100` باشد، هر chunk تقریباً یک سلول `10m x 10m` خواهد بود، چون:
|
||||
|
||||
```text
|
||||
step = sqrt(100) = 10 meters
|
||||
```
|
||||
|
||||
این مقدار از `.env` یا environment خوانده میشود:
|
||||
|
||||
```env
|
||||
SUBDIVISION_CHUNK_SQM=100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3) مدلهای اصلی اپ
|
||||
|
||||
## 3.1) `SoilLocation`
|
||||
|
||||
این مدل رکورد اصلی location را نگه میدارد.
|
||||
|
||||
### فیلدها
|
||||
|
||||
- `latitude`
|
||||
- `longitude`
|
||||
- `task_id`
|
||||
- `farm_boundary`
|
||||
- `input_block_count`
|
||||
- `block_layout`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
### نقش
|
||||
|
||||
- هر location با ترکیب `latitude + longitude` یکتا است.
|
||||
- اطلاعات کلی زمین یا مرکز زمین را نگه میدارد.
|
||||
- اگر هنوز هیچ تقسیمبندی انجام نشده باشد، ساختار اولیه بلوکها را در `block_layout` نگه میدارد.
|
||||
|
||||
### `block_layout`
|
||||
|
||||
این فیلد JSON ساختار بلوکها را نگه میدارد. نمونه ساده:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_block_count": 1,
|
||||
"default_full_farm": true,
|
||||
"algorithm_status": "completed",
|
||||
"blocks": [
|
||||
{
|
||||
"block_code": "block-1",
|
||||
"order": 1,
|
||||
"source": "default",
|
||||
"needs_subdivision": true,
|
||||
"sub_blocks": [
|
||||
{
|
||||
"sub_block_code": "sub-block-1",
|
||||
"centroid_lat": 35.689123,
|
||||
"centroid_lon": 51.389456
|
||||
}
|
||||
],
|
||||
"subdivision_summary": {
|
||||
"chunk_size_sqm": 100,
|
||||
"grid_point_count": 24,
|
||||
"centroid_count": 3,
|
||||
"optimal_k": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### رفتار مهم
|
||||
|
||||
- اگر `block_layout` خالی باشد، به صورت پیشفرض با یک بلوک کامل ساخته میشود.
|
||||
- متد `set_input_block_count()` ساختار اولیه بلوکهای اصلی را میسازد.
|
||||
|
||||
---
|
||||
|
||||
## 3.2) `BlockSubdivision`
|
||||
|
||||
این مدل نتیجه واقعی subdivision برای هر بلوک را ذخیره میکند.
|
||||
|
||||
### فیلدها
|
||||
|
||||
- `soil_location`: ارتباط با `SoilLocation`
|
||||
- `block_code`: شناسه بلوکی که subdivision روی آن اجرا شده
|
||||
- `source_boundary`: مرز همان بلوک
|
||||
- `chunk_size_sqm`: اندازه هر chunk
|
||||
- `grid_points`: نقاط اولیه شبکه
|
||||
- `centroid_points`: centroidهای نهایی خوشهها
|
||||
- `grid_point_count`: تعداد نقاط اولیه
|
||||
- `centroid_count`: تعداد centroidهای نهایی
|
||||
- `elbow_plot`: تصویر نمودار elbow
|
||||
- `status`: وضعیت رکورد
|
||||
- `metadata`: داده تکمیلی مانند `optimal_k` و `inertia_curve`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
### نقش
|
||||
|
||||
این مدل منبع اصلی داده subdivision است.
|
||||
|
||||
یعنی:
|
||||
|
||||
- نقاط خام شبکه در این مدل ذخیره میشوند
|
||||
- centroidهای نهایی هم در این مدل ذخیره میشوند
|
||||
- نمودار elbow هم در همین مدل ذخیره میشود
|
||||
|
||||
### قید یکتا
|
||||
|
||||
برای هر location و هر `block_code` فقط یک subdivision وجود دارد:
|
||||
|
||||
```text
|
||||
(soil_location, block_code) unique
|
||||
```
|
||||
|
||||
بنابراین اگر برای یک بلوک قبلاً subdivision ساخته شده باشد، دوباره ایجاد نمیشود.
|
||||
|
||||
---
|
||||
|
||||
## 3.3) `SoilDepthData`
|
||||
|
||||
این مدل دادههای خاک برای عمقهای مختلف را نگه میدارد.
|
||||
|
||||
عمقهای فعلی:
|
||||
|
||||
- `0-5cm`
|
||||
- `5-15cm`
|
||||
- `15-30cm`
|
||||
|
||||
این بخش در حال حاضر مستقل از subdivision است و هنوز برای هر sub-block جداگانه داده خاک تولید نمیکند.
|
||||
|
||||
---
|
||||
|
||||
## 3.4) `NdviObservation`
|
||||
|
||||
این مدل دادههای NDVI و سلامت پوشش گیاهی را نگه میدارد.
|
||||
|
||||
این بخش هم فعلاً مستقل از منطق subdivision است.
|
||||
|
||||
---
|
||||
|
||||
## 4) فایل `block_subdivision.py`
|
||||
|
||||
فایل `location_data/block_subdivision.py` مرکز اصلی منطق هوشمند subdivision است.
|
||||
|
||||
### وظایف اصلی این فایل
|
||||
|
||||
- استخراج polygon از ورودی
|
||||
- تبدیل مختصات جغرافیایی به صفحه محلی متری
|
||||
- ساخت grid points با اندازه chunk مشخص
|
||||
- اجرای `KMeans` برای `K=1..10`
|
||||
- ذخیره `SSE` یا همان `Inertia`
|
||||
- پیدا کردن elbow point
|
||||
- ساخت centroidهای نهایی خوشهها
|
||||
- sync کردن نتیجه با `block_layout`
|
||||
- تولید تصویر نمودار elbow
|
||||
- ذخیره تصویر در مدل با `ContentFile`
|
||||
|
||||
---
|
||||
|
||||
## 5) روند هندسی subdivision
|
||||
|
||||
## 5.1) استخراج Polygon
|
||||
|
||||
ورودی boundary میتواند به چند شکل بیاید:
|
||||
|
||||
- GeoJSON Polygon
|
||||
- `corners`
|
||||
- آرایه مستقیم از نقاط
|
||||
|
||||
تابع `extract_polygon()` این ورودی را به لیستی از نقاط جغرافیایی تبدیل میکند.
|
||||
|
||||
نمونه ورودی معتبر:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[51.3890, 35.6890],
|
||||
[51.3902, 35.6890],
|
||||
[51.3902, 35.6900],
|
||||
[51.3890, 35.6900],
|
||||
[51.3890, 35.6890]
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.2) تبدیل مختصات به فضای محلی متری
|
||||
|
||||
برای اینکه بتوانیم فاصلهها و گریدبندی را بر اساس متر حساب کنیم، polygon از مختصات جغرافیایی به مختصات محلی متری تبدیل میشود.
|
||||
|
||||
تابع مربوط:
|
||||
|
||||
- `project_polygon_to_local_meters()`
|
||||
|
||||
ویژگی این تبدیل:
|
||||
|
||||
- نقطه اول polygon به عنوان origin در نظر گرفته میشود
|
||||
- با تقریب محلی، `lat/lon` به `x/y` در واحد متر تبدیل میشوند
|
||||
|
||||
این تبدیل برای subdivision کوچک و محلی مناسب است.
|
||||
|
||||
---
|
||||
|
||||
## 5.3) تولید grid points
|
||||
|
||||
تابع:
|
||||
|
||||
- `generate_grid_points()`
|
||||
|
||||
منطق:
|
||||
|
||||
1. ابتدا اندازه گام محاسبه میشود:
|
||||
|
||||
```text
|
||||
step_m = sqrt(chunk_size_sqm)
|
||||
```
|
||||
|
||||
2. روی bounding box polygon، نقاط مرکزی grid بررسی میشوند.
|
||||
3. هر نقطهای که داخل polygon باشد نگه داشته میشود.
|
||||
|
||||
خروجی:
|
||||
|
||||
- `grid_points`: مختصات جغرافیایی قابل ذخیره در JSON
|
||||
- `grid_vectors`: مختصات محلی متری برای ورود به `KMeans`
|
||||
|
||||
نمونه هر grid point:
|
||||
|
||||
```json
|
||||
{
|
||||
"point_code": "pt-1",
|
||||
"lat": 35.689123,
|
||||
"lon": 51.389456
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) الگوریتم خوشهبندی هوشمند
|
||||
|
||||
## 6.1) اجرای `KMeans`
|
||||
|
||||
تابع:
|
||||
|
||||
- `cluster_grid_points()`
|
||||
|
||||
منطق:
|
||||
|
||||
- روی `grid_vectors` خوشهبندی انجام میشود
|
||||
- برای `K=1` تا `K=10` اجرا میشود
|
||||
- اگر تعداد نقاط کمتر از 10 باشد، `max_k = len(grid_vectors)` در نظر گرفته میشود
|
||||
|
||||
برای هر `K`:
|
||||
|
||||
- مدل `KMeans` ساخته میشود
|
||||
- `fit()` اجرا میشود
|
||||
- مقدار `model.inertia_` به عنوان `SSE` ذخیره میشود
|
||||
|
||||
خروجی میانی:
|
||||
|
||||
```json
|
||||
[
|
||||
{"k": 1, "sse": 1300.5},
|
||||
{"k": 2, "sse": 640.2},
|
||||
{"k": 3, "sse": 390.1}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.2) پیدا کردن Elbow Point
|
||||
|
||||
تابع:
|
||||
|
||||
- `detect_elbow_point()`
|
||||
|
||||
منطق فعلی:
|
||||
|
||||
1. از روی SSEها، شیب افت بین نقاط متوالی محاسبه میشود.
|
||||
2. سپس تغییرات شیب محاسبه میشود.
|
||||
3. هر جایی که افت شیب ناگهان متوقف شود، همان نقطه elbow در نظر گرفته میشود.
|
||||
|
||||
یعنی در عمل:
|
||||
|
||||
- ابتدا `slopes` محاسبه میشود
|
||||
- سپس اختلاف شیبها بررسی میشود
|
||||
- بیشترین تغییر شیب به عنوان elbow انتخاب میشود
|
||||
|
||||
خروجی:
|
||||
|
||||
- `optimal_k`
|
||||
|
||||
---
|
||||
|
||||
## 6.3) تولید centroidهای نهایی
|
||||
|
||||
بعد از پیدا شدن `optimal_k`:
|
||||
|
||||
- مدل `KMeans` همان `K` نهایی انتخاب میشود
|
||||
- مختصات مراکز خوشهها (`cluster_centers_`) گرفته میشود
|
||||
- از فضای متری به `lat/lon` تبدیل میشود
|
||||
- در `centroid_points` ذخیره میشود
|
||||
|
||||
نمونه centroid:
|
||||
|
||||
```json
|
||||
{
|
||||
"sub_block_code": "sub-block-1",
|
||||
"centroid_lat": 35.689321,
|
||||
"centroid_lon": 51.389789
|
||||
}
|
||||
```
|
||||
|
||||
این centroidها در عمل همان مراکز بخشهای کوچکتر زمین هستند.
|
||||
|
||||
---
|
||||
|
||||
## 7) تولید و ذخیره نمودار Elbow
|
||||
|
||||
### تابع
|
||||
|
||||
- `render_elbow_plot()`
|
||||
|
||||
### منطق
|
||||
|
||||
پس از محاسبه `inertia_curve` و `optimal_k`:
|
||||
|
||||
1. نمودار `K` در برابر `SSE` رسم میشود
|
||||
2. نقطه elbow با رنگ قرمز مشخص میشود
|
||||
3. تصویر به صورت PNG در `BytesIO` ذخیره میشود
|
||||
4. با `ContentFile` به `ImageField` مدل `BlockSubdivision` داده میشود
|
||||
|
||||
### نکته مهم حافظه
|
||||
|
||||
برای جلوگیری از memory leak:
|
||||
|
||||
- از backend غیرتعاملی `Agg` استفاده میشود
|
||||
- بعد از ذخیره تصویر، `plt.close(fig)` اجرا میشود
|
||||
- buffer هم بسته میشود
|
||||
|
||||
این برای پردازشهای همزمان سرور ضروری است.
|
||||
|
||||
---
|
||||
|
||||
## 8) جریان کامل `POST /api/soil-data/`
|
||||
|
||||
این endpoint الان مهمترین ورودی subdivision است.
|
||||
|
||||
### ورودیهای قابل پشتیبانی
|
||||
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_count`
|
||||
- `block_code`
|
||||
- `farm_boundary`
|
||||
|
||||
### سناریوی اجرا
|
||||
|
||||
#### مرحله 1: اعتبارسنجی ورودی
|
||||
|
||||
سریالایزر `SoilDataRequestSerializer` داده را validate میکند.
|
||||
|
||||
#### مرحله 2: پیدا کردن یا ساخت location
|
||||
|
||||
بر اساس `lat/lon`:
|
||||
|
||||
- اگر location وجود نداشته باشد ساخته میشود
|
||||
- اگر وجود داشته باشد از همان رکورد استفاده میشود
|
||||
|
||||
#### مرحله 3: آپدیت ساختار اولیه بلوکها
|
||||
|
||||
اگر `block_count` فرق کرده باشد:
|
||||
|
||||
- `block_layout` دوباره با `set_input_block_count()` ساخته میشود
|
||||
|
||||
#### مرحله 4: انتخاب boundary برای subdivision
|
||||
|
||||
اولویت:
|
||||
|
||||
1. `farm_boundary` ارسالی در request
|
||||
2. اگر نبود، `location.farm_boundary` ذخیرهشده
|
||||
|
||||
#### مرحله 5: اجرای subdivision فقط در صورت نیاز
|
||||
|
||||
تابع:
|
||||
|
||||
- `create_or_get_block_subdivision()`
|
||||
|
||||
اگر رکورد `(location, block_code)` از قبل وجود داشته باشد:
|
||||
|
||||
- هیچ پردازش جدیدی اجرا نمیشود
|
||||
- همان رکورد قبلی برگردانده میشود
|
||||
|
||||
اگر وجود نداشته باشد:
|
||||
|
||||
- grid ساخته میشود
|
||||
- KMeans اجرا میشود
|
||||
- elbow پیدا میشود
|
||||
- centroidها ساخته میشوند
|
||||
- نمودار elbow ساخته میشود
|
||||
- همه چیز در `BlockSubdivision` ذخیره میشود
|
||||
- `block_layout` با `sub_blocks` sync میشود
|
||||
|
||||
#### مرحله 6: response
|
||||
|
||||
خروجی شامل اینهاست:
|
||||
|
||||
- اطلاعات `SoilLocation`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
- `block_subdivisions`
|
||||
- `depths`
|
||||
|
||||
فیلد `source` در response:
|
||||
|
||||
- `created` اگر location یا subdivision جدید ساخته شده باشد
|
||||
- `database` اگر قبلاً وجود داشته باشد
|
||||
|
||||
---
|
||||
|
||||
## 9) جریان کامل `GET /api/soil-data/`
|
||||
|
||||
این endpoint الان فقط برای read استفاده میشود.
|
||||
|
||||
### ورودی
|
||||
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_code` اختیاری
|
||||
|
||||
### رفتار
|
||||
|
||||
- location را از دیتابیس پیدا میکند
|
||||
- subdivisionهای ذخیرهشده را میخواند
|
||||
- هیچ الگوریتمی را اجرا نمیکند
|
||||
- هیچ `KMeans` یا پردازش هندسی انجام نمیدهد
|
||||
|
||||
### پاسخ
|
||||
|
||||
داده ذخیرهشده را با `source = database` برمیگرداند.
|
||||
|
||||
اگر location پیدا نشود:
|
||||
|
||||
- `404`
|
||||
|
||||
---
|
||||
|
||||
## 10) نقش `serializers.py`
|
||||
|
||||
### `SoilDataRequestSerializer`
|
||||
|
||||
ورودی endpoint اصلی را مدیریت میکند:
|
||||
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_count`
|
||||
- `block_code`
|
||||
- `farm_boundary`
|
||||
|
||||
### `SoilLocationResponseSerializer`
|
||||
|
||||
خروجی location را برمیگرداند:
|
||||
|
||||
- `id`
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `input_block_count`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
- `block_subdivisions`
|
||||
- `depths`
|
||||
|
||||
### `BlockSubdivisionSerializer`
|
||||
|
||||
خروجی subdivision را برمیگرداند:
|
||||
|
||||
- `block_code`
|
||||
- `chunk_size_sqm`
|
||||
- `grid_points`
|
||||
- `centroid_points`
|
||||
- `grid_point_count`
|
||||
- `centroid_count`
|
||||
- `elbow_plot`
|
||||
- `status`
|
||||
- `metadata`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
---
|
||||
|
||||
## 11) نقش `block_layout` در کنار `BlockSubdivision`
|
||||
|
||||
در معماری فعلی دو سطح ذخیرهسازی داریم:
|
||||
|
||||
### 11.1) `BlockSubdivision`
|
||||
|
||||
منبع اصلی و canonical برای subdivision
|
||||
|
||||
### 11.2) `block_layout`
|
||||
|
||||
خلاصهای از نتیجه subdivision برای مصرف سریعتر در response و ساختار کلی location
|
||||
|
||||
یعنی:
|
||||
|
||||
- داده دقیق در `BlockSubdivision` است
|
||||
- خلاصه آن در `block_layout.blocks[].sub_blocks` قرار میگیرد
|
||||
|
||||
---
|
||||
|
||||
## 12) وضعیت فعلی بخشهای قدیمیتر اپ
|
||||
|
||||
## 12.1) `tasks.py`
|
||||
|
||||
این فایل هنوز وجود دارد و برای fetch داده خاک به صورت قدیمی استفاده میشود، اما در مسیر subdivision فعلی نقشی ندارد.
|
||||
|
||||
## 12.2) `soil_adapters.py`
|
||||
|
||||
این فایل adapterهای داده خاک را نگه میدارد و فعلاً برای subdivision استفاده نمیشود.
|
||||
|
||||
## 12.3) `remote_sensing.py`
|
||||
|
||||
منطق سنجشازدور را نگه میدارد و هنوز مستقیماً به subdivision وصل نشده است.
|
||||
|
||||
## 12.4) `ndvi.py`
|
||||
|
||||
برای endpoint مربوط به NDVI استفاده میشود و فعلاً از centroidهای subdivision استفاده نمیکند.
|
||||
|
||||
---
|
||||
|
||||
## 13) وابستگیهای جدید
|
||||
|
||||
برای عملکرد فعلی subdivision این dependencyها لازم هستند:
|
||||
|
||||
- `scikit-learn`
|
||||
- `matplotlib`
|
||||
- `Pillow`
|
||||
- `numpy`
|
||||
|
||||
### دلیل هرکدام
|
||||
|
||||
- `scikit-learn`: اجرای `KMeans`
|
||||
- `matplotlib`: رسم elbow plot
|
||||
- `Pillow`: پشتیبانی از `ImageField`
|
||||
- `numpy`: وابستگی پایه `scikit-learn`
|
||||
|
||||
---
|
||||
|
||||
## 14) migrationهای مهم مرتبط با ساختار فعلی
|
||||
|
||||
- `0008_soillocation_block_layout.py`
|
||||
- اضافه شدن `input_block_count`
|
||||
- اضافه شدن `block_layout`
|
||||
|
||||
- `0009_blocksubdivision.py`
|
||||
- اضافه شدن مدل `BlockSubdivision`
|
||||
|
||||
- `0010_blocksubdivision_elbow_plot.py`
|
||||
- اضافه شدن فیلد `elbow_plot`
|
||||
|
||||
---
|
||||
|
||||
## 15) محدودیتهای فعلی
|
||||
|
||||
چند محدودیت مهم در پیادهسازی فعلی وجود دارد:
|
||||
|
||||
- subdivision فعلاً بر اساس هندسه و خوشهبندی نقاط انجام میشود، نه بر اساس داده واقعی خاک یا NDVI
|
||||
- برای هر `block_code` فرض میشود یک مرز مستقل از بیرون داده میشود
|
||||
- هنوز برای هر `sub_block` رکورد location مستقل ساخته نمیشود
|
||||
- هنوز داده خاک، هوا و NDVI برای centroidهای جدید به صورت جداگانه fetch نمیشود
|
||||
- elbow detection فعلی heuristic-based است و هنوز نسخه پیشرفتهتر آماری ندارد
|
||||
|
||||
---
|
||||
|
||||
## 16) تستهای مرتبط
|
||||
|
||||
### `location_data/test_block_subdivision.py`
|
||||
|
||||
این تستها بررسی میکنند:
|
||||
|
||||
- elbow detection کار میکند
|
||||
- payload subdivision ساخته میشود
|
||||
- grid points و centroid points خروجی دارند
|
||||
|
||||
### `location_data/test_soil_api.py`
|
||||
|
||||
این تستها بررسی میکنند:
|
||||
|
||||
- `POST` subdivision جدید میسازد
|
||||
- `GET` فقط داده ذخیرهشده را برمیگرداند
|
||||
- الگوریتم در `GET` دوباره اجرا نمیشود
|
||||
|
||||
---
|
||||
|
||||
## 17) جمعبندی معماری فعلی
|
||||
|
||||
در وضعیت فعلی، `location_data` این معماری را دارد:
|
||||
|
||||
### لایه 1: Location پایه
|
||||
|
||||
- `SoilLocation`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
|
||||
### لایه 2: Subdivision هوشمند
|
||||
|
||||
- `BlockSubdivision`
|
||||
- grid generation
|
||||
- KMeans
|
||||
- elbow detection
|
||||
- centroid generation
|
||||
- elbow plot generation
|
||||
|
||||
### لایه 3: دادههای مکمل
|
||||
|
||||
- `SoilDepthData`
|
||||
- `NdviObservation`
|
||||
- بخشهای legacy مثل `tasks.py`
|
||||
|
||||
در نتیجه، اپ الان میتواند:
|
||||
|
||||
- یک بلوک با مرز مشخص بگیرد
|
||||
- آن را به نقاط شبکهای خرد کند
|
||||
- تعداد بهینه بخشها را با KMeans + Elbow پیدا کند
|
||||
- centroidهای نهایی را ذخیره کند
|
||||
- نمودار elbow را ذخیره کند
|
||||
- و در درخواستهای بعدی فقط همان نتیجه ذخیرهشده را بدون پردازش مجدد برگرداند
|
||||
|
||||
---
|
||||
|
||||
## 18) پیشنهاد برای مراحل بعدی
|
||||
|
||||
اگر در مرحله بعد بخواهی این ساختار را توسعه بدهی، منطقیترین قدمها اینها هستند:
|
||||
|
||||
1. ساخت endpoint مستقل برای subdivision هر block
|
||||
2. اتصال هر centroid به fetch داده خاک و هوا
|
||||
3. ساخت رکورد مستقل برای هر `sub_block`
|
||||
4. استفاده از NDVI یا داده سنسور برای تعیین `K` یا وزندهی خوشهها
|
||||
5. نمایش مستقیم `elbow_plot` با URL کامل media
|
||||
|
||||
@@ -0,0 +1,972 @@
|
||||
# مستند کامل اپ `location_data`
|
||||
|
||||
این سند، وضعیت فعلی اپ `location_data` را به صورت کامل توضیح میدهد:
|
||||
|
||||
- مدلهای داده
|
||||
- منطق business
|
||||
- جریان ساخت location و block
|
||||
- subdivision و خوشهبندی
|
||||
- تولید analysis grid
|
||||
- سنجشازدور با openEO
|
||||
- تسکهای Celery
|
||||
- APIهای فعلی
|
||||
- ساختار responseها
|
||||
- محدودیتها و فرضیات فعلی
|
||||
|
||||
این فایل بر اساس کد فعلی پروژه نوشته شده است و هدفش این است که یک مرجع فنی برای توسعهدهندههای بعدی باشد.
|
||||
|
||||
---
|
||||
|
||||
## 1) هدف اپ `location_data`
|
||||
|
||||
اپ `location_data` در وضعیت فعلی چند نقش اصلی دارد:
|
||||
|
||||
1. نگهداری موقعیت جغرافیایی زمین با `lat/lon`
|
||||
2. نگهداری مرز زمین یا مرز blockها
|
||||
3. ساخت ساختار blockهای مزرعه
|
||||
4. اجرای subdivision برای blockها
|
||||
5. تولید grid analysis با ابعاد 30x30 متر
|
||||
6. نگهداری نتایج سنجشازدور روی هر grid cell
|
||||
7. نگهداری دادههای خاک و NDVI سنتی
|
||||
8. فراهم کردن API برای:
|
||||
- location data
|
||||
- subdivision
|
||||
- remote sensing trigger/result
|
||||
- NDVI health
|
||||
|
||||
به صورت خلاصه، `location_data` الان فقط یک جدول مختصات نیست؛ بلکه هاب مکانی پروژه است.
|
||||
|
||||
---
|
||||
|
||||
## 2) مفاهیم اصلی دامنه
|
||||
|
||||
### 2.1) SoilLocation
|
||||
|
||||
`SoilLocation` نماینده یک location اصلی برای یک مزرعه یا مرکز زمین است.
|
||||
|
||||
این مدل:
|
||||
- مختصات `latitude` و `longitude` را نگه میدارد
|
||||
- `farm_boundary` را ذخیره میکند
|
||||
- تعداد blockهای اولیه را نگه میدارد
|
||||
- `block_layout` را نگه میدارد
|
||||
- مبنای ارتباط با:
|
||||
- `SoilDepthData`
|
||||
- `BlockSubdivision`
|
||||
- `AnalysisGridCell`
|
||||
- `RemoteSensingRun`
|
||||
- `NdviObservation`
|
||||
|
||||
---
|
||||
|
||||
### 2.2) BlockSubdivision
|
||||
|
||||
`BlockSubdivision` نتیجه خردسازی یک block است.
|
||||
|
||||
این مدل نگه میدارد:
|
||||
- block code
|
||||
- مرز همان block
|
||||
- chunk size برای subdivision
|
||||
- grid points اولیه
|
||||
- centroid points نهایی
|
||||
- elbow plot
|
||||
- metadata الگوریتم
|
||||
|
||||
این مدل برای مرحلهای است که یک block را به بخشهای کوچکتر تقسیم میکنیم.
|
||||
|
||||
---
|
||||
|
||||
### 2.3) AnalysisGridCell
|
||||
|
||||
`AnalysisGridCell` سلولهای 30x30 متری تحلیل سنجشازدور را نگه میدارد.
|
||||
|
||||
هر cell:
|
||||
- به یک `SoilLocation` وصل است
|
||||
- در صورت نیاز به یک `BlockSubdivision` وصل است
|
||||
- یک `cell_code` یکتا دارد
|
||||
- geometry خودش را به صورت Polygon نگه میدارد
|
||||
- centroid خودش را نگه میدارد
|
||||
|
||||
این مدل واحد اصلی تحلیل remote sensing است.
|
||||
|
||||
---
|
||||
|
||||
### 2.4) AnalysisGridObservation
|
||||
|
||||
`AnalysisGridObservation` داده زمانی هر سلول را نگه میدارد.
|
||||
|
||||
برای هر cell و بازه زمانی:
|
||||
- `ndvi`
|
||||
- `ndwi`
|
||||
- `lst_c`
|
||||
- `soil_vv`
|
||||
- `soil_vv_db`
|
||||
- `dem_m`
|
||||
- `slope_deg`
|
||||
|
||||
ذخیره میشود.
|
||||
|
||||
این مدل cache دیتابیسی اصلی برای نتایج openEO است.
|
||||
|
||||
---
|
||||
|
||||
### 2.5) RemoteSensingRun
|
||||
|
||||
`RemoteSensingRun` وضعیت یک اجرای async سنجشازدور را نگه میدارد.
|
||||
|
||||
این مدل:
|
||||
- به `SoilLocation` وصل است
|
||||
- optionally به `BlockSubdivision` وصل است
|
||||
- `block_code` و بازه زمانی را نگه میدارد
|
||||
- status execution را نگه میدارد
|
||||
- metadata مربوط به task/backend/result summary را نگه میدارد
|
||||
|
||||
این مدل برای tracking jobها در Celery استفاده میشود.
|
||||
|
||||
---
|
||||
|
||||
### 2.6) SoilDepthData
|
||||
|
||||
این مدل دادههای خاک را در عمقهای مختلف نگه میدارد:
|
||||
- `0-5cm`
|
||||
- `5-15cm`
|
||||
- `15-30cm`
|
||||
|
||||
---
|
||||
|
||||
### 2.7) NdviObservation
|
||||
|
||||
این مدل نگهدارنده NDVI سنتی است که جدا از workflow جدید openEO هم هنوز وجود دارد.
|
||||
|
||||
---
|
||||
|
||||
## 3) ساختار فایلهای مهم اپ
|
||||
|
||||
```text
|
||||
location_data/
|
||||
├── admin.py
|
||||
├── apps.py
|
||||
├── block_subdivision.py
|
||||
├── grid_analysis.py
|
||||
├── models.py
|
||||
├── ndvi.py
|
||||
├── openeo_service.py
|
||||
├── remote_sensing.py
|
||||
├── serializers.py
|
||||
├── soil_adapters.py
|
||||
├── tasks.py
|
||||
├── urls.py
|
||||
├── views.py
|
||||
├── migrations/
|
||||
└── tests...
|
||||
```
|
||||
|
||||
### نقش فایلها
|
||||
|
||||
- `models.py`: مدلهای اصلی
|
||||
- `serializers.py`: serializerهای API
|
||||
- `views.py`: endpointهای DRF
|
||||
- `urls.py`: routeها
|
||||
- `tasks.py`: تسکهای Celery
|
||||
- `block_subdivision.py`: subdivision و elbow/kmeans
|
||||
- `grid_analysis.py`: ساخت analysis grid cells
|
||||
- `openeo_service.py`: لایه سرویس openEO
|
||||
- `remote_sensing.py`: منطق قدیمیتر سنجشازدور/NDVI ساده
|
||||
- `soil_adapters.py`: adapterهای داده خاک
|
||||
|
||||
---
|
||||
|
||||
## 4) تنظیمات مهم
|
||||
|
||||
### `SUBDIVISION_CHUNK_SQM`
|
||||
|
||||
در `config/settings.py`:
|
||||
|
||||
```python
|
||||
SUBDIVISION_CHUNK_SQM = int(os.environ.get("SUBDIVISION_CHUNK_SQM", "900"))
|
||||
```
|
||||
|
||||
مقدار پیشفرض فعلی:
|
||||
- `900`
|
||||
|
||||
معنا:
|
||||
- grid analysis با سلولهای `30m x 30m`
|
||||
|
||||
چون:
|
||||
|
||||
```text
|
||||
step_m = sqrt(900) = 30m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) مدلهای دیتابیس و منطق آنها
|
||||
|
||||
## 5.1) SoilLocation
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
- `latitude`
|
||||
- `longitude`
|
||||
- `task_id`
|
||||
- `farm_boundary`
|
||||
- `input_block_count`
|
||||
- `block_layout`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
### قید مهم
|
||||
|
||||
- `latitude + longitude` یکتا هستند
|
||||
|
||||
### block_layout
|
||||
|
||||
`block_layout` JSON summary کلی blockها را نگه میدارد.
|
||||
|
||||
نمونه:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_block_count": 1,
|
||||
"default_full_farm": true,
|
||||
"algorithm_status": "completed",
|
||||
"blocks": [
|
||||
{
|
||||
"block_code": "block-1",
|
||||
"order": 1,
|
||||
"source": "default",
|
||||
"needs_subdivision": true,
|
||||
"sub_blocks": [
|
||||
{
|
||||
"sub_block_code": "sub-block-1",
|
||||
"centroid_lat": 35.689123,
|
||||
"centroid_lon": 51.389456
|
||||
}
|
||||
],
|
||||
"subdivision_summary": {
|
||||
"chunk_size_sqm": 900,
|
||||
"grid_point_count": 12,
|
||||
"centroid_count": 3,
|
||||
"optimal_k": 3
|
||||
},
|
||||
"analysis_grid_summary": {
|
||||
"chunk_size_sqm": 900,
|
||||
"cell_count": 18
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`block_layout` canonical source نیست؛ بیشتر یک summary سریع برای API است.
|
||||
|
||||
---
|
||||
|
||||
## 5.2) BlockSubdivision
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
- `soil_location`
|
||||
- `block_code`
|
||||
- `source_boundary`
|
||||
- `chunk_size_sqm`
|
||||
- `grid_points`
|
||||
- `centroid_points`
|
||||
- `grid_point_count`
|
||||
- `centroid_count`
|
||||
- `elbow_plot`
|
||||
- `status`
|
||||
- `metadata`
|
||||
|
||||
### نقش
|
||||
|
||||
برای هر `block_code` در هر location، نتیجه subdivision در این مدل ذخیره میشود.
|
||||
|
||||
### metadata
|
||||
|
||||
شامل مواردی مثل:
|
||||
- `estimated_area_sqm`
|
||||
- `optimal_k`
|
||||
- `inertia_curve`
|
||||
- `analysis_grid`
|
||||
|
||||
---
|
||||
|
||||
## 5.3) RemoteSensingRun
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
- `soil_location`
|
||||
- `block_subdivision`
|
||||
- `block_code`
|
||||
- `provider`
|
||||
- `chunk_size_sqm`
|
||||
- `temporal_start`
|
||||
- `temporal_end`
|
||||
- `status`
|
||||
- `metadata`
|
||||
- `error_message`
|
||||
- `started_at`
|
||||
- `finished_at`
|
||||
|
||||
### statusها
|
||||
|
||||
- `pending`
|
||||
- `running`
|
||||
- `success`
|
||||
- `failure`
|
||||
|
||||
### نقش
|
||||
|
||||
این جدول وضعیت اجرای async را نگه میدارد.
|
||||
|
||||
---
|
||||
|
||||
## 5.4) AnalysisGridCell
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
- `soil_location`
|
||||
- `block_subdivision`
|
||||
- `block_code`
|
||||
- `cell_code`
|
||||
- `chunk_size_sqm`
|
||||
- `geometry`
|
||||
- `centroid_lat`
|
||||
- `centroid_lon`
|
||||
|
||||
### نقش
|
||||
|
||||
واحد spatial اصلی برای تحلیل remote sensing است.
|
||||
|
||||
### idempotency
|
||||
|
||||
سطح سرویس با این شرط enforce میشود:
|
||||
- اگر برای یک `SoilLocation + block_code + chunk_size_sqm` cellها قبلاً ساخته شده باشند، دوباره ساخته نمیشوند.
|
||||
|
||||
### geometry
|
||||
|
||||
به صورت GeoJSON-like polygon ذخیره میشود.
|
||||
|
||||
---
|
||||
|
||||
## 5.5) AnalysisGridObservation
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
- `cell`
|
||||
- `run`
|
||||
- `temporal_start`
|
||||
- `temporal_end`
|
||||
- `ndvi`
|
||||
- `ndwi`
|
||||
- `lst_c`
|
||||
- `soil_vv`
|
||||
- `soil_vv_db`
|
||||
- `dem_m`
|
||||
- `slope_deg`
|
||||
- `metadata`
|
||||
|
||||
### uniqueness
|
||||
|
||||
برای جلوگیری از duplicate:
|
||||
- روی `cell + temporal_start + temporal_end` constraint داریم.
|
||||
|
||||
این باعث میشود cache دیتابیسی پایدار باشد.
|
||||
|
||||
---
|
||||
|
||||
## 5.6) SoilDepthData
|
||||
|
||||
این مدل دادههای خاک را در عمقهای مختلف نگه میدارد.
|
||||
|
||||
هنوز به صورت مستقیم برای هر analysis grid cell جداگانه استفاده نشده است.
|
||||
|
||||
---
|
||||
|
||||
## 5.7) NdviObservation
|
||||
|
||||
این مدل legacy / parallel NDVI store است.
|
||||
|
||||
workflow جدید openEO جایگزین آن نشده، بلکه کنار آن وجود دارد.
|
||||
|
||||
---
|
||||
|
||||
## 6) منطق subdivision در `block_subdivision.py`
|
||||
|
||||
این فایل مسئول خردسازی blockها است.
|
||||
|
||||
### کارهایی که انجام میدهد
|
||||
|
||||
- استخراج polygon از boundary
|
||||
- تبدیل مختصات جغرافیایی به مختصات محلی متری
|
||||
- تولید grid points اولیه
|
||||
- اجرای KMeans برای `K=1..10`
|
||||
- محاسبه SSE/Inertia
|
||||
- پیدا کردن elbow point
|
||||
- انتخاب centroidها
|
||||
- رسم elbow plot با matplotlib
|
||||
- ذخیره plot در `ImageField`
|
||||
- sync کردن نتیجه با `block_layout`
|
||||
|
||||
### input
|
||||
|
||||
ممکن است boundary به شکلهای زیر برسد:
|
||||
- GeoJSON Polygon
|
||||
- corners
|
||||
- list مستقیم از points
|
||||
|
||||
### خروجی
|
||||
|
||||
- centroidهای نهایی block
|
||||
- metadata الگوریتم
|
||||
- elbow plot
|
||||
|
||||
---
|
||||
|
||||
## 7) منطق ساخت analysis grid در `grid_analysis.py`
|
||||
|
||||
این فایل مسئول تولید سلولهای 30x30 متری برای تحلیل remote sensing است.
|
||||
|
||||
### تابع اصلی
|
||||
|
||||
- `create_or_get_analysis_grid_cells(...)`
|
||||
|
||||
### ورودیها
|
||||
|
||||
- `location`
|
||||
- optional `boundary`
|
||||
- optional `block_code`
|
||||
- optional `block_subdivision`
|
||||
- optional `chunk_size_sqm`
|
||||
|
||||
### رفتار
|
||||
|
||||
1. chunk size را تعیین میکند
|
||||
2. boundary را resolve میکند
|
||||
3. polygon را extract میکند
|
||||
4. اگر قبلاً برای همان `location + block_code + chunk_size` cell ساخته شده باشد، خروجی existing برمیگرداند
|
||||
5. اگر نه، grid cellها ساخته میشوند و `AnalysisGridCell` ذخیره میشود
|
||||
|
||||
### نحوه ساخت شبکه
|
||||
|
||||
- polygon به دستگاه محلی متری تبدیل میشود
|
||||
- `step_m = sqrt(chunk_size_sqm)` محاسبه میشود
|
||||
- یک grid مستطیلی روی bounding box ساخته میشود
|
||||
- هر cell که با polygon intersect داشته باشد نگه داشته میشود
|
||||
|
||||
### cell_code
|
||||
|
||||
فرمت فعلی deterministic است:
|
||||
|
||||
```text
|
||||
loc-{location_id}__block-{block_code}__chunk-{chunk_size_sqm}__rXXXXcYYYY
|
||||
```
|
||||
|
||||
### metadata summary
|
||||
|
||||
پس از ساخت grid:
|
||||
- روی `BlockSubdivision.metadata["analysis_grid"]`
|
||||
- و روی `SoilLocation.block_layout`
|
||||
|
||||
summary ذخیره میشود.
|
||||
|
||||
---
|
||||
|
||||
## 8) منطق openEO در `openeo_service.py`
|
||||
|
||||
این فایل لایه service اصلی برای تحلیل openEO است.
|
||||
|
||||
### backend
|
||||
|
||||
```text
|
||||
https://openeofed.dataspace.copernicus.eu
|
||||
```
|
||||
|
||||
### هدف
|
||||
|
||||
گرفتن batch metricها برای مجموعهای از `AnalysisGridCell`ها.
|
||||
|
||||
### جریان کلی
|
||||
|
||||
1. اتصال و auth به openEO
|
||||
2. ساخت `FeatureCollection` از cellها
|
||||
3. ساخت `spatial_extent`
|
||||
4. اجرای یک job per metric روی همه cellها
|
||||
5. parse کردن نتیجه aggregate_spatial
|
||||
6. merge کردن metricها روی map keyed by `cell_code`
|
||||
|
||||
### metricهای فعلی
|
||||
|
||||
- `ndvi` از `SENTINEL2_L2A`
|
||||
- `ndwi` از `SENTINEL2_L2A`
|
||||
- `lst_c` از `SENTINEL3_SLSTR_L2_LST`
|
||||
- `soil_vv` از `SENTINEL1_GRD`
|
||||
- `soil_vv_db` در Python از `soil_vv`
|
||||
- `dem_m` از `COPERNICUS_30`
|
||||
- `slope_deg` از DEM اگر backend پشتیبانی کند
|
||||
|
||||
### cloud mask Sentinel-2
|
||||
|
||||
کلاسهای معتبر SCL:
|
||||
- `4`
|
||||
- `5`
|
||||
- `6`
|
||||
|
||||
نکته مهم:
|
||||
- از `isin()` استفاده نمیشود
|
||||
- فقط logical comparison استفاده میشود
|
||||
|
||||
### aggregate_spatial
|
||||
|
||||
فقط از:
|
||||
|
||||
```python
|
||||
aggregate_spatial(geometries=feature_collection, reducer="mean")
|
||||
```
|
||||
|
||||
استفاده میشود.
|
||||
|
||||
### slope support
|
||||
|
||||
اگر backend `slope()` را پشتیبانی نکند:
|
||||
- `slope_deg = null`
|
||||
- و metadata میگوید `slope_supported=False`
|
||||
|
||||
### normalized output
|
||||
|
||||
خروجی نهایی به این شکل است:
|
||||
|
||||
```python
|
||||
{
|
||||
"results": {
|
||||
"cell-1": {
|
||||
"ndvi": ...,
|
||||
"ndwi": ...,
|
||||
"lst_c": ...,
|
||||
"soil_vv": ...,
|
||||
"soil_vv_db": ...,
|
||||
"dem_m": ...,
|
||||
"slope_deg": ...,
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"backend": "openeo",
|
||||
"collections_used": [...],
|
||||
"slope_supported": True,
|
||||
"job_refs": {},
|
||||
"failed_metrics": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9) Celery workflow در `tasks.py`
|
||||
|
||||
### تسک قدیمی
|
||||
|
||||
- `fetch_soil_data_task`
|
||||
|
||||
برای خاک legacy است.
|
||||
|
||||
### workflow جدید remote sensing
|
||||
|
||||
تابع/تسکهای اصلی:
|
||||
|
||||
- `run_remote_sensing_analysis(...)`
|
||||
- `run_remote_sensing_analysis_task.delay(...)`
|
||||
|
||||
### ورودی task
|
||||
|
||||
- `soil_location_id`
|
||||
- optional `block_code`
|
||||
- `temporal_start`
|
||||
- `temporal_end`
|
||||
- optional `force_refresh`
|
||||
- optional `run_id`
|
||||
|
||||
### رفتار task
|
||||
|
||||
1. `SoilLocation` را پیدا میکند
|
||||
2. `BlockSubdivision` را اگر لازم باشد resolve میکند
|
||||
3. `RemoteSensingRun` را create/update میکند
|
||||
4. `AnalysisGridCell`ها را ensure میکند
|
||||
5. اگر observation برای همان range قبلاً باشد و `force_refresh=False`، دوباره process نمیکند
|
||||
6. در غیر این صورت، `compute_remote_sensing_metrics()` را صدا میزند
|
||||
7. `AnalysisGridObservation`ها را upsert میکند
|
||||
8. status run را success/failure میکند
|
||||
|
||||
### idempotency
|
||||
|
||||
اگر observation قبلاً برای همان:
|
||||
- cell
|
||||
- temporal_start
|
||||
- temporal_end
|
||||
|
||||
وجود داشته باشد، duplicate ساخته نمیشود.
|
||||
|
||||
### retry behavior
|
||||
|
||||
task روی خطاهای transient مثل:
|
||||
- `OpenEOExecutionError`
|
||||
- `OpenEOServiceError`
|
||||
- request-level failures
|
||||
|
||||
retry میکند.
|
||||
|
||||
روی auth failure retry نمیکند.
|
||||
|
||||
---
|
||||
|
||||
## 10) APIهای فعلی `location_data`
|
||||
|
||||
## 10.1) `GET /api/soil-data/`
|
||||
|
||||
کاربرد:
|
||||
- فقط اطلاعات ذخیرهشده location را برمیگرداند
|
||||
- subdivision را rerun نمیکند
|
||||
|
||||
ورودی:
|
||||
- `lat`
|
||||
- `lon`
|
||||
- optional `block_code`
|
||||
|
||||
خروجی:
|
||||
- location data
|
||||
- block layout
|
||||
- block subdivisions
|
||||
- depths
|
||||
|
||||
---
|
||||
|
||||
## 10.2) `POST /api/soil-data/`
|
||||
|
||||
کاربرد:
|
||||
- `SoilLocation` را create/get میکند
|
||||
- در صورت نیاز `BlockSubdivision` میسازد
|
||||
|
||||
ورودی:
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_count`
|
||||
- `block_code`
|
||||
- `farm_boundary`
|
||||
|
||||
خروجی:
|
||||
- location کامل
|
||||
- `source` = `created` یا `database`
|
||||
|
||||
---
|
||||
|
||||
## 10.3) `GET /api/soil-data/tasks/<task_id>/status/`
|
||||
|
||||
کاربرد:
|
||||
- status task قدیمی fetch خاک
|
||||
|
||||
---
|
||||
|
||||
## 10.4) `POST /api/soil-data/ndvi-health/`
|
||||
|
||||
کاربرد:
|
||||
- NDVI health مستقل برای farm
|
||||
|
||||
---
|
||||
|
||||
## 10.5) `POST /api/soil-data/remote-sensing/`
|
||||
|
||||
کاربرد:
|
||||
- remote sensing analysis را queue میکند
|
||||
- heavy work را sync اجرا نمیکند
|
||||
|
||||
ورودی:
|
||||
- `lat`
|
||||
- `lon`
|
||||
- optional `block_code`
|
||||
- `start_date`
|
||||
- `end_date`
|
||||
- optional `force_refresh`
|
||||
|
||||
رفتار:
|
||||
1. location را پیدا میکند
|
||||
2. run میسازد
|
||||
3. Celery task را enqueue میکند
|
||||
4. `202 Accepted` برمیگرداند
|
||||
|
||||
خروجی شامل:
|
||||
- `status=processing`
|
||||
- `source=processing`
|
||||
- `location`
|
||||
- `block_code`
|
||||
- `chunk_size_sqm`
|
||||
- `temporal_extent`
|
||||
- `summary` خالی
|
||||
- `cells=[]`
|
||||
- `run`
|
||||
- `task_id`
|
||||
|
||||
---
|
||||
|
||||
## 10.6) `GET /api/soil-data/remote-sensing/`
|
||||
|
||||
کاربرد:
|
||||
- فقط cache دیتابیسی remote sensing را میخواند
|
||||
- هیچ openEO یا subdivision sync اجرا نمیکند
|
||||
|
||||
ورودی:
|
||||
- `lat`
|
||||
- `lon`
|
||||
- optional `block_code`
|
||||
- `start_date`
|
||||
- `end_date`
|
||||
|
||||
خروجی حالتها:
|
||||
|
||||
### حالت 1: result موجود است
|
||||
- `status=success`
|
||||
- `source=database`
|
||||
- summary metrics
|
||||
- cells list
|
||||
- run info
|
||||
|
||||
### حالت 2: result هنوز نیست ولی job در حال اجراست
|
||||
- `status=processing`
|
||||
- `source=processing`
|
||||
- summary خالی
|
||||
- cells empty
|
||||
- run info
|
||||
|
||||
### حالت 3: location نیست
|
||||
- `404`
|
||||
|
||||
---
|
||||
|
||||
## 11) serializerهای مهم
|
||||
|
||||
### `SoilDataRequestSerializer`
|
||||
برای endpoint اصلی location.
|
||||
|
||||
### `SoilLocationResponseSerializer`
|
||||
برای بازگشت location + blocks + depths.
|
||||
|
||||
### `BlockSubdivisionSerializer`
|
||||
برای بازگشت subdivision data.
|
||||
|
||||
### `RemoteSensingTriggerSerializer`
|
||||
برای trigger API remote sensing.
|
||||
|
||||
### `RemoteSensingCellObservationSerializer`
|
||||
برای بازگشت per-cell remote sensing metrics.
|
||||
|
||||
### `RemoteSensingSummarySerializer`
|
||||
برای بازگشت summary statisticها.
|
||||
|
||||
### `RemoteSensingRunSerializer`
|
||||
برای بازگشت status run.
|
||||
|
||||
### `RemoteSensingResponseSerializer`
|
||||
برای payload کامل remote sensing GET.
|
||||
|
||||
---
|
||||
|
||||
## 12) منطق summary statistics در remote sensing GET
|
||||
|
||||
در response مربوط به `GET /remote-sensing/` این فیلدها برمیگردند:
|
||||
|
||||
- `cell_count`
|
||||
- `ndvi_mean`
|
||||
- `ndwi_mean`
|
||||
- `lst_c_mean`
|
||||
- `soil_vv_db_mean`
|
||||
- `dem_m_mean`
|
||||
- `slope_deg_mean`
|
||||
|
||||
اینها از روی observationهای موجود در DB محاسبه میشوند، نه از openEO live.
|
||||
|
||||
---
|
||||
|
||||
## 13) admin
|
||||
|
||||
در `admin.py` الان موارد زیر رجیستر شدهاند:
|
||||
|
||||
- `SoilLocation`
|
||||
- `SoilDepthData`
|
||||
- `BlockSubdivision`
|
||||
- `RemoteSensingRun`
|
||||
- `AnalysisGridCell`
|
||||
- `AnalysisGridObservation`
|
||||
|
||||
این باعث میشود debugging و inspection از طریق admin ممکن باشد.
|
||||
|
||||
---
|
||||
|
||||
## 14) تستهای فعلی
|
||||
|
||||
### `test_soil_api.py`
|
||||
- ساخت location
|
||||
- ساخت subdivision
|
||||
- رفتار GET/POST location
|
||||
|
||||
### `test_block_subdivision.py`
|
||||
- elbow detection
|
||||
- payload subdivision
|
||||
|
||||
### `test_grid_analysis.py`
|
||||
- ساخت analysis grid 30x30
|
||||
- idempotency grid cells
|
||||
- استفاده از boundary location
|
||||
|
||||
### `test_openeo_service.py`
|
||||
- parse نتیجه aggregate_spatial
|
||||
- merge metricها
|
||||
- conversion به dB
|
||||
|
||||
### `test_remote_sensing_api.py`
|
||||
- queue شدن remote sensing task
|
||||
- processing response
|
||||
- cache read response
|
||||
- not found behavior
|
||||
|
||||
### `test_ndvi_health_api.py`
|
||||
- NDVI health API
|
||||
|
||||
---
|
||||
|
||||
## 15) وابستگیهای مهم
|
||||
|
||||
در `requirements.txt` dependencyهای مهم این بخشها شامل اینها هستند:
|
||||
|
||||
- `scikit-learn`
|
||||
- `matplotlib`
|
||||
- `Pillow`
|
||||
- `numpy`
|
||||
- `openeo`
|
||||
|
||||
### نقش آنها
|
||||
|
||||
- `scikit-learn`: KMeans
|
||||
- `matplotlib`: elbow plot
|
||||
- `Pillow`: ImageField support
|
||||
- `numpy`: وابستگی عددی
|
||||
- `openeo`: ارتباط با backend سنجشازدور
|
||||
|
||||
---
|
||||
|
||||
## 16) migrationهای مهم
|
||||
|
||||
- `0008_soillocation_block_layout.py`
|
||||
- `0009_blocksubdivision.py`
|
||||
- `0010_blocksubdivision_elbow_plot.py`
|
||||
- `0011_remote_sensing_models.py`
|
||||
|
||||
این migrationها ساختار فعلی location/subdivision/remote sensing را ساختهاند.
|
||||
|
||||
---
|
||||
|
||||
## 17) محدودیتها و فرضیات فعلی
|
||||
|
||||
### محدودیتهای فعلی
|
||||
|
||||
1. `block_layout` canonical source نیست و summary است.
|
||||
2. subdivision و analysis grid دو لایه جدا هستند.
|
||||
3. slope ممکن است روی backend همیشه پشتیبانی نشود.
|
||||
4. API GET remote-sensing فقط cache میخواند.
|
||||
5. هنوز endpoint مجزای status run نداریم.
|
||||
6. grid generation از projection محلی استفاده میکند، نه GIS stack سنگین.
|
||||
7. openEO calls فعلاً برای batch metric processing طراحی شدهاند، نه orchestration پیچیده job lifecycle.
|
||||
|
||||
### فرضیات
|
||||
|
||||
1. مزرعهها آنقدر کوچک هستند که local projected approximation مناسب باشد.
|
||||
2. `SUBDIVISION_CHUNK_SQM=900` برای workflow فعلی درست است.
|
||||
3. `cell_code` deterministic بودن برای idempotency کافی است.
|
||||
4. `AnalysisGridObservation` cache اصلی remote sensing است.
|
||||
|
||||
---
|
||||
|
||||
## 18) جریان کامل داده از ابتدا تا نتیجه remote sensing
|
||||
|
||||
### مرحله 1: ایجاد location
|
||||
|
||||
کاربر `POST /api/soil-data/` را صدا میزند.
|
||||
|
||||
نتیجه:
|
||||
- `SoilLocation` ساخته میشود
|
||||
- `farm_boundary` ذخیره میشود
|
||||
- block layout ساخته میشود
|
||||
- در صورت نیاز `BlockSubdivision` ساخته میشود
|
||||
|
||||
### مرحله 2: تولید analysis grid
|
||||
|
||||
وقتی task remote sensing اجرا میشود:
|
||||
- اگر cellها قبلاً نباشند، `AnalysisGridCell`ها ساخته میشوند
|
||||
|
||||
### مرحله 3: اجرای openEO
|
||||
|
||||
Celery task:
|
||||
- FeatureCollection از cellها میسازد
|
||||
- metricها را batch اجرا میکند
|
||||
- نتیجه را parse میکند
|
||||
|
||||
### مرحله 4: ذخیره observation
|
||||
|
||||
برای هر cell:
|
||||
- یک `AnalysisGridObservation` برای بازه زمانی موردنظر ذخیره/آپدیت میشود
|
||||
|
||||
### مرحله 5: بازگشت نتیجه از API
|
||||
|
||||
کاربر `GET /api/soil-data/remote-sensing/` را صدا میزند.
|
||||
|
||||
سیستم:
|
||||
- فقط DB را میخواند
|
||||
- summary میسازد
|
||||
- cells را برمیگرداند
|
||||
|
||||
---
|
||||
|
||||
## 19) پیشنهاد توسعه بعدی
|
||||
|
||||
برای ادامه توسعه، اینها منطقیترین قدمها هستند:
|
||||
|
||||
1. ساخت endpoint مستقل status برای `RemoteSensingRun`
|
||||
2. اضافه کردن pagination برای cell responseها
|
||||
3. اضافه کردن job reference واقعی openEO در metadata
|
||||
4. پشتیبانی از چند resolution دیگر غیر از 30x30
|
||||
5. ساخت serializer/model جدا برای summaryهای precomputed
|
||||
6. اضافه کردن نمودارها یا aggregationهای block-level
|
||||
7. اتصال remote sensing resultها به recommendation engine
|
||||
|
||||
---
|
||||
|
||||
## 20) جمعبندی نهایی
|
||||
|
||||
اپ `location_data` الان یک سیستم چندلایه است:
|
||||
|
||||
### لایه مکانی پایه
|
||||
- `SoilLocation`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
|
||||
### لایه subdivision
|
||||
- `BlockSubdivision`
|
||||
- KMeans
|
||||
- elbow plot
|
||||
|
||||
### لایه grid analysis
|
||||
- `AnalysisGridCell`
|
||||
|
||||
### لایه observation
|
||||
- `AnalysisGridObservation`
|
||||
- `RemoteSensingRun`
|
||||
|
||||
### لایه سرویس
|
||||
- `block_subdivision.py`
|
||||
- `grid_analysis.py`
|
||||
- `openeo_service.py`
|
||||
- `tasks.py`
|
||||
|
||||
### لایه API
|
||||
- `SoilDataView`
|
||||
- `RemoteSensingAnalysisView`
|
||||
- `NdviHealthView`
|
||||
|
||||
در نتیجه، `location_data` الان از یک app ساده location عبور کرده و به یک زیرسیستم کامل spatial + remote sensing تبدیل شده است.
|
||||
@@ -0,0 +1,290 @@
|
||||
# لیست APIهایی که با استفاده از PCSE تحلیل انجام میدهند
|
||||
|
||||
این فایل APIهایی را فهرست میکند که در این پروژه یا مستقیماً از `PCSE` استفاده میکنند، یا بخشی از تحلیلشان به خروجیهای شبیهسازی `PCSE` وابسته است.
|
||||
همچنین مشخص میکند:
|
||||
|
||||
- از چه مدل `PCSE` استفاده میشود
|
||||
- ورودیهای لازم `PCSE` در این پروژه از کجا تأمین میشوند
|
||||
- برنامه آبیاری، برنامه کودهی و داده آبوهوا چطور به مدل تزریق میشوند
|
||||
|
||||
## مدل PCSE مورد استفاده در پروژه
|
||||
|
||||
مدل پیشفرضی که در سرویس شبیهسازی استفاده میشود این است:
|
||||
|
||||
- `Wofost81_NWLP_CWB_CNB`
|
||||
|
||||
توضیح کوتاه:
|
||||
|
||||
- `Wofost81`: نسخه 8.1 از خانواده مدلهای WOFOST
|
||||
- `NWLP`: شبیهسازی با محدودیت نیتروژن
|
||||
- `CWB`: water balance
|
||||
- `CNB`: carbon/nitrogen balance
|
||||
|
||||
بنابراین APIهایی که واقعاً از موتور اصلی شبیهسازی استفاده میکنند، عملاً روی این مدل اجرا میشوند مگر اینکه بعداً در کد override شده باشد.
|
||||
|
||||
## PCSE در این پروژه چه ورودیهایی میخواهد؟
|
||||
|
||||
در این پروژه لایه شبیهسازی برای اجرای `PCSE` این ورودیها را میسازد:
|
||||
|
||||
### 1. `weather`
|
||||
رکوردهای آبوهوا با فیلدهای زیر:
|
||||
|
||||
- `DAY`
|
||||
- `LAT`
|
||||
- `LON`
|
||||
- `ELEV`
|
||||
- `IRRAD`
|
||||
- `TMIN`
|
||||
- `TMAX`
|
||||
- `VAP`
|
||||
- `WIND`
|
||||
- `RAIN`
|
||||
- `E0`
|
||||
- `ES0`
|
||||
- `ET0`
|
||||
|
||||
### 2. `soil`
|
||||
پارامترهای خاک، از جمله:
|
||||
|
||||
- `SMFCF`
|
||||
- `SMW`
|
||||
- `SM0`
|
||||
- `RDMSOL`
|
||||
- `CRAIRC`
|
||||
- `SOPE`
|
||||
- `KSUB`
|
||||
|
||||
و در این پروژه بعضی شاخصهای کمکی هم کنار آن نگهداری میشوند، مثل:
|
||||
|
||||
- `nitrogen`
|
||||
- `phosphorus`
|
||||
- `potassium`
|
||||
- `soil_ph`
|
||||
- `electrical_conductivity`
|
||||
|
||||
### 3. `site_parameters`
|
||||
پارامترهای سایت/شرایط اولیه، از جمله:
|
||||
|
||||
- `WAV`
|
||||
- `SMLIM`
|
||||
- `IFUNRN`
|
||||
- `NOTINF`
|
||||
- `SSI`
|
||||
- `SSMAX`
|
||||
- `NAVAILI`
|
||||
|
||||
### 4. `crop_parameters`
|
||||
پارامترهای محصول. اینها یا از پروفایل شبیهسازی گیاه میآیند، یا اگر موجود نباشند بهصورت default ساخته میشوند. مهمترین defaultها:
|
||||
|
||||
- `crop_name`
|
||||
- `TSUM1`
|
||||
- `TSUM2`
|
||||
- `YIELD_SCALE`
|
||||
- `MAX_LAI`
|
||||
- `MAX_BIOMASS`
|
||||
|
||||
### 5. `agromanagement`
|
||||
تقویم و رویدادهای زراعی، شامل:
|
||||
|
||||
- `CropCalendar`
|
||||
- `TimedEvents`
|
||||
- `StateEvents`
|
||||
|
||||
همین بخش جایی است که برنامه آبیاری و برنامه کودهی به شبیهسازی تزریق میشود.
|
||||
|
||||
## ورودیها از کجا میآیند؟
|
||||
|
||||
### آبوهوا
|
||||
منبع اصلی:
|
||||
|
||||
- جدول `WeatherForecast`
|
||||
|
||||
مسیر تولید:
|
||||
|
||||
- با `farm_uuid` مزرعه پیدا میشود
|
||||
- از `center_location` مزرعه، forecastها خوانده میشوند
|
||||
- معمولاً تا `14` روز آینده برداشته میشوند
|
||||
- دادههای `precipitation` و `et0` که در دیتابیس به `mm/day` هستند، برای `PCSE` به `cm/day` تبدیل میشوند
|
||||
|
||||
فیلدهایی که از forecast استفاده میشوند:
|
||||
|
||||
- `forecast_date` → `DAY`
|
||||
- `temperature_min` → `TMIN`
|
||||
- `temperature_max` یا `temperature_mean` → `TMAX`
|
||||
- `humidity_mean` → `VAP`
|
||||
- `wind_speed_max` → `WIND`
|
||||
- `precipitation` → `RAIN`
|
||||
- `et0` → `E0`, `ES0`, `ET0`
|
||||
|
||||
اگر کاربر خودش `weather` را مستقیم بدهد، همان ورودی مستقیم استفاده میشود.
|
||||
|
||||
### خاک و وضعیت سایت
|
||||
منبع اصلی:
|
||||
|
||||
- جدول `SensorData`
|
||||
- رابطه `center_location.depths`
|
||||
- بخشی از `sensor_payload`
|
||||
|
||||
نحوه ساخت:
|
||||
|
||||
- از لایه سطحی خاک (`top_depth`) پارامترهایی مثل `wv0033`, `wv1500`, `porosity` خوانده میشوند
|
||||
- از روی آنها `SMFCF`, `SMW`, `SM0` ساخته میشود
|
||||
- از `sensor_payload`، شاخصهایی مثل `soil_moisture`, `nitrogen`, `phosphorus`, `potassium`, `soil_ph`, `electrical_conductivity` استخراج میشود
|
||||
- سپس از اینها `soil` و `site_parameters` نهایی ساخته میشود
|
||||
|
||||
### پارامترهای محصول
|
||||
منبع اصلی:
|
||||
|
||||
- مدل `Plant`
|
||||
|
||||
اولویت تأمین:
|
||||
|
||||
1. `simulation profile` داخل یکی از این profileها:
|
||||
- `growth_profile.simulation`
|
||||
- `irrigation_profile.simulation`
|
||||
- `health_profile.simulation`
|
||||
2. اگر profile آماده وجود نداشته باشد، پارامترهای پیشفرض از روی اطلاعات رشد گیاه ساخته میشوند
|
||||
|
||||
### تقویم زراعی `agromanagement`
|
||||
منبع اصلی:
|
||||
|
||||
- اگر در `simulation profile` گیاه موجود باشد، از همان استفاده میشود
|
||||
- وگرنه بهصورت پیشفرض از بازه زمانی آبوهوای موجود ساخته میشود
|
||||
|
||||
ساختار پیشفرض:
|
||||
|
||||
- `crop_start_date` از اولین روز forecast
|
||||
- `crop_end_date` از آخرین روز forecast یا کمی بعد از آن
|
||||
- `TimedEvents` و `StateEvents` بهصورت اولیه خالی هستند
|
||||
|
||||
## 1) APIهای مستقیم و قطعی مبتنی بر PCSE
|
||||
|
||||
### 1. `POST /api/crop-simulation/growth/`
|
||||
- کاربرد: اجرای شبیهسازی رشد گیاه.
|
||||
- نقش PCSE: هسته اصلی این API اجرای مدل شبیهسازی `PCSE/WOFOST` است.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- ورودیها:
|
||||
- آبوهوا: از `WeatherForecast` یا ورودی مستقیم `weather`
|
||||
- خاک: از `SensorData` و `center_location.depths`
|
||||
- crop parameters: از `Plant` و `simulation profile` یا default
|
||||
- agromanagement: از `simulation profile` یا default
|
||||
- نوع استفاده: مستقیم.
|
||||
|
||||
### 2. `GET /api/crop-simulation/growth/<task_id>/status/`
|
||||
- کاربرد: دریافت وضعیت و نتیجه شبیهسازی رشد.
|
||||
- نقش PCSE: نتیجهای که برمیگرداند خروجی همان شبیهسازی مبتنی بر `PCSE` است.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- نوع استفاده: مستقیم.
|
||||
|
||||
### 3. `POST /api/crop-simulation/current-farm-chart/`
|
||||
- کاربرد: تولید chart وضعیت فعلی مزرعه.
|
||||
- نقش PCSE: دادههای chart مثل `LAI`، `TAGP`، `TWSO`، `SM` و `daily_output` از شبیهسازی `PCSE` ساخته میشوند.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- ورودیها:
|
||||
- `farm_uuid`
|
||||
- آبوهوا از `WeatherForecast`
|
||||
- خاک/سایت از `SensorData` و دادههای خاک location
|
||||
- پارامتر گیاه از `Plant`
|
||||
- نوع استفاده: مستقیم.
|
||||
|
||||
### 4. `POST /api/crop-simulation/yield-prediction/`
|
||||
- کاربرد: پیشبینی عملکرد مزرعه.
|
||||
- نقش PCSE: عملکرد پیشبینیشده از خروجی شبیهسازی رشد/خروجیهای `PCSE` استخراج میشود.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- ورودیها: همان ورودیهای `current-farm-chart`
|
||||
- نوع استفاده: مستقیم.
|
||||
|
||||
### 5. `POST /api/crop-simulation/harvest-prediction/`
|
||||
- کاربرد: پیشبینی زمان تقریبی برداشت.
|
||||
- نقش PCSE: با استفاده از `daily_output`، `DVS` و سایر خروجیهای شبیهسازی، زمان رسیدن به برداشت برآورد میشود.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- ورودیها: همان ورودیهای شبیهسازی رشد مزرعه
|
||||
- نوع استفاده: مستقیم.
|
||||
|
||||
### 6. `GET /api/crop-simulation/yield-harvest-summary/`
|
||||
- کاربرد: خلاصه عملکرد و برداشت.
|
||||
- نقش PCSE: چند بخش این API مثل `yield_prediction`، `harvest_prediction_card` و `yield_prediction_chart` از خروجیهای شبیهسازی `PCSE` تغذیه میشوند.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- ورودیها:
|
||||
- خروجی `yield_prediction`
|
||||
- خروجی `harvest_prediction`
|
||||
- خروجی `current-farm-chart`
|
||||
- همگی در نهایت متکی به همان ورودیهای farm/weather/soil/plant هستند
|
||||
- نوع استفاده: مستقیم/ترکیبی.
|
||||
|
||||
### 7. `POST /api/irrigation/water-stress/`
|
||||
- کاربرد: محاسبه شاخص تنش آبی مزرعه.
|
||||
- نقش PCSE: این API از شبیهسازی `crop_simulation` استفاده میکند و شاخص تنش آبی را با تکیه بر خروجیهایی مثل `SM`، `DVS`، `ET0` و `RAIN` میسازد.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- ورودیها:
|
||||
- آبوهوا از `WeatherForecast`
|
||||
- خاک و رطوبت خاک از `SensorData`
|
||||
- پارامتر گیاه از `Plant`
|
||||
- نوع استفاده: مستقیم، ولی شاخص نهایی یک فرمول داخلی روی خروجی شبیهسازی است.
|
||||
|
||||
## 2) APIهایی که بخشی از تحلیلشان ممکن است با PCSE انجام شود
|
||||
|
||||
### 8. `POST /api/irrigation/recommend/`
|
||||
- کاربرد: تولید توصیه آبیاری.
|
||||
- نقش PCSE: در صورت موجود بودن `simulation profile`، داده مزرعه و forecast مناسب، optimizer ابتدا سناریوهای آبیاری را با `PCSE` ارزیابی میکند.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- برنامه آبیاری از کجا میآید؟
|
||||
- ابتدا در خود سیستم چند strategy ساخته میشود
|
||||
- بر اساس `daily_water_needs` و تعداد eventها، برای هر سناریو `irrigation_events` ساخته میشود
|
||||
- این eventها به شکل `TimedEvents` با سیگنال `irrigate` وارد `agromanagement` میشوند
|
||||
- آبوهوا از کجا میآید؟
|
||||
- از forecastهای جدول `WeatherForecast`
|
||||
- سایر ورودیها از کجا میآیند؟
|
||||
- خاک و رطوبت و مواد غذایی: از `SensorData`
|
||||
- گیاه و simulation profile: از `Plant`
|
||||
- نکته: اگر شرایط کافی نباشد، به مسیر heuristic برمیگردد.
|
||||
- نوع استفاده: مشروط/جزئی.
|
||||
|
||||
### 9. `POST /api/fertilization/recommend/`
|
||||
- کاربرد: تولید توصیه کودهی.
|
||||
- نقش PCSE: در صورت موجود بودن `simulation profile` و forecast، سناریوهای کودهی با `PCSE` شبیهسازی و امتیازدهی میشوند.
|
||||
- مدل PCSE: `Wofost81_NWLP_CWB_CNB`
|
||||
- برنامه کودهی از کجا میآید؟
|
||||
- optimizer چند سناریوی کودهی میسازد
|
||||
- برای هر سناریو event کودهی به شکل `TimedEvents`
|
||||
- با سیگنال `apply_n`
|
||||
- و payload شامل `N_amount` و `N_recovery`
|
||||
- وارد `agromanagement` میشود
|
||||
- آبوهوا از کجا میآید؟
|
||||
- از forecastهای جدول `WeatherForecast`
|
||||
- سایر ورودیها از کجا میآیند؟
|
||||
- خاک و وضعیت عناصر از `SensorData`
|
||||
- پروفایل گیاه از `Plant`
|
||||
- نکته: اگر `PCSE` یا داده کافی در دسترس نباشد، fallback heuristic استفاده میشود.
|
||||
- نوع استفاده: مشروط/جزئی.
|
||||
|
||||
## 3) APIهایی که از PCSE استفاده نمیکنند
|
||||
|
||||
این endpointها در همین حوزه هستند اما خودشان تحلیل مبتنی بر `PCSE` انجام نمیدهند:
|
||||
|
||||
- `POST /api/irrigation/plan-from-text/`
|
||||
- `POST /api/fertilization/plan-from-text/`
|
||||
|
||||
این دو بیشتر parser متن آزاد هستند و برنامه را از متن به JSON ساختاریافته تبدیل میکنند.
|
||||
|
||||
## جمعبندی کوتاه
|
||||
|
||||
اگر بخواهیم فقط APIهایی را نام ببریم که واقعاً تحلیل یا شبیهسازی وابسته به `PCSE` دارند، مهمترینها اینها هستند:
|
||||
|
||||
- `POST /api/crop-simulation/growth/`
|
||||
- `GET /api/crop-simulation/growth/<task_id>/status/`
|
||||
- `POST /api/crop-simulation/current-farm-chart/`
|
||||
- `POST /api/crop-simulation/yield-prediction/`
|
||||
- `POST /api/crop-simulation/harvest-prediction/`
|
||||
- `GET /api/crop-simulation/yield-harvest-summary/`
|
||||
- `POST /api/irrigation/water-stress/`
|
||||
- `POST /api/irrigation/recommend/` (مشروط)
|
||||
- `POST /api/fertilization/recommend/` (مشروط)
|
||||
|
||||
## جمعبندی فنی خیلی کوتاه
|
||||
|
||||
- مدل اصلی `PCSE` در این پروژه: `Wofost81_NWLP_CWB_CNB`
|
||||
- آبوهوا عمدتاً از `WeatherForecast` میآید
|
||||
- خاک و رطوبت و بخشی از وضعیت تغذیه از `SensorData` و دادههای خاک location میآید
|
||||
- پارامترهای گیاه و setup شبیهسازی از `Plant` و `simulation profile` میآید
|
||||
- برنامه آبیاری و کودهی در optimizer ساخته میشوند و از طریق `TimedEvents` داخل `agromanagement` به `PCSE` تزریق میشوند
|
||||
@@ -0,0 +1,743 @@
|
||||
# مستند کامل APIهای آپدیتشده مرتبط با PCSE
|
||||
|
||||
این فایل APIهایی را توضیح میدهد که اخیراً آپدیت شدهاند تا بتوانند `برنامه آبیاری` و `برنامه کودهی` را از ورودی بگیرند و به شبیهسازی `PCSE` پاس بدهند.
|
||||
|
||||
APIهای این سند:
|
||||
|
||||
- `POST /api/crop-simulation/growth/`
|
||||
- `GET /api/crop-simulation/growth/<task_id>/status/`
|
||||
- `POST /api/crop-simulation/current-farm-chart/`
|
||||
- `POST /api/crop-simulation/yield-prediction/`
|
||||
- `POST /api/crop-simulation/harvest-prediction/`
|
||||
- `GET /api/crop-simulation/yield-harvest-summary/`
|
||||
- `POST /api/irrigation/water-stress/`
|
||||
|
||||
---
|
||||
|
||||
## فرمت مشترک `irrigation_recommendation`
|
||||
|
||||
این فیلد در APIهای این سند میتواند ارسال شود:
|
||||
|
||||
```json
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-25",
|
||||
"amount": 2.5,
|
||||
"efficiency": 0.8
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح فیلدها
|
||||
|
||||
- `events`: آرایهای از رویدادهای آبیاری
|
||||
- `date`: تاریخ اجرای آبیاری
|
||||
- `amount`: مقدار آبیاری برای event
|
||||
- `efficiency`: راندمان آبیاری، اختیاری
|
||||
|
||||
### رفتار در PCSE
|
||||
|
||||
این دادهها به `TimedEvents` با سیگنال `irrigate` تبدیل میشوند.
|
||||
|
||||
---
|
||||
|
||||
## فرمت مشترک `fertilization_recommendation`
|
||||
|
||||
```json
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-20",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح فیلدها
|
||||
|
||||
- `events`: آرایهای از رویدادهای کودهی
|
||||
- `date`: تاریخ اجرای کودهی
|
||||
- `N_amount`: مقدار نیتروژن
|
||||
- `N_recovery`: ضریب بازیافت/جذب نیتروژن
|
||||
|
||||
### رفتار در PCSE
|
||||
|
||||
این دادهها به `TimedEvents` با سیگنال `apply_n` تبدیل میشوند.
|
||||
|
||||
---
|
||||
|
||||
## 1) `POST /api/crop-simulation/growth/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
شروع شبیهسازی رشد گیاه بهصورت async و برگرداندن `task_id`.
|
||||
|
||||
### ورودی
|
||||
|
||||
```json
|
||||
{
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"dynamic_parameters": ["DVS", "LAI", "TAGP", "TWSO", "SM"],
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"weather": [
|
||||
{
|
||||
"DAY": "2026-04-01",
|
||||
"LAT": 35.7,
|
||||
"LON": 51.4,
|
||||
"TMIN": 12,
|
||||
"TMAX": 24,
|
||||
"RAIN": 0.0,
|
||||
"ET0": 0.32
|
||||
}
|
||||
],
|
||||
"soil_parameters": {
|
||||
"SMFCF": 0.34,
|
||||
"SMW": 0.14,
|
||||
"RDMSOL": 120.0
|
||||
},
|
||||
"site_parameters": {
|
||||
"WAV": 40.0
|
||||
},
|
||||
"crop_parameters": {},
|
||||
"agromanagement": {},
|
||||
"irrigation_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-02",
|
||||
"amount": 2.5
|
||||
}
|
||||
]
|
||||
},
|
||||
"fertilization_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-02",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7
|
||||
}
|
||||
]
|
||||
},
|
||||
"page_size": 2
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح پارامترهای ورودی
|
||||
|
||||
- `plant_name`: نام گیاه
|
||||
- `dynamic_parameters`: لیست پارامترهای دینامیک موردنیاز در خروجی
|
||||
- `farm_uuid`: شناسه مزرعه؛ اگر باشد، داده مزرعه و forecast از سیستم خوانده میشود
|
||||
- `weather`: آبوهوا بهصورت مستقیم؛ اگر `farm_uuid` نباشد لازم است
|
||||
- `soil_parameters`: override برای پارامترهای خاک
|
||||
- `site_parameters`: override برای پارامترهای site
|
||||
- `crop_parameters`: override برای پارامترهای crop
|
||||
- `agromanagement`: override برای تقویم زراعی
|
||||
- `irrigation_recommendation`: برنامه آبیاری برای تزریق به PCSE
|
||||
- `fertilization_recommendation`: برنامه کودهی برای تزریق به PCSE
|
||||
- `page_size`: اندازه صفحه مراحل رشد در endpoint وضعیت task
|
||||
|
||||
### اعتبارسنجی
|
||||
|
||||
- حداقل یکی از `farm_uuid` یا `weather` باید ارسال شود
|
||||
- `dynamic_parameters` نباید خالی باشد
|
||||
- `page_size` باید بین `1` تا `50` باشد
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 202,
|
||||
"msg": "تسک شبیه سازی رشد در صف قرار گرفت.",
|
||||
"data": {
|
||||
"task_id": "growth-task-1",
|
||||
"status_url": "/api/crop-simulation/growth/growth-task-1/status/",
|
||||
"plant_name": "گوجهفرنگی"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح فیلدهای پاسخ
|
||||
|
||||
- `code`: کد داخلی پاسخ
|
||||
- `msg`: پیام پاسخ
|
||||
- `data.task_id`: شناسه task
|
||||
- `data.status_url`: آدرس پیگیری وضعیت task
|
||||
- `data.plant_name`: نام گیاه
|
||||
|
||||
---
|
||||
|
||||
## 2) `GET /api/crop-simulation/growth/<task_id>/status/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
بررسی وضعیت اجرای شبیهسازی رشد و دریافت نتیجه نهایی.
|
||||
|
||||
### پارامترهای Query
|
||||
|
||||
- `page`: شماره صفحه
|
||||
- `page_size`: اندازه صفحه
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"task_id": "growth-task-1",
|
||||
"status": "SUCCESS",
|
||||
"status_fa": "موفق",
|
||||
"result": {
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"dynamic_parameters": ["DVS", "LAI", "TAGP"],
|
||||
"engine": "موتور شبیه سازی PCSE",
|
||||
"model_name": "مدل ووفوست",
|
||||
"scenario_id": 12,
|
||||
"simulation_warning": null,
|
||||
"summary_metrics": {
|
||||
"yield_estimate": 5400.0,
|
||||
"biomass": 9800.0,
|
||||
"max_lai": 4.2
|
||||
},
|
||||
"stage_timeline": [
|
||||
{
|
||||
"order": 1,
|
||||
"stage_code": "vegetative",
|
||||
"stage_name": "رشد رویشی",
|
||||
"start_date": "2026-04-01",
|
||||
"end_date": "2026-04-05",
|
||||
"days_count": 5,
|
||||
"metrics": {
|
||||
"DVS": {
|
||||
"start": 0.1,
|
||||
"end": 0.8,
|
||||
"min": 0.1,
|
||||
"max": 0.8,
|
||||
"avg": 0.45
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"stages_page": [],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"page_size": 10,
|
||||
"total_items": 1,
|
||||
"total_pages": 1,
|
||||
"has_next": false,
|
||||
"has_previous": false
|
||||
},
|
||||
"daily_records_count": 14,
|
||||
"default_page_size": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح کامل فیلدهای `result`
|
||||
|
||||
- `plant_name`: نام گیاه
|
||||
- `dynamic_parameters`: پارامترهای پویا
|
||||
- `engine`: نام فارسی موتور اجرا
|
||||
- `model_name`: نام فارسی مدل
|
||||
- `scenario_id`: شناسه سناریوی ذخیرهشده
|
||||
- `simulation_warning`: هشدار fallback یا خطای غیرکشنده
|
||||
- `summary_metrics.yield_estimate`: برآورد عملکرد
|
||||
- `summary_metrics.biomass`: بیوماس
|
||||
- `summary_metrics.max_lai`: بیشینه شاخص سطح برگ
|
||||
- `stage_timeline`: خلاصه مراحل رشد
|
||||
- `stages_page`: همان `stage_timeline` بهصورت صفحهبندیشده
|
||||
- `pagination`: متادیتای صفحهبندی
|
||||
- `daily_records_count`: تعداد رکوردهای روزانه
|
||||
- `default_page_size`: اندازه صفحه پیشفرض
|
||||
|
||||
---
|
||||
|
||||
## 3) `POST /api/crop-simulation/current-farm-chart/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
ساخت chart وضعیت فعلی مزرعه بر اساس شبیهسازی.
|
||||
|
||||
### ورودی
|
||||
|
||||
```json
|
||||
{
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-25",
|
||||
"amount": 2.5
|
||||
}
|
||||
]
|
||||
},
|
||||
"fertilization_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-20",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح ورودی
|
||||
|
||||
- `farm_uuid`: شناسه مزرعه
|
||||
- `plant_name`: نام گیاه، اختیاری
|
||||
- `irrigation_recommendation`: برنامه آبیاری
|
||||
- `fertilization_recommendation`: برنامه کودهی
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"engine": "موتور شبیه سازی PCSE",
|
||||
"model_name": "مدل ووفوست",
|
||||
"scenario_id": 15,
|
||||
"simulation_warning": null,
|
||||
"categories": ["2026-04-01", "2026-04-02"],
|
||||
"series": [
|
||||
{
|
||||
"name": "تعداد برگ تخمینی",
|
||||
"key": "leaf_count_estimate",
|
||||
"data": [2400, 3600]
|
||||
}
|
||||
],
|
||||
"summary": [
|
||||
{
|
||||
"title": "تعداد برگ تخمینی",
|
||||
"subtitle": "وضعیت فعلی",
|
||||
"amount": 3600,
|
||||
"unit": "برگ",
|
||||
"avatarColor": "success",
|
||||
"avatarIcon": "tabler-leaf"
|
||||
}
|
||||
],
|
||||
"current_state": {
|
||||
"date": "2026-04-02",
|
||||
"leaf_count_estimate": 3600,
|
||||
"leaf_area_index": 0.3,
|
||||
"biomass_weight": 120.5,
|
||||
"storage_organ_weight": 0.0,
|
||||
"soil_moisture_percent": 41.2,
|
||||
"development_stage": 0.15,
|
||||
"gdd": 12.5
|
||||
},
|
||||
"metrics": {
|
||||
"yield_estimate": 5400,
|
||||
"biomass": 9800,
|
||||
"max_lai": 4.2
|
||||
},
|
||||
"daily_output": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح کامل پاسخ
|
||||
|
||||
- `categories`: محور تاریخ chart
|
||||
- `series`: سریهای نمودار
|
||||
- `summary`: کارتهای summary
|
||||
- `current_state`: وضعیت آخرین روز شبیهسازی
|
||||
- `metrics`: متریکهای خلاصه شبیهسازی
|
||||
- `daily_output`: خروجی روزانه کامل PCSE
|
||||
|
||||
---
|
||||
|
||||
## 4) `POST /api/crop-simulation/yield-prediction/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
تبدیل خروجی شبیهسازی به پیشبینی عملکرد.
|
||||
|
||||
### ورودی
|
||||
|
||||
```json
|
||||
{
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-25",
|
||||
"amount": 2.5
|
||||
}
|
||||
]
|
||||
},
|
||||
"fertilization_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-20",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"predictedYieldTons": 5.4,
|
||||
"predictedYieldRaw": 5400.0,
|
||||
"unit": "تن",
|
||||
"sourceUnit": "کیلوگرم در هکتار",
|
||||
"simulationEngine": "موتور شبیه سازی PCSE",
|
||||
"simulationModel": "مدل ووفوست",
|
||||
"scenarioId": 15,
|
||||
"simulationWarning": null,
|
||||
"supportingMetrics": {
|
||||
"yield_estimate": 5400.0,
|
||||
"biomass": 9800.0,
|
||||
"max_lai": 4.2
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح فیلدها
|
||||
|
||||
- `predictedYieldTons`: عملکرد پیشبینیشده بر حسب تن
|
||||
- `predictedYieldRaw`: عملکرد خام بر حسب کیلوگرم در هکتار
|
||||
- `unit`: واحد نهایی
|
||||
- `sourceUnit`: واحد منبع
|
||||
- `simulationEngine`: نام موتور
|
||||
- `simulationModel`: نام مدل
|
||||
- `scenarioId`: شناسه سناریو
|
||||
- `simulationWarning`: هشدار احتمالی
|
||||
- `supportingMetrics`: متریکهای پشتیبان شبیهسازی
|
||||
|
||||
---
|
||||
|
||||
## 5) `POST /api/crop-simulation/harvest-prediction/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
پیشبینی زمان تقریبی برداشت.
|
||||
|
||||
### ورودی
|
||||
|
||||
```json
|
||||
{
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-25",
|
||||
"amount": 2.5
|
||||
}
|
||||
]
|
||||
},
|
||||
"fertilization_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-20",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"date": "2026-05-14",
|
||||
"dateFormatted": "14 May 2026",
|
||||
"daysUntil": 23,
|
||||
"description": "توضیح زمان برداشت",
|
||||
"optimalWindowStart": "2026-05-11",
|
||||
"optimalWindowEnd": "2026-05-17",
|
||||
"gddDetails": {
|
||||
"current_cumulative_gdd": 50,
|
||||
"required_gdd_for_maturity": 1200,
|
||||
"remaining_gdd": 1150,
|
||||
"estimated_days_to_harvest": 23,
|
||||
"predicted_harvest_date": "2026-05-14",
|
||||
"predicted_harvest_window": {
|
||||
"start": "2026-05-11",
|
||||
"end": "2026-05-17"
|
||||
},
|
||||
"daily_gdd_forecast": [],
|
||||
"simulation_engine": "pcse",
|
||||
"simulation_model_name": "Wofost81_NWLP_CWB_CNB",
|
||||
"simulation_warning": null,
|
||||
"scenario_id": 18
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح فیلدها
|
||||
|
||||
- `date`: تاریخ برداشت پیشبینیشده
|
||||
- `dateFormatted`: تاریخ فرمتشده برای UI
|
||||
- `daysUntil`: تعداد روز تا برداشت
|
||||
- `description`: توضیح خلاصه
|
||||
- `optimalWindowStart`: شروع بازه بهینه برداشت
|
||||
- `optimalWindowEnd`: پایان بازه بهینه برداشت
|
||||
- `gddDetails`: جزئیات مبتنی بر GDD و شبیهسازی
|
||||
|
||||
---
|
||||
|
||||
## 6) `GET /api/crop-simulation/yield-harvest-summary/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
ساخت داشبورد خلاصه عملکرد و برداشت.
|
||||
|
||||
### ورودی
|
||||
|
||||
این API از query string استفاده میکند.
|
||||
|
||||
### پارامترهای query
|
||||
|
||||
- `farm_uuid`: شناسه مزرعه
|
||||
- `season_year`: سال زراعی
|
||||
- `crop_name`: نام محصول
|
||||
- `include_narrative`: اگر `true` باشد، تلاش برای تولید narrative میشود
|
||||
- `irrigation_recommendation`: JSON string برنامه آبیاری
|
||||
- `fertilization_recommendation`: JSON string برنامه کودهی
|
||||
|
||||
### نمونه درخواست
|
||||
|
||||
```text
|
||||
GET /api/crop-simulation/yield-harvest-summary/?farm_uuid=11111111-1111-1111-1111-111111111111&season_year=1404&crop_name=wheat&include_narrative=true&irrigation_recommendation={"events":[{"date":"2026-04-25","amount":2.5}]}&fertilization_recommendation={"events":[{"date":"2026-04-20","N_amount":45,"N_recovery":0.7}]}
|
||||
```
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "موفق",
|
||||
"data": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"season_highlights_card": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"crop_name": "wheat",
|
||||
"season_year": "1404",
|
||||
"title": "خلاصه فصل",
|
||||
"subtitle": "",
|
||||
"total_predicted_yield": 5.4,
|
||||
"yield_unit": "تن",
|
||||
"target_harvest_date": "2026-05-14",
|
||||
"days_until_harvest": 23,
|
||||
"average_readiness": 74,
|
||||
"primary_quality_grade": "A",
|
||||
"estimated_revenue": null,
|
||||
"soil_type": "loam"
|
||||
},
|
||||
"yield_prediction": {},
|
||||
"harvest_prediction_card": {},
|
||||
"harvest_readiness_zones": {},
|
||||
"yield_quality_bands": {},
|
||||
"harvest_operations_card": {},
|
||||
"yield_prediction_chart": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ساختار response
|
||||
|
||||
- `season_highlights_card`: خلاصه فصل
|
||||
- `yield_prediction`: بلوک پیشبینی عملکرد
|
||||
- `harvest_prediction_card`: بلوک پیشبینی برداشت
|
||||
- `harvest_readiness_zones`: آمادگی برداشت نواحی
|
||||
- `yield_quality_bands`: باندهای کیفیت محصول
|
||||
- `harvest_operations_card`: پیشنهاد عملیات برداشت
|
||||
- `yield_prediction_chart`: نمودار عملکرد و بیوماس
|
||||
|
||||
### توضیح مهم
|
||||
|
||||
این API بخشهایی از response را از چند سرویس مختلف میسازد، بنابراین بعضی بلوکها ساختار nested و بزرگ دارند. مخصوصاً:
|
||||
|
||||
- `yield_prediction`
|
||||
- `harvest_prediction_card`
|
||||
- `yield_prediction_chart`
|
||||
|
||||
این سه بخش مستقیماً تحت تأثیر `irrigation_recommendation` و `fertilization_recommendation` قرار میگیرند چون از اجرای PCSE استفاده میکنند.
|
||||
|
||||
---
|
||||
|
||||
## 7) `POST /api/irrigation/water-stress/`
|
||||
|
||||
### کاربرد
|
||||
|
||||
محاسبه شاخص تنش آبی با استفاده از خروجی شبیهسازی.
|
||||
|
||||
### ورودی
|
||||
|
||||
```json
|
||||
{
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-25",
|
||||
"amount": 2.5
|
||||
}
|
||||
]
|
||||
},
|
||||
"fertilization_recommendation": {
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-20",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح ورودی
|
||||
|
||||
- `farm_uuid`: شناسه مزرعه
|
||||
- `sensor_uuid`: نام قدیمی برای `farm_uuid`
|
||||
- `plant_name`: نام گیاه، اختیاری
|
||||
- `irrigation_recommendation`: برنامه آبیاری
|
||||
- `fertilization_recommendation`: برنامه کودهی
|
||||
|
||||
### پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"waterStressIndex": 37,
|
||||
"level": "متوسط",
|
||||
"sourceMetric": {
|
||||
"soilMoisturePercent": 46.0,
|
||||
"availableWaterRatio": 0.72,
|
||||
"fieldCapacity": 0.34,
|
||||
"wiltingPoint": 0.14,
|
||||
"rootDepthCm": 120.0,
|
||||
"recentEt0": 0.33,
|
||||
"recentRain": 1.0,
|
||||
"soilMoistureDrop": 4.2,
|
||||
"developmentStage": 1.02,
|
||||
"stageCode": "flowering",
|
||||
"stageSensitivity": 1.2,
|
||||
"engine": "crop_simulation",
|
||||
"formula": "stress = clamp(((moisture_deficit + et0_pressure + trend_pressure - rainfall_relief - root_depth_relief) * stage_sensitivity), 0, 100)"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### توضیح فیلدهای response
|
||||
|
||||
- `farm_uuid`: شناسه مزرعه
|
||||
- `plant_name`: نام گیاه resolved شده
|
||||
- `waterStressIndex`: شاخص نهایی تنش آبی بین `0` تا `100`
|
||||
- `level`: سطح تنش آبی (`پایین`، `متوسط`، `بالا`)
|
||||
- `sourceMetric`: جزئیات محاسبه
|
||||
|
||||
### توضیح `sourceMetric`
|
||||
|
||||
- `soilMoisturePercent`: درصد رطوبت خاک
|
||||
- `availableWaterRatio`: نسبت آب قابل استفاده
|
||||
- `fieldCapacity`: ظرفیت مزرعه
|
||||
- `wiltingPoint`: نقطه پژمردگی
|
||||
- `rootDepthCm`: عمق ریشه
|
||||
- `recentEt0`: تبخیر و تعرق مرجع اخیر
|
||||
- `recentRain`: بارش اخیر
|
||||
- `soilMoistureDrop`: افت رطوبت خاک
|
||||
- `developmentStage`: مرحله رشد عددی
|
||||
- `stageCode`: کد مرحله رشد
|
||||
- `stageSensitivity`: حساسیت مرحله رشد
|
||||
- `engine`: منبع محاسبه
|
||||
- `formula`: فرمول محاسبه شاخص
|
||||
|
||||
---
|
||||
|
||||
## خطاهای رایج
|
||||
|
||||
### خطای 400
|
||||
|
||||
وقتی ورودی نامعتبر باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"msg": "داده نامعتبر.",
|
||||
"data": {
|
||||
"farm_uuid": ["farm_uuid الزامی است."]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### خطای 404
|
||||
|
||||
بیشتر در `water-stress` وقتی مزرعه پیدا نشود:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 404,
|
||||
"msg": "Farm not found.",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### خطای 500
|
||||
|
||||
وقتی اجرای شبیهسازی یا ساخت response شکست بخورد:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "خطا در پیش بینی عملکرد: ...",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## جمعبندی
|
||||
|
||||
APIهایی که در این سند توضیح داده شدند، حالا میتوانند از ورودی کاربر:
|
||||
|
||||
- `irrigation_recommendation`
|
||||
- `fertilization_recommendation`
|
||||
|
||||
را دریافت کنند و آنها را به لایه شبیهسازی `PCSE` پاس بدهند. این یعنی خروجیهای:
|
||||
|
||||
- رشد
|
||||
- chart مزرعه
|
||||
- پیشبینی عملکرد
|
||||
- پیشبینی برداشت
|
||||
- خلاصه Yield/Harvest
|
||||
- شاخص تنش آبی
|
||||
|
||||
میتوانند تحت تأثیر برنامه آبیاری و برنامه کودهی ارسالی کاربر قرار بگیرند.
|
||||
@@ -0,0 +1,746 @@
|
||||
# راهنمای پیادهسازی API صفحه Yield & Harvest با PCSE و RAG
|
||||
|
||||
این فایل برای تیم بکاند نوشته شده تا صفحه `yield-harvest` را با تکیه بر:
|
||||
|
||||
- مستند `psce_doc.txt` پروژه
|
||||
- سرویسهای موجود در `crop_simulation/`
|
||||
- معماری فعلی RAG در `rag/`
|
||||
|
||||
به شکل قابل اتکا پیادهسازی کند.
|
||||
|
||||
نکته: فایل `psce_doc.txt` در عمل مستند PCSE است و در این سند هم با عنوان PCSE به آن اشاره میشود.
|
||||
|
||||
---
|
||||
|
||||
## جمعبندی سریع
|
||||
|
||||
برای این صفحه، بهترین معماری این است:
|
||||
|
||||
1. عددها، تاریخها، درصدها و سریهای نمودار از PCSE و دادههای مزرعه ساخته شوند.
|
||||
2. RAG فقط برای متنهای توضیحی، خلاصهسازی، و wording کاربرپسند استفاده شود.
|
||||
3. RAG اجازه نداشته باشد عدد جدید، تاریخ جدید، یا KPI جدید اختراع کند.
|
||||
4. یک endpoint خلاصه برای فرانت برگردانده شود:
|
||||
|
||||
`GET /api/yield-harvest/summary/?farm_uuid=<uuid>`
|
||||
|
||||
اگر لازم شد از نظر convention داخلی پروژه همهچیز داخل `crop_simulation` بماند، میتوانید معادل زیر را هم نگه دارید:
|
||||
|
||||
`GET /api/crop-simulation/yield-harvest-summary/?farm_uuid=<uuid>`
|
||||
|
||||
برای فرانت فعلی، مدل summary بهتر از چند endpoint پراکنده است.
|
||||
|
||||
---
|
||||
|
||||
## چیزی که همین الان در پروژه دارید
|
||||
|
||||
### لایه PCSE / شبیهسازی
|
||||
|
||||
الان پروژه این اجزا را دارد:
|
||||
|
||||
- `crop_simulation/services.py`
|
||||
- `crop_simulation/growth_simulation.py`
|
||||
- `crop_simulation/harvest_prediction.py`
|
||||
- `crop_simulation/yield_prediction.py`
|
||||
|
||||
و همین حالا از PCSE این خروجیها را میسازید:
|
||||
|
||||
- `yield` از `TWSO`
|
||||
- `biomass` از `TAGP`
|
||||
- `leaf_area_index` از `LAI`
|
||||
- `development_stage` از `DVS`
|
||||
- `soil_moisture` از `SM`
|
||||
|
||||
این دقیقاً با چیزی که در `psce_doc.txt` آمده همراستا است: PCSE برای state/rate variables و خروجی روزانه مناسب است و برای ساخت KPI، trend و harvest timing گزینه خوبی است.
|
||||
|
||||
### لایه RAG
|
||||
|
||||
الان پروژه الگوی خوبی برای سرویسهای ترکیبی deterministic + RAG دارد:
|
||||
|
||||
- `rag/services/irrigation.py`
|
||||
- `rag/services/fertilization.py`
|
||||
- `rag/chat.py`
|
||||
- `config/rag_config.yaml`
|
||||
|
||||
الگوی درست در این پروژه این است:
|
||||
|
||||
- اول عددها و تصمیمهای پایه از منطق deterministic ساخته میشوند
|
||||
- بعد RAG فقط متن نهایی را polish میکند
|
||||
- در صورت خراب شدن خروجی LLM، fallback ساختاریافته دارید
|
||||
|
||||
برای `yield-harvest` هم باید دقیقاً همین الگو تکرار شود.
|
||||
|
||||
---
|
||||
|
||||
## اصل طراحی مهم
|
||||
|
||||
### چه چیزی باید deterministic باشد
|
||||
|
||||
این فیلدها نباید از RAG بیایند:
|
||||
|
||||
- `daysUntil`
|
||||
- `readiness`
|
||||
- `share`
|
||||
- `series[].data`
|
||||
- `averageReadiness`
|
||||
- `predicted yield`
|
||||
- `harvest date`
|
||||
- هر عددی که در progress bar، chart، KPI یا sorting استفاده میشود
|
||||
|
||||
### چه چیزی میتواند از RAG بیاید
|
||||
|
||||
این فیلدها مناسب RAG هستند:
|
||||
|
||||
- `season_highlights_card.subtitle`
|
||||
- `season_highlights_card.spotlight.caption`
|
||||
- `harvest_prediction_card.description`
|
||||
- `harvest_operations_card.summary`
|
||||
- `steps[].note`
|
||||
- badgeها و labelهای توصیفی کوتاه
|
||||
|
||||
### قانون طلایی
|
||||
|
||||
RAG باید فقط روی context قطعی زیر کار کند:
|
||||
|
||||
- farm details
|
||||
- خروجیهای PCSE
|
||||
- دادههای آبوهوا
|
||||
- دادههای سنسور
|
||||
- در صورت وجود: داده کیفیت، آفات، اقتصاد، بازار
|
||||
|
||||
و prompt آن باید صریح بگوید:
|
||||
|
||||
- عدد جدید نساز
|
||||
- فقط از مقادیر دادهشده استفاده کن
|
||||
- اگر داده کافی نیست، کمبود را بگو
|
||||
- خروجی را در JSON معتبر برگردان
|
||||
|
||||
---
|
||||
|
||||
## پیشنهاد معماری برای این صفحه
|
||||
|
||||
بهترین راه این است که یک orchestrator service جدید بسازید:
|
||||
|
||||
- `crop_simulation/yield_harvest_summary.py`
|
||||
|
||||
و داخل آن چند builder کوچک داشته باشید:
|
||||
|
||||
- `_build_yield_prediction_block()`
|
||||
- `_build_harvest_prediction_block()`
|
||||
- `_build_yield_prediction_chart_block()`
|
||||
- `_build_harvest_readiness_zones_block()`
|
||||
- `_build_yield_quality_bands_block()`
|
||||
- `_build_harvest_operations_block()`
|
||||
- `_build_season_highlights_block()`
|
||||
|
||||
و یک facade نهایی:
|
||||
|
||||
- `YieldHarvestSummaryService.get_summary(farm_uuid, season_year=None, crop_name=None)`
|
||||
|
||||
این service باید:
|
||||
|
||||
1. داده مزرعه را از `farm_data.services.get_farm_details()` بگیرد.
|
||||
2. خروجی شبیهسازی را از `CurrentFarmChartSimulator` و سرویسهای فعلی بگیرد.
|
||||
3. بلوکهای deterministic را بسازد.
|
||||
4. اگر RAG فعال بود، متنهای narrative را enrich کند.
|
||||
5. یک payload نهایی برای فرانت برگرداند.
|
||||
|
||||
---
|
||||
|
||||
## Mapping پیشنهادی هر کارت به منبع داده
|
||||
|
||||
| بلوک | منبع اصلی | وضعیت امکان پیادهسازی |
|
||||
|---|---|---|
|
||||
| `yield_prediction` | `crop_simulation/yield_prediction.py` + chart metrics | قابل پیادهسازی همین الآن |
|
||||
| `harvest_prediction_card` | `crop_simulation/harvest_prediction.py` | قابل پیادهسازی همین الآن |
|
||||
| `yield_prediction_chart` | `CurrentFarmChartSimulator` + historical baseline | نیمهآماده، نیاز به تصمیم درباره سری مقایسهای |
|
||||
| `season_highlights_card` | aggregate از بقیه بلوکها + RAG | قابل پیادهسازی اگر بقیه بلوکها حاضر باشند |
|
||||
| `harvest_readiness_zones` | داده قطعه/زون + PCSE per zone | فعلاً gap دادهای دارد |
|
||||
| `yield_quality_bands` | quality model / grading rules / lab data | فعلاً gap دادهای دارد |
|
||||
| `harvest_operations_card` | rules + optimizer + RAG | قابل پیادهسازی بهصورت اولیه |
|
||||
|
||||
---
|
||||
|
||||
## نکته مهم: همه بلوکها را PCSE بهتنهایی تولید نمیکند
|
||||
|
||||
PCSE برای این بخشها خیلی مناسب است:
|
||||
|
||||
- زمان برداشت
|
||||
- روند رشد
|
||||
- عملکرد پیشبینیشده
|
||||
- stage / maturity / readiness proxy
|
||||
- trend chart
|
||||
|
||||
اما PCSE بهصورت پیشفرض برای اینها کافی نیست:
|
||||
|
||||
- `بریکس`
|
||||
- `گرید ممتاز / درجه یک / فرآوری`
|
||||
- `پتانسیل صادرات`
|
||||
- `قیمت فروش`
|
||||
- `premium`
|
||||
- readiness per block وقتی مدل block-level ندارید
|
||||
|
||||
پس برای صفحه کامل، باید سه لایه را از هم جدا ببینید:
|
||||
|
||||
1. `PCSE layer`: عددهای زیستی و رشد
|
||||
2. `Rules/Aggregation layer`: تبدیل خروجی PCSE به KPIهای محصولی
|
||||
3. `RAG layer`: توضیح، جمعبندی، و متن قابل نمایش
|
||||
|
||||
---
|
||||
|
||||
## طراحی endpoint پیشنهادی
|
||||
|
||||
### Route
|
||||
|
||||
پیشنهاد اصلی:
|
||||
|
||||
- `GET /api/yield-harvest/summary/?farm_uuid=<uuid>&season_year=1404&crop_name=wheat`
|
||||
|
||||
### Query params
|
||||
|
||||
- `farm_uuid`: اجباری
|
||||
- `season_year`: اختیاری
|
||||
- `crop_name`: اختیاری
|
||||
- `include_narrative`: اختیاری، پیشفرض `true`
|
||||
|
||||
### چرا `include_narrative` خوب است
|
||||
|
||||
چون اگر RAG کند یا موقتاً غیرفعال باشد، باز هم فرانت میتواند با داده deterministic رندر شود.
|
||||
|
||||
---
|
||||
|
||||
## ساختار پیشنهادی response
|
||||
|
||||
همان envelope فعلی پروژه مناسب است:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"season_highlights_card": {},
|
||||
"yield_prediction": {},
|
||||
"harvest_prediction_card": {},
|
||||
"harvest_readiness_zones": {},
|
||||
"yield_quality_bands": {},
|
||||
"harvest_operations_card": {},
|
||||
"yield_prediction_chart": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## پیشنهاد فنی برای ساخت هر بلوک
|
||||
|
||||
## 1) `yield_prediction`
|
||||
|
||||
### منبع
|
||||
|
||||
- `YieldPredictionService`
|
||||
- یا مستقیم `CurrentFarmChartSimulator.simulate()`
|
||||
|
||||
### منطق پیشنهادی
|
||||
|
||||
- `predicted yield` از `TWSO`
|
||||
- `harvest readiness` از `DVS` و فاصله تا maturity
|
||||
- `quality score` فعلاً rule-based، نه RAG-based
|
||||
- `loss risk` از ترکیب moisture/weather/pest signals اگر دارید، وگرنه با fallback ساده
|
||||
|
||||
### توصیه
|
||||
|
||||
اگر الآن فقط داده قطعی میخواهید:
|
||||
|
||||
- KPI اول را دقیق بسازید
|
||||
- KPIهای دوم تا چهارم را با rules ساده و شفاف بسازید
|
||||
- آنها را به عنوان `estimated` در کد داخلی mark کنید
|
||||
|
||||
---
|
||||
|
||||
## 2) `harvest_prediction_card`
|
||||
|
||||
### منبع
|
||||
|
||||
- `crop_simulation/harvest_prediction.py`
|
||||
|
||||
### وضعیت
|
||||
|
||||
این بلوک تقریباً آماده است.
|
||||
|
||||
### توصیه
|
||||
|
||||
- `dateFormatted` را از serializer-ready formatter بگیرید
|
||||
- `daysUntil` حتماً `number` بماند
|
||||
- متن `description` اگر خواستید با RAG بهتر شود، version deterministic را هم نگه دارید
|
||||
|
||||
یعنی بهتر است این دو فیلد را داشته باشید:
|
||||
|
||||
- `description`
|
||||
- `descriptionSource = "deterministic" | "rag"`
|
||||
|
||||
این source flag برای debugging خیلی کمک میکند.
|
||||
|
||||
---
|
||||
|
||||
## 3) `yield_prediction_chart`
|
||||
|
||||
### واقعیت فعلی
|
||||
|
||||
خروجی chart فعلی شما در `CurrentFarmChartSimulator` بیشتر اینها را میدهد:
|
||||
|
||||
- leaf count estimate
|
||||
- biomass
|
||||
- storage organ weight
|
||||
- lai
|
||||
- soil moisture
|
||||
|
||||
اما payload فرانت `yield-harvest` در سند شما نمودار `سال قبل / سال جاری` میخواهد.
|
||||
|
||||
### نتیجه
|
||||
|
||||
این بخش هنوز one-to-one با کد فعلی آماده نیست.
|
||||
|
||||
### راه درست
|
||||
|
||||
یکی از این دو مسیر را انتخاب کنید:
|
||||
|
||||
#### مسیر A - سریعتر
|
||||
|
||||
همان chart فعلی را به contract فرانت نزدیک کنید و فعلاً بهجای `سال قبل/سال جاری` از:
|
||||
|
||||
- `عملکرد تجمعی پیشبینیشده`
|
||||
- `بیوماس`
|
||||
|
||||
استفاده کنید.
|
||||
|
||||
#### مسیر B - محصولیتر
|
||||
|
||||
دو سری واقعی تولید کنید:
|
||||
|
||||
- `سال جاری`: از simulation جاری
|
||||
- `سال قبل`: از historical scenario یا historical harvest data
|
||||
|
||||
اگر داده سال قبل ندارید، این سری را fake نکنید.
|
||||
|
||||
### توصیه نهایی
|
||||
|
||||
برای production بهتر است تا وقتی historical source ندارید، label سریها را صادقانه تغییر دهید.
|
||||
|
||||
---
|
||||
|
||||
## 4) `harvest_readiness_zones`
|
||||
|
||||
### بزرگترین gap فعلی
|
||||
|
||||
مدل داده فعلی شما عمدتاً farm-level است:
|
||||
|
||||
- `SensorData`
|
||||
- `center_location`
|
||||
- `farm_details`
|
||||
|
||||
اما این کارت block-level data میخواهد:
|
||||
|
||||
- `A1`
|
||||
- `A2`
|
||||
- cultivar per block
|
||||
- readiness per block
|
||||
|
||||
### نتیجه
|
||||
|
||||
بدون model یا source جدید برای block/zone، این کارت را نباید fake کنید.
|
||||
|
||||
### راه درست
|
||||
|
||||
یا باید یکی از اینها را اضافه کنید:
|
||||
|
||||
1. مدل `FarmBlock` یا `FarmZone`
|
||||
2. چند sensor/polygon برای هر مزرعه
|
||||
3. block metadata در JSON مزرعه
|
||||
|
||||
### پیادهسازی پیشنهادی
|
||||
|
||||
برای هر block:
|
||||
|
||||
1. weather/location را resolve کنید
|
||||
2. crop parameters block-specific بسازید
|
||||
3. یک simulation جدا اجرا کنید
|
||||
4. `DVS`, `TWSO`, `SM`, `maturity date` را بگیرید
|
||||
5. readiness را از rule زیر بسازید
|
||||
|
||||
مثال rule:
|
||||
|
||||
```text
|
||||
readiness = clamp((current_dvs / 2.0) * 100, 0, 100)
|
||||
```
|
||||
|
||||
یا:
|
||||
|
||||
```text
|
||||
readiness = clamp(100 - days_until_harvest * 4, 0, 100)
|
||||
```
|
||||
|
||||
بهتر است این rule داخل code comment شفاف توضیح داده شود.
|
||||
|
||||
---
|
||||
|
||||
## 5) `yield_quality_bands`
|
||||
|
||||
### نکته مهم
|
||||
|
||||
PCSE بهصورت پیشفرض grade یا brix تولید نمیکند.
|
||||
|
||||
پس این کارت را باید از یکی از این منابع ساخت:
|
||||
|
||||
1. داده آزمایشگاهی
|
||||
2. rule engine بر اساس moisture + disease risk + maturity + crop profile
|
||||
3. مدل quality جدا
|
||||
|
||||
### چیزی که نباید انجام شود
|
||||
|
||||
اینکه RAG خودش بگوید:
|
||||
|
||||
- 46% گرید ممتاز
|
||||
- 34% گرید یک
|
||||
|
||||
بدون منطق deterministic
|
||||
|
||||
این کار برای dashboard اشتباه است.
|
||||
|
||||
### پیشنهاد عملی
|
||||
|
||||
فاز اول:
|
||||
|
||||
- اگر quality data ندارید، این کارت را با flag `unavailable` برگردانید
|
||||
- یا bands را از rule-based scoring بسازید و در backend صریح mark کنید که estimated هستند
|
||||
|
||||
مثلاً:
|
||||
|
||||
- `quality_score >= 85 -> premium`
|
||||
- `70..84 -> grade_1`
|
||||
- `< 70 -> processing`
|
||||
|
||||
اما این rule باید crop-specific باشد، نه universal.
|
||||
|
||||
---
|
||||
|
||||
## 6) `harvest_operations_card`
|
||||
|
||||
### بهترین جا برای ترکیب deterministic + RAG
|
||||
|
||||
این کارت candidate خیلی خوبی برای RAG است، چون:
|
||||
|
||||
- order و timing میتواند از rules و simulation بیاید
|
||||
- متن summary و noteها میتواند از RAG بیاید
|
||||
|
||||
### طراحی پیشنهادی
|
||||
|
||||
ابتدا backend یک payload قطعی بسازد:
|
||||
|
||||
```json
|
||||
{
|
||||
"recommended_shift_count": 2,
|
||||
"sorting_capacity_ton_per_day": 15,
|
||||
"required_workers": 12,
|
||||
"max_transfer_hours": 6,
|
||||
"priority_blocks": ["A1", "A2"],
|
||||
"reasoning": [
|
||||
"A1 readiness بالاتر دارد",
|
||||
"moisture در محدوده مناسب است"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
بعد این payload را به RAG بدهید تا فقط اینها را تولید کند:
|
||||
|
||||
- `summary`
|
||||
- `steps[].note`
|
||||
- `steps[].status`
|
||||
|
||||
و حتیالمقدور:
|
||||
|
||||
- `outputs[]` را deterministic نگه دارید
|
||||
|
||||
---
|
||||
|
||||
## 7) `season_highlights_card`
|
||||
|
||||
### بهترین روش
|
||||
|
||||
این کارت را از بقیه بلوکها derive کنید، نه از یک منبع مستقل.
|
||||
|
||||
یعنی:
|
||||
|
||||
- `seasonLabel` از ورودی season
|
||||
- `badges` از readiness/quality/risk
|
||||
- `spotlight.value` از harvest window یا best sale window
|
||||
- `metrics` از yield, grade, revenue estimate
|
||||
|
||||
### نکته
|
||||
|
||||
اگر هنوز economy data ندارید، فیلدهایی مثل:
|
||||
|
||||
- `درآمد هدف`
|
||||
- `پنجره طلایی فروش`
|
||||
|
||||
نباید با RAG اختراع شوند.
|
||||
|
||||
یا باید:
|
||||
|
||||
- حذف شوند
|
||||
- یا با `null`
|
||||
- یا با fallback business rule
|
||||
|
||||
برگردند.
|
||||
|
||||
---
|
||||
|
||||
## پیشنهاد برای RAG service جدید
|
||||
|
||||
مثل `irrigation` و `fertilization` یک سرویس جدید اضافه کنید:
|
||||
|
||||
- `rag/services/yield_harvest.py`
|
||||
|
||||
و در `config/rag_config.yaml` هم service جدید تعریف کنید:
|
||||
|
||||
- `yield_harvest`
|
||||
|
||||
### نقش این سرویس
|
||||
|
||||
این سرویس نباید خودش KPI بسازد.
|
||||
|
||||
فقط باید:
|
||||
|
||||
- summary بنویسد
|
||||
- subtitle بسازد
|
||||
- operation notes تولید کند
|
||||
- badge text را human-friendly کند
|
||||
|
||||
### ورودی پیشنهادی به RAG
|
||||
|
||||
```json
|
||||
{
|
||||
"farm_uuid": "...",
|
||||
"plant_name": "...",
|
||||
"deterministic_metrics": {
|
||||
"predicted_yield_tons": 42.8,
|
||||
"days_until_harvest": 18,
|
||||
"readiness_avg": 84,
|
||||
"quality_score": 91,
|
||||
"loss_risk_percent": 6.5
|
||||
},
|
||||
"operations": {
|
||||
"shift_count": 2,
|
||||
"required_workers": 12,
|
||||
"max_transfer_hours": 6
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### خروجی پیشنهادی از RAG
|
||||
|
||||
فقط این بخشها:
|
||||
|
||||
```json
|
||||
{
|
||||
"season_highlights_card": {
|
||||
"subtitle": "...",
|
||||
"spotlight": {
|
||||
"caption": "..."
|
||||
},
|
||||
"badges": ["...", "..."]
|
||||
},
|
||||
"harvest_prediction_card": {
|
||||
"description": "..."
|
||||
},
|
||||
"harvest_operations_card": {
|
||||
"summary": "...",
|
||||
"steps": [
|
||||
{
|
||||
"title": "...",
|
||||
"note": "...",
|
||||
"status": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rule مهم
|
||||
|
||||
اگر JSON RAG خراب بود:
|
||||
|
||||
- fallback deterministic برگردان
|
||||
- endpoint fail نکند
|
||||
|
||||
---
|
||||
|
||||
## فایلهایی که پیشنهاد میشود تغییر کنند
|
||||
|
||||
### در `crop_simulation/`
|
||||
|
||||
- `crop_simulation/apps.py`
|
||||
- اضافه کردن getter برای summary service
|
||||
|
||||
- `crop_simulation/serializers.py`
|
||||
- serializer درخواست و پاسخ summary
|
||||
|
||||
- `crop_simulation/views.py`
|
||||
- view جدید برای summary
|
||||
|
||||
- `crop_simulation/urls.py`
|
||||
- route جدید
|
||||
|
||||
- `crop_simulation/yield_harvest_summary.py`
|
||||
- orchestrator اصلی
|
||||
|
||||
### در `Schemas/`
|
||||
|
||||
- `Schemas/crop_simulation_yield_harvest_summary.py`
|
||||
- contract برای مستندسازی route
|
||||
|
||||
- `Schemas/__init__.py`
|
||||
- register کردن contract جدید
|
||||
|
||||
### در `rag/`
|
||||
|
||||
- `rag/services/yield_harvest.py`
|
||||
- narrative enrichment
|
||||
|
||||
### در config
|
||||
|
||||
- `config/rag_config.yaml`
|
||||
- service جدید `yield_harvest`
|
||||
|
||||
- `config/tones/yield_harvest_tone.txt`
|
||||
- tone مخصوص summary صفحه
|
||||
|
||||
- در صورت نیاز:
|
||||
- `config/knowledge_base/yield_harvest/`
|
||||
|
||||
---
|
||||
|
||||
## ترتیب پیشنهادی پیادهسازی
|
||||
|
||||
### فاز 1 - سریع و قابل اتکا
|
||||
|
||||
فقط این بلوکها را واقعی کنید:
|
||||
|
||||
- `yield_prediction`
|
||||
- `harvest_prediction_card`
|
||||
- `yield_prediction_chart`
|
||||
- `harvest_operations_card` با rules ساده
|
||||
|
||||
و برای بقیه اگر داده ندارید:
|
||||
|
||||
- `null`
|
||||
- یا `available: false`
|
||||
|
||||
برگردانید.
|
||||
|
||||
### فاز 2 - RAG narrative
|
||||
|
||||
به summary و operation notes متن کاربرپسند اضافه کنید.
|
||||
|
||||
### فاز 3 - block-level readiness
|
||||
|
||||
بعد از اضافه شدن مدل zone/block، `harvest_readiness_zones` را واقعی کنید.
|
||||
|
||||
### فاز 4 - quality/economy
|
||||
|
||||
بعد از اضافه شدن منبع quality یا market:
|
||||
|
||||
- `yield_quality_bands`
|
||||
- revenue
|
||||
- sale window
|
||||
|
||||
را کامل کنید.
|
||||
|
||||
---
|
||||
|
||||
## توصیه مهم درباره contract
|
||||
|
||||
اگر بعضی بلوکها هنوز data source واقعی ندارند، بهتر است از همین حالا response را اینطور طراحی کنید:
|
||||
|
||||
```json
|
||||
{
|
||||
"harvest_readiness_zones": {
|
||||
"available": false,
|
||||
"reason": "block_level_data_missing",
|
||||
"averageReadiness": null,
|
||||
"blocks": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
این بهتر از mock پنهان است، چون:
|
||||
|
||||
- فرانت میفهمد چرا کارت کامل نیست
|
||||
- QA راحتتر تست میکند
|
||||
- بعداً migration سادهتر میشود
|
||||
|
||||
---
|
||||
|
||||
## ریسکهای اصلی
|
||||
|
||||
### 1. استفاده از RAG برای عددسازی
|
||||
|
||||
بزرگترین خطر این صفحه همین است. RAG فقط برای narrative مناسب است.
|
||||
|
||||
### 2. نبود داده block-level
|
||||
|
||||
`harvest_readiness_zones` بدون مدل block عملاً دقیق نمیشود.
|
||||
|
||||
### 3. نبود quality source
|
||||
|
||||
`yield_quality_bands` بدون مدل کیفیت یا rule engine crop-specific قابل اتکا نیست.
|
||||
|
||||
### 4. ناهماهنگی chart contract
|
||||
|
||||
chart فعلی backend دقیقاً همان chart مورد انتظار فرانت نیست.
|
||||
|
||||
---
|
||||
|
||||
## پیشنهاد نهایی من
|
||||
|
||||
اگر بخواهم برای همین repo کمریسکترین مسیر را انتخاب کنم:
|
||||
|
||||
1. یک summary service در `crop_simulation` بسازید.
|
||||
2. `yield_prediction` و `harvest_prediction_card` را از سرویسهای فعلی reuse کنید.
|
||||
3. `yield_prediction_chart` را فعلاً با داده واقعی current simulation برگردانید، نه مقایسه fake سال قبل.
|
||||
4. `harvest_operations_card` را با rules deterministic + optional RAG summary بسازید.
|
||||
5. `season_highlights_card` را از همین بلوکها aggregate کنید.
|
||||
6. `harvest_readiness_zones` و `yield_quality_bands` را فقط وقتی source واقعی دارید production کنید.
|
||||
|
||||
این مسیر با معماری فعلی پروژه، با `psce_doc.txt`، و با pattern موجود RAG بیشترین سازگاری را دارد.
|
||||
|
||||
---
|
||||
|
||||
## یک pseudo flow پیشنهادی
|
||||
|
||||
```text
|
||||
GET /api/yield-harvest/summary/?farm_uuid=...
|
||||
->
|
||||
load farm details
|
||||
->
|
||||
run/reuse PCSE simulation outputs
|
||||
->
|
||||
build deterministic blocks
|
||||
->
|
||||
optionally call RAG for narrative fields only
|
||||
->
|
||||
merge response
|
||||
->
|
||||
return code/msg/data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## نتیجه
|
||||
|
||||
برای این صفحه:
|
||||
|
||||
- PCSE باید موتور عددها باشد
|
||||
- rules باید موتور KPIهای محصولی باشد
|
||||
- RAG باید موتور متن و explanation باشد
|
||||
|
||||
اگر این مرزبندی رعایت شود، هم dashboard قابل اعتماد میشود، هم توسعه بعدی آن تمیز و قابل نگهداری میماند.
|
||||
Reference in New Issue
Block a user