# سیاست منبع داده و تجمیع کلاستر برای APIهای AI

این سند مشخص می‌کند هر API از کجا داده می‌گیرد، آیا الان خروجی آن بر اساس میانگین داده سنسور کلاسترها و میانگین داده ماهواره‌ای کلاسترهای `location_data` هست یا نه، و برای یکپارچه‌سازی چه policyای باید رعایت شود.

این سند بر اساس کد فعلی پروژه نوشته شده و مخصوص APIهای زیر است:

- `POST /api/rag/chat/`
- `POST /api/soile/anomaly-detection/`
- `POST /api/soile/health-summary/`
- `POST /api/soile/moisture-heatmap/`
- `POST /api/weather/farm-card/`
- `POST /api/weather/water-need-prediction/`
- `POST /api/pest-disease/detect/`
- `POST /api/pest-disease/risk/`
- `POST /api/irrigation/plan-from-text/`
- `POST /api/irrigation/recommend/`
- `POST /api/farm-data/parameters/`
- `POST /api/fertilization/plan-from-text/`
- `POST /api/fertilization/recommend/`
- `POST /api/crop-simulation/current-farm-chart/`
- `POST /api/crop-simulation/growth/`
- `GET /api/crop-simulation/growth/{task_id}/status/`
- `POST /api/crop-simulation/harvest-prediction/`
- `GET /api/crop-simulation/yield-harvest-summary/`
- `POST /api/crop-simulation/yield-prediction/`
- `POST /api/economy/overview/`
- `POST /api/farm-alerts/tracker/`

---

## 1) قانون هدف کسب‌وکاری

قانون مطلوبی که باید برای محاسبه‌های عمومی AI رعایت شود این است:

1. برای محاسبه‌های عمومی AI مثل `RAG`، `crop_simulation`، `irrigation`، `fertilization`، `farm_alerts` و سرویس‌های تحلیلی خاک، مبنای داده باید `میانگین داده سنسورهای کلاسترها` و `میانگین داده ماهواره‌ای کلاسترهای location_data` باشد.
2. این تجمیع باید در سطح `بلوک‌های بزرگ کشاورز` انجام شود؛ یعنی اول هر block اصلی جداگانه محاسبه شود، بعد در صورت نیاز یک خلاصه کل مزرعه از روی blockهای اصلی ساخته شود.
3. اگر کشاورز هنوز block تعریف نکرده باشد، حالت پیش‌فرض دامنه باید این باشد:
   - `1 بلوک بزرگ` برای کل مزرعه
   - `1 بلوک کوچک` داخل همان بلوک بزرگ
4. هر API که فقط `farm_uuid` می‌گیرد و رفتار عمومی مزرعه را تحلیل می‌کند، باید از snapshot تجمیع‌شده‌ی مبتنی بر block/sub-block استفاده کند، نه از یک سنسور خام یا یک location خام.

---

## 2) وضعیت فعلی مدل داده

### 2.1) لایه مزرعه

رکورد canonical مزرعه در `farm_data.SensorData` نگه‌داری می‌شود:

- شناسه اصلی: `farm_uuid`
- اتصال مکانی: `center_location -> location_data.SoilLocation`
- سنسورها: `sensor_payload`
- آب‌وهوا: `weather_forecast`
- گیاه: `plant_assignments` و `plant_snapshots`
- روش آبیاری: `irrigation_method`

### 2.2) لایه مکانی

رکورد canonical مکانی در `location_data.SoilLocation` است:

- `farm_boundary`
- `input_block_count`
- `block_layout`
- `block_subdivisions`
- `remote_sensing_runs`

### 2.3) لایه کلاستر و سنجش از دور

در `location_data` عملاً دو سطح تجمیع وجود دارد:

- `block` = بلوک اصلی که کشاورز تعریف کرده
- `sub-block / cluster` = بخش‌های کوچک‌تر داخل هر block

منطق فعلی تجمیع در `location_data/satellite_snapshot.py` پیاده شده است:

- `build_block_sensor_summary(...)`
  - داده سنسورهای منتسب به sub-blockها را جمع می‌کند
  - ابتدا برای هر sub-block میانگین می‌سازد
  - سپس از روی sub-blockها میانگین block را می‌سازد
- `summarize_block_satellite_metrics(...)`
  - داده‌های grid/cell ماهواره‌ای را برای cluster blockها خلاصه می‌کند
  - سپس میانگین block را می‌سازد
- `build_location_block_satellite_snapshots(...)`
  - برای هر block اصلی یک snapshot می‌سازد
- `build_farmer_block_aggregated_snapshot(...)`
  - از روی blockهای اصلی یک خلاصه کل مزرعه می‌سازد

پس زیرساخت فنی برای policy موردنظر تا حد زیادی در پروژه وجود دارد.

---

## 3) رفتار پیش‌فرض blockها

### رفتار فعلی در کد

تابع `build_block_layout()` در `location_data/models.py` وقتی blockی داده نشود، این رفتار را دارد:

- `input_block_count = 1`
- فقط `1 بلوک اصلی` با `block-1` ساخته می‌شود
- `sub_blocks = []`

یعنی الان بخش دوم rule شما کامل اعمال نشده، چون:

- `1 بلوک بزرگ` وجود دارد
- ولی `1 بلوک کوچک داخل آن` به‌صورت default ساخته نمی‌شود

### رفتار مطلوب پیشنهادی

برای سازگاری با خواسته شما، policy پیشنهادی این است:

- اگر هیچ block و هیچ subdivisionی تعریف نشده بود:
  - `block_layout.blocks = [block-1]`
  - داخل `block-1.sub_blocks` یک sub-block پیش‌فرض ساخته شود
- این sub-block پیش‌فرض می‌تواند چیزی شبیه این داشته باشد:

```json
{
  "sub_block_code": "block-1-sub-1",
  "cluster_label": 0,
  "source": "default",
  "boundary": {},
  "cluster_uuid": null
}
```

این policy باعث می‌شود تمام APIهای downstream همیشه حداقل یک سطح `sub-block` داشته باشند و منطق aggregation یکنواخت بماند.

---

## 4) منبع canonical پیشنهادی برای همه APIهای AI

برای APIهای عمومی AI، منبع canonical باید این ترتیب باشد:

### 4.1) سنسورها

منبع اصلی:

- `farm_data.SensorData.sensor_payload`

اما نه به صورت خام. باید از مسیر زیر مصرف شود:

- assign سنسورها به `cluster/sub-block`
- میانگین‌گیری در سطح sub-block
- میانگین‌گیری مجدد در سطح block اصلی
- در صورت نیاز میانگین‌گیری در سطح کل مزرعه

### 4.2) ماهواره و remote sensing

منبع اصلی:

- `location_data.RemoteSensingRun`
- `location_data.AnalysisGridObservation`
- `location_data.RemoteSensingSubdivisionResult`
- `location_data.RemoteSensingClusterBlock`
- `location_data.RemoteSensingClusterAssignment`

و باز هم باید از مسیر زیر مصرف شود:

- میانگین متریک هر cluster/sub-block
- میانگین متریک هر block اصلی
- در صورت نیاز میانگین کل مزرعه

### 4.3) آب‌وهوا

منبع اصلی آب‌وهوا فعلاً cluster-based نیست و از اینجا می‌آید:

- `farm.weather_forecast`
- یا آخرین `center_location.weather_forecasts`

یعنی weather فعلاً location-center based است، نه cluster based.

### 4.4) source of truth نهایی برای APIهای عمومی

برای APIهایی که `farm_uuid` می‌گیرند، بهتر است `source of truth` نهایی یکی از این دو باشد:

- `get_farm_details(farm_uuid)` بعد از ارتقا به policy جدید
- یا یک service جدید مثل `build_ai_farm_snapshot(farm_uuid)`

این snapshot باید شامل این بخش‌ها باشد:

- `farm_level_aggregated_metrics`
- `block_level_metrics`
- `sub_block_level_metrics`
- `weather`
- `plants`
- `irrigation_method`
- `source_metadata`

---

## 5) وضعیت فعلی هر API

در این بخش برای هر API مشخص شده:

- آیا الان مبتنی بر میانگین سنسور کلاسترها و میانگین ماهواره‌ای کلاسترها هست یا نه
- اگر نیست، الان داده را از کجا می‌گیرد
- وضعیت انطباق آن با policy جدید

---

## 5.1) RAG

### `POST /api/rag/chat/`

وضعیت فعلی: `نیمه‌منطبق`

منبع داده فعلی:

- متن خاک کاربر در `rag/user_data.py -> build_user_soil_text()`
- از `SensorData` و `center_location` می‌خواند
- از `build_farmer_block_aggregated_snapshot(...)` استفاده می‌کند
- از `build_location_block_satellite_snapshots(...)` هم برای blockهای اصلی استفاده می‌کند

نتیجه:

- برای summary کلی مزرعه، از aggregation مبتنی بر block استفاده می‌کند
- برای satellite و sensor summary تا حدی با policy شما سازگار است
- اما RAG chat هنوز یک contract رسمی و واحد برای `cluster mean only` ندارد و متن embed شده ممکن است ترکیبی از داده‌های سطح farm و block باشد

نیاز به اصلاح:

- canonical snapshot باید صریحاً از `block mean` و `sub-block mean` ساخته شود
- prompt/context باید روشن بگوید که اعداد از `cluster averages` آمده‌اند

### `POST /api/soile/anomaly-detection/`

وضعیت فعلی: `غیرمنطبق مستقیم، منطبق غیرمستقیم`

منبع داده فعلی:

- anomaly payload از request می‌آید
- اطلاعات مزرعه از `farm_data.services.get_farm_details()`
- سپس به `rag/services/soil_anomaly.py` داده می‌شود

نتیجه:

- خود anomaly ممکن است از هر منبعی آمده باشد و API آن را enforce نمی‌کند
- اما farm context از `get_farm_details()` می‌آید که فعلاً `latest_satellite + sensor_payload` را ترکیب می‌کند
- `get_farm_details()` هنوز خلاصه soil را از `build_location_satellite_snapshot(center_location)` می‌گیرد، نه از `build_farmer_block_aggregated_snapshot()`

نیاز به اصلاح:

- `get_farm_details()` باید خلاصه soil اصلی را از `farmer-level aggregated snapshot` بگیرد
- anomaly input هم اگر داخلی تولید می‌شود، باید بر پایه cluster mean باشد

---

## 5.2) Soile

### `POST /api/soile/health-summary/`

وضعیت فعلی: `احتمالاً نیمه‌منطبق`

منبع داده فعلی:

- از `farm_data.context.load_farm_context()` و سرویس‌های soil استفاده می‌کند
- `load_farm_context()` از `build_location_block_satellite_snapshots(location)` استفاده می‌کند
- بخشی از context block-based است

اما:

- اگر در summary نهایی از metrics خام سنسور یا latest weather/location استفاده شود، خروجی کاملاً cluster-mean-only نیست

نیاز به اصلاح:

- health summary باید صریحاً از `block_level` و `farm_level` aggregated metrics استفاده کند
- هیچ metric خام سنسور بدون عبور از منطق sub-block mean وارد پاسخ نشود

### `POST /api/soile/moisture-heatmap/`

وضعیت فعلی: `منطبق نیست`

منبع داده فعلی:

- heatmap ذاتاً spatial است و معمولاً از grid/cell یا location-level moisture data ساخته می‌شود
- برای heatmap، میانگین‌گیری کامل روی clusterها کافی نیست چون heatmap نیاز به توزیع مکانی دارد

نتیجه:

- این API نباید به خروجی تک‌عددی farm average تقلیل داده شود
- بلکه باید داده خام grid/cell یا cluster-level map را نگه دارد

policy صحیح:

- برای کارت summary یا average moisture بله، از cluster mean استفاده شود
- اما برای heatmap خودِ خروجی باید `cluster map` یا `grid map` باشد، نه صرفاً میانگین کل مزرعه

---

## 5.3) Weather

### `POST /api/weather/farm-card/`

وضعیت فعلی: `غیرمنطبق`

منبع داده فعلی:

- از `weather_forecast` مرتبط با `center_location`
- یا آخرین forecast همان location

نتیجه:

- weather فعلاً بر اساس clusterهای `location_data` نیست
- چون مدل weather cluster-based ندارد

نیاز به اصلاح:

- اگر قرار است cluster-based شود، باید weather در سطح block/sub-block تعریف شود
- در غیر این صورت، این API باید در سند به‌عنوان `location-centered weather` ثبت شود

### `POST /api/weather/water-need-prediction/`

وضعیت فعلی: `نیمه‌منطبق`

منبع داده فعلی:

- در `rag/services/water_need_prediction.py` از `get_farm_details(farm_uuid)` استفاده می‌شود
- همچنین از weather forecast مزرعه استفاده می‌کند

نتیجه:

- بخش خاک/سنسور می‌تواند از snapshot مزرعه بیاید
- اما چون `get_farm_details()` هنوز fully farmer-block-aggregated نیست، خروجی کامل منطبق نیست
- بخش weather هم location-level است

نیاز به اصلاح:

- خاک و ماهواره از farmer aggregated snapshot
- weather تا زمان توسعه مدل cluster-weather، به‌صورت location-level باقی بماند ولی source آن شفاف اعلام شود

---

## 5.4) Pest & Disease

### `POST /api/pest-disease/detect/`

وضعیت فعلی: `غیرمنطبق مفهومی`

منبع داده فعلی:

- ورودی اصلی: تصویر
- context کمکی: `farm_uuid` و داده مزرعه از `get_farm_details()`

نتیجه:

- هسته این API image-based است، نه cluster average based
- context مزرعه می‌تواند cluster-based شود، اما خروجی نهایی وابسته به تصویر است

policy صحیح:

- context مزرعه برای کمک به تشخیص از snapshot تجمیعی بیاید
- اما این API ذاتاً APIِ میانگین‌محور نیست

### `POST /api/pest-disease/risk/`

وضعیت فعلی: `نیمه‌منطبق`

منبع داده فعلی:

- `rag/services/pest_disease.py`
- اطلاعات مزرعه از `get_farm_details()`
- داده RAG و پایگاه دانش تخصصی

نتیجه:

- risk API باید از context مزرعه استفاده کند
- اگر `get_farm_details()` اصلاح شود، این API هم خودبه‌خود نزدیک به policy مطلوب می‌شود

---

## 5.5) Irrigation

### `POST /api/irrigation/plan-from-text/`

وضعیت فعلی: `غیرمنطبق ذاتی`

منبع داده فعلی:

- ورودی اصلی: متن آزاد
- مدل parser متنی

نتیجه:

- این API parser است، نه تحلیل agronomic مبتنی بر sensor/satellite
- بنابراین لازم نیست خروجی‌اش بر پایه cluster average باشد

policy صحیح:

- فقط اگر farm context کمکی به parser اضافه شود، آن context باید از snapshot تجمیعی بیاید

### `POST /api/irrigation/recommend/`

وضعیت فعلی: `نیمه‌منطبق`

منبع داده فعلی:

- از farm context و سرویس RAG irrigation استفاده می‌کند
- بخشی از سنسور را ممکن است مستقیماً از `sensor.sensor_payload` بخواند

نتیجه:

- اگر بعضی metricها مستقیم از payload خام خوانده شوند، با policy شما ناسازگار است

نیاز به اصلاح:

- recommendation فقط باید از aggregated block/sub-block metrics تغذیه شود
- fallback مستقیم به اولین سنسور یا payload خام باید حذف یا محدود شود

---

## 5.6) Farm Parameters

### `POST /api/farm-data/parameters/`

وضعیت فعلی: `خارج از دامنه این policy`

منبع داده فعلی:

- این API داده را ایجاد/ویرایش می‌کند
- `farm_boundary` را می‌گیرد
- `center_location` را resolve می‌کند
- `sensor_payload` را ذخیره می‌کند
- weather/location sync را trigger می‌کند

نتیجه:

- این API تولیدکننده داده است، نه consumer تحلیلی
- بنابراین قرار نیست خروجی analytic آن بر پایه cluster mean باشد

اما نقش مهم:

- باید sensorها را طوری ذخیره کند که assignment به `cluster/sub-block` ممکن باشد
- اگر cluster_uuid یا sub_block_code در payload نیاید، aggregation دقیق محدود می‌شود

---

## 5.7) Fertilization

### `POST /api/fertilization/plan-from-text/`

وضعیت فعلی: `غیرمنطبق ذاتی`

منبع داده فعلی:

- parser متن آزاد

نتیجه:

- مانند irrigation text parser، این API ماهیتاً cluster-average consumer نیست

### `POST /api/fertilization/recommend/`

وضعیت فعلی: `نیمه‌منطبق`

منبع داده فعلی:

- context مزرعه و RAG fertilization
- معمولاً از `get_farm_details()` و contextهای وابسته استفاده می‌کند

نتیجه:

- با اصلاح منبع canonical مزرعه، این API هم می‌تواند کاملاً منطبق شود

---

## 5.8) Crop Simulation

### `POST /api/crop-simulation/current-farm-chart/`
### `POST /api/crop-simulation/growth/`
### `POST /api/crop-simulation/harvest-prediction/`
### `GET /api/crop-simulation/yield-harvest-summary/`
### `POST /api/crop-simulation/yield-prediction/`

وضعیت فعلی: `غیرمنطبق تا نیمه‌منطبق`

منبع داده فعلی:

- سرویس‌های `crop_simulation/services.py`
- بخشی از داده‌ها از farm context و بخشی از سنسور یا weather فعلی می‌آیند
- در بعضی helperها متریک‌ها ممکن است مستقیماً از `sensor_payload` یا propertyهای سنسور resolve شوند

نتیجه:

- با rule شما که گفته بودی برای `crop_simulation` باید از داده کل بلوک‌های بزرگ استفاده شود، وضعیت فعلی هنوز کامل نیست
- چون منبع canonical شبیه‌سازی هنوز به‌صورت صریح farmer-block aggregation enforced نشده

نیاز به اصلاح:

- ورودی تمام simulation endpointها باید از farm aggregated snapshot بیاید
- یعنی متریک‌های soil moisture, temperature, ndvi, ndwi, slope, nitrogen, phosphorus, potassium از `cluster mean -> block mean -> farm mean` ساخته شوند
- weather فعلاً جداگانه location-level می‌ماند مگر بعداً block-weather اضافه شود

### `GET /api/crop-simulation/growth/{task_id}/status/`

وضعیت فعلی: `وابسته به منبع run اولیه`

نتیجه:

- این endpoint فقط status/result job را برمی‌گرداند
- اگر `growth` هنگام شروع از داده غیرمنطبق استفاده کرده باشد، status endpoint هم همان خروجی را reflect می‌کند

---

## 5.9) Economy

### `POST /api/economy/overview/`

وضعیت فعلی: `منطبق نیست و حتی منبع واقعی ندارد`

منبع داده فعلی:

- `economy/services.py`
- پیام صریح دارد که منبع واقعی تنظیم نشده است

نتیجه:

- این API فعلاً نه cluster-based است و نه حتی data-backed

نیاز به اصلاح:

- اگر قرار است فعال شود، باید از snapshot مزرعه + هزینه‌ها + yield prediction + irrigation/fertilization plan استفاده کند

---

## 5.10) Farm Alerts

### `POST /api/farm-alerts/tracker/`

وضعیت فعلی: `نیمه‌منطبق`

منبع داده فعلی:

- `farm_alerts/services.py`
- از `load_farm_context(farm_uuid)` و `get_farm_details(farm_uuid)` استفاده می‌کند

نتیجه:

- چون farm context بخشی از snapshotهای block-based را می‌خواند، تا حدی با policy سازگار است
- اما اگر context نهایی هنوز metric خام یا latest non-aggregated را ترجیح بدهد، کامل منطبق نیست

نیاز به اصلاح:

- همه alert ruleها باید روی block/farm aggregated metrics سوار شوند
- در صورت نیاز، هشدارهای spatial باید روی cluster-level breakdown هم نمایش داده شوند

---

## 6) جمع‌بندی سریع انطباق APIها

### APIهایی که ماهیتاً باید cluster-aggregated باشند

این‌ها باید به policy جدید مهاجرت کنند:

- `POST /api/rag/chat/`
- `POST /api/soile/anomaly-detection/`
- `POST /api/soile/health-summary/`
- `POST /api/weather/water-need-prediction/`
- `POST /api/pest-disease/risk/`
- `POST /api/irrigation/recommend/`
- `POST /api/fertilization/recommend/`
- `POST /api/crop-simulation/current-farm-chart/`
- `POST /api/crop-simulation/growth/`
- `POST /api/crop-simulation/harvest-prediction/`
- `GET /api/crop-simulation/yield-harvest-summary/`
- `POST /api/crop-simulation/yield-prediction/`
- `POST /api/farm-alerts/tracker/`

### APIهایی که ماهیتاً parser/input/image/weather هستند و کامل cluster-based نیستند

- `POST /api/pest-disease/detect/` -> image-first
- `POST /api/irrigation/plan-from-text/` -> text parser
- `POST /api/fertilization/plan-from-text/` -> text parser
- `POST /api/farm-data/parameters/` -> data ingestion/upsert
- `POST /api/weather/farm-card/` -> location-centered weather
- `POST /api/economy/overview/` -> فعلاً منبع واقعی ندارد

### APIهایی که spatial map هستند و نباید صرفاً average شوند

- `POST /api/soile/moisture-heatmap/`

این API باید cluster/grid-aware بماند، نه فقط average-based.

---

## 7) مهم‌ترین mismatch فعلی در کد

مهم‌ترین mismatch فعلی این است که `farm_data.services.get_farm_details()` برای `soil.resolved_metrics` این کار را می‌کند:

- `latest_satellite = build_location_satellite_snapshot(center_location)`
- سپس `latest_satellite.resolved_metrics` را به عنوان soil_metrics می‌گیرد

این یعنی:

- خلاصه اصلی soil بر اساس یک snapshot location/block واحد ساخته می‌شود
- نه بر اساس `build_farmer_block_aggregated_snapshot(center_location, sensor_payload=...)`

در حالی که برای policy موردنظر شما بهتر است این کار انجام شود:

- `farm_level_snapshot = build_farmer_block_aggregated_snapshot(...)`
- `soil.resolved_metrics = farm_level_snapshot.resolved_metrics`
- `soil.satellite_snapshots = block-level snapshots`
- در صورت نیاز `sub_block snapshots` هم در context بمانند

---

## 8) پیشنهاد ساختار استاندارد پاسخ داده برای AI

برای همه سرویس‌های AI بهتر است یک snapshot واحد با این ساختار وجود داشته باشد:

```json
{
  "farm_uuid": "...",
  "aggregation_policy": {
    "sensor": "cluster_mean_then_block_mean_then_farm_mean",
    "satellite": "cluster_mean_then_block_mean_then_farm_mean",
    "weather": "center_location_latest_forecast",
    "default_block_policy": "1_main_block + 1_default_sub_block_when_missing"
  },
  "farm_metrics": {},
  "block_metrics": [],
  "sub_block_metrics": [],
  "weather": {},
  "plants": [],
  "irrigation_method": {}
}
```

مزیت این طراحی:

- همه APIها source of truth یکسان دارند
- اختلاف بین RAG، crop simulation، irrigation و alerts کم می‌شود
- تست‌پذیری ساده‌تر می‌شود

---

## 9) نتیجه نهایی

### الان کدام APIها دقیقاً مطابق خواسته شما نیستند؟

تقریباً همه APIهای تحلیلی هنوز `کاملاً` مطابق policy شما نیستند، چون منبع canonical یکپارچه‌ای که صریحاً بر پایه `cluster mean sensor + cluster mean satellite` باشد هنوز همه‌جا enforce نشده است.

بیشترین فاصله با policy مطلوب در این بخش‌هاست:

- `crop_simulation`
- `irrigation/recommend`
- `fertilization/recommend`
- `weather/farm-card`
- `economy/overview`
- بخشی از `soile` و `farm_alerts`

### الان کدام APIها از همین حالا تا حدی نزدیک هستند؟

- `rag/chat`
- `farm_alerts/tracker`
- `soile/health-summary`
- `pest-disease/risk`

چون به‌صورت مستقیم یا غیرمستقیم از snapshotهای block-based و `get_farm_details()` استفاده می‌کنند.

### الان کدام APIها ماهیتاً نباید صرفاً average-based شوند؟

- `soile/moisture-heatmap`
- `pest-disease/detect`
- parserهای `plan-from-text`

---

## 10) پیشنهاد اجرای فنی در کد

ترتیب پیشنهادی برای پیاده‌سازی:

1. `get_farm_details()` را به `farmer aggregated snapshot` مهاجرت بده.
2. یک service جدید مثل `build_ai_farm_snapshot()` بساز.
3. default block policy را به `1 main block + 1 default sub-block` ارتقا بده.
4. همه APIهای تحلیلی را وادار کن فقط از snapshot جدید بخوانند.
5. برای APIهای spatial مثل heatmap، علاوه بر average summary، خروجی cluster/grid breakdown نگه دار.

