# گزارش تحلیل خطای `location_data` برای Remote Sensing Run

این سند بر اساس payload دریافتی از endpoint زیر تهیه شده است:

- `GET /api/location-data/remote-sensing/runs/{task_id}/status/`

نمونه run بررسی‌شده:

- `task_id`: `02959179-63b2-4388-bb2e-a4d79176eca2`
- `run.id`: `6`
- بازه زمانی: `2026-04-09` تا `2026-05-09`
- وضعیت API: `failed`
- وضعیت Celery: `RETRY`

---

## 1) خلاصه خیلی کوتاه

این run تا مرحله واکشی metricها و ذخیره observationها جلو رفته، اما در مرحله ساخت subdivision داده‌محور شکست خورده است.

خطای نهایی:

- `هیچ observation قابل استفاده‌ای برای خوشه‌بندی باقی نماند.`

این خطا در کد دقیقاً زمانی رخ می‌دهد که سیستم بعد از بارگذاری observationها، هیچ ردیفی را برای clustering قابل استفاده تشخیص ندهد.

مسیر کد:

- `location_data/tasks.py`
- `location_data/data_driven_subdivision.py`

---

## 2) جدول مدل‌ها / جدول‌های درگیر

| مدل | جدول Django | نقش |
|---|---|---|
| `SoilLocation` | `location_data_soillocation` | اطلاعات location و farm boundary |
| `BlockSubdivision` | `location_data_blocksubdivision` | تقسیم‌بندی بلوک/مزرعه |
| `RemoteSensingRun` | `location_data_remotesensingrun` | وضعیت اجرای pipeline سنجش‌ازدور |
| `AnalysisGridCell` | `location_data_analysisgridcell` | سلول‌های شبکه تحلیلی |
| `AnalysisGridObservation` | `location_data_analysisgridobservation` | مقادیر metricها برای هر cell |
| `RemoteSensingSubdivisionResult` | `location_data_remotesensingsubdivisionresult` | نتیجه clustering و subdivision نهایی |
| `RemoteSensingClusterAssignment` | `location_data_remotesensingclusterassignment` | نسبت هر cell به cluster |

---

## 3) جدول معنی فیلدهای سطح اول response

| فیلد | مقدار نمونه | توضیح |
|---|---|---|
| `code` | `200` | پاسخ HTTP موفق از API؛ لزوماً به معنای موفق بودن task نیست |
| `msg` | `success` | پیام wrapper API |
| `data.status` | `failed` | وضعیت client-facing run |
| `data.source` | `database` | payload از داده‌های ذخیره‌شده در DB ساخته شده |
| `data.task_id` | UUID | شناسه Celery task |
| `data.run` | object | snapshot اصلی run |
| `data.task` | object | جزئیات stageها، timestamps و وضعیت Celery |

---

## 4) جدول معنی فیلدهای `run`

| فیلد | مقدار نمونه | توضیح |
|---|---|---|
| `id` | `6` | شناسه رکورد run در DB |
| `block_code` | `""` | خالی یعنی کل مزرعه، نه یک بلوک خاص |
| `chunk_size_sqm` | `900` | اندازه هر cell به متر مربع |
| `temporal_start` | `2026-04-09` | شروع بازه تحلیل |
| `temporal_end` | `2026-05-09` | پایان بازه تحلیل |
| `status` | `failure` | مقدار خام DB |
| `status_label` | `failed` | نسخه نرمال‌شده برای client |
| `pipeline_status` | `failed` | تکرار client-facing status |
| `stage` | `observations_persisted` | آخرین stage ذخیره‌شده در metadata |
| `selected_features` | `ndvi`, `ndwi`, `soil_vv_db`, `dem_m`, `slope_deg` | featureهایی که pipeline فکر می‌کند برای clustering لازم‌اند |
| `requested_cluster_count` | `null` | تعداد cluster صریح از کاربر نیامده و الگوریتم باید تصمیم بگیرد |
| `error_message` | متن فارسی خطا | خطای نهایی ثبت‌شده روی run |
| `started_at` | timestamp | زمان شروع واقعی run |
| `finished_at` | timestamp | زمان ثبت failure روی run |
| `created_at` | timestamp | زمان ایجاد رکورد run |
| `updated_at` | timestamp | آخرین به‌روزرسانی رکورد run |

---

## 5) جدول معنی فیلدهای `run.metadata`

| فیلد | توضیح |
|---|---|
| `scope` | scope اجرای تحلیل؛ در این نمونه `all_blocks` |
| `stage` | stage فعلی/آخر ذخیره‌شده |
| `task_id` | شناسه Celery task |
| `pipeline.name` | نام pipeline |
| `pipeline.version` | نسخه pipeline |
| `farm_uuid` | شناسه مزرعه |
| `timestamps` | زمان ورود به هر stage |
| `status_label` | وضعیت client-facing ذخیره‌شده در metadata |
| `requested_via` | مبدا درخواست؛ در این نمونه `api` |
| `stage_details` | جزئیات هر stage |
| `failure_reason` | دلیل نهایی ثبت failure |
| `selected_features` | featureهای مورد استفاده برای clustering |
| `requested_cluster_count` | cluster count درخواستی، اگر وجود داشته باشد |

---

## 6) جدول معنی stageها در این run

| stage | وضعیت مشاهده‌شده | معنی |
|---|---|---|
| `queued` | completed | task در صف قرار گرفته |
| `preparing_analysis_grid` | completed | آماده‌سازی grid تحلیلی |
| `analysis_grid_ready` | completed | grid سلول‌ها آماده بوده |
| `analysis_cells_selected` | completed | 12 سلول برای پردازش انتخاب شده‌اند |
| `fetching_remote_metrics` | completed | metricها از openEO درخواست شده‌اند |
| `remote_metrics_fetched` | completed | jobهای openEO تمام شده‌اند |
| `observations_persisted` | failed | observationها ذخیره شده‌اند ولی pipeline بعد از این مرحله fail شده |
| `failed` | completed | stage شکست نهایی ثبت شده |

نکته مهم:

- در payload فعلی، `run.stage` هنوز `observations_persisted` است اما `data.status` برابر `failed` است.
- این یعنی status و stage با هم perfectly sync نیستند.

---

## 7) جدول معنی `stage_details`

### 7.1) `preparing_analysis_grid`

| فیلد | توضیح |
|---|---|
| `block_code` | بلوک هدف |
| `temporal_extent.start_date` | شروع بازه |
| `temporal_extent.end_date` | پایان بازه |

### 7.2) `analysis_grid_ready`

| فیلد | توضیح |
|---|---|
| `grid_summary.created` | آیا grid همین run ساخته شده یا از قبل وجود داشته |
| `grid_summary.block_code` | کد بلوک |
| `grid_summary.total_count` | تعداد کل cellها |
| `grid_summary.created_count` | تعداد cell تازه ایجادشده |
| `grid_summary.chunk_size_sqm` | اندازه cell |
| `grid_summary.existing_count` | تعداد cellهای موجود از قبل |

### 7.3) `analysis_cells_selected`

| فیلد | توضیح |
|---|---|
| `force_refresh` | آیا cache نادیده گرفته شده |
| `total_cell_count` | تعداد کل cellها |
| `existing_cell_count` | تعداد cellهایی که داده از قبل داشته‌اند |
| `cell_count_to_process` | تعداد cellهایی که باید پردازش شوند |

### 7.4) `fetching_remote_metrics`

| فیلد | توضیح |
|---|---|
| `target_cells` | لیست cellهای هدف |
| `requested_cell_count` | تعداد cellهای هدف |
| `metric_progress.total_metrics` | تعداد metricهایی که pipeline انتظار دارد |
| `metric_progress.completed_metric_count` | تعداد metricهای کامل‌شده |
| `metric_progress.completed_metrics` | metricهای کامل‌شده واقعی |
| `metric_progress.failed_metrics` | metricهای fail شده |
| `metric_progress.states` | وضعیت هر metric از نگاه progress tracker |

### 7.5) `remote_metrics_fetched`

| فیلد | توضیح |
|---|---|
| `failed_metric_count` | تعداد metric fail شده |
| `service_metadata.backend` | backend مورد استفاده |
| `service_metadata.job_refs` | job id هر metric در openEO |
| `service_metadata.backend_url` | آدرس backend |
| `service_metadata.failed_metrics` | خطاهای metric-level |
| `service_metadata.collections_used` | collectionهای مصرف‌شده |

### 7.6) `observations_persisted`

| فیلد | توضیح |
|---|---|
| `created_count` | تعداد observation ساخته‌شده |
| `updated_count` | تعداد observation به‌روزشده |

---

## 8) جدول معنی `task`

| فیلد | مقدار نمونه | توضیح |
|---|---|---|
| `current_stage` | `observations_persisted` | stage فعلی/آخرین stage ثبت‌شده |
| `current_stage_details` | `created_count=12`, `updated_count=0` | جزئیات stage جاری |
| `timestamps` | object | timeline کامل stageها |
| `stages` | list | نسخه ترتیبی از stageها |
| `metric_progress` | object | وضعیت پیشرفت metricها |
| `failure_reason` | متن فارسی | خطای ثبت‌شده |
| `celery.state` | `RETRY` | Celery task هنوز final نشده و در retry است |
| `celery.ready` | `false` | نتیجه نهایی هنوز آماده نیست |
| `celery.successful` | `false` | task هنوز success نشده |
| `celery.failed` | `false` | task هنوز fail نهایی نشده |
| `celery.info` | متن خطا | پیام retry فعلی Celery |

---

## 9) دلیل دقیق خطا

دلیل مستقیم خطا از خود کد:

- در `location_data/data_driven_subdivision.py` اگر بعد از ساخت dataset، هیچ observation قابل استفاده برای clustering باقی نماند، این exception پرتاب می‌شود:
  - `هیچ observation قابل استفاده‌ای برای خوشه‌بندی باقی نماند.`

این یعنی pipeline به این نقطه رسیده:

1. cellها ساخته شده‌اند
2. metricها از openEO درخواست شده‌اند
3. observationها در DB ذخیره شده‌اند
4. اما در زمان ساخت dataset برای clustering، ردیف قابل مصرفی پیدا نشده است

### معنی عملی این خطا

یعنی برای همه observationهای این run، سیستم نتوانسته feature vector قابل استفاده بسازد.

در کد فعلی، observation وقتی از clustering حذف می‌شود که:

- همه featureهای انتخاب‌شده برای آن observation برابر `None` باشند

پس این خطا معمولاً یعنی:

- یا تمام metricهای لازم برای همه cellها `null` شده‌اند
- یا feature set انتخابی با داده‌هایی که واقعاً ذخیره شده‌اند mismatch دارد
- یا persistence / selection طوری انجام شده که clustering به داده قابل استفاده دسترسی پیدا نکرده است

---

## 10) محتمل‌ترین علت‌ها برای همین run

### علت 1: featureهای حذف‌شده هنوز در pipeline زنده هستند

در payload می‌بینیم:

- `selected_features` هنوز شامل `dem_m` و `slope_deg` است
- اما در `location_data/openeo_service.py` این metricها از محاسبه حذف شده‌اند

نتیجه:

- API و metadata هنوز فکر می‌کنند این دو feature بخشی از clustering هستند
- ولی openEO دیگر آن‌ها را تولید نمی‌کند
- بنابراین در observationها این فیلدها عملاً `None` می‌مانند

نکته مهم:

- فقط `None` بودن `dem_m` و `slope_deg` به‌تنهایی برای تولید این خطا کافی نیست
- اما این mismatch یکی از مهم‌ترین نشانه‌های ناسازگاری pipeline است

### علت 2: metricهای اصلی احتمالاً برای همه cellها مقدار قابل استفاده نداشته‌اند

چون این خطا فقط وقتی رخ می‌دهد که هیچ observation usable باقی نماند، محتمل است که برای همه 12 cell:

- `ndvi`
- `ndwi`
- `soil_vv_db`

هم عملاً `None` شده باشند یا آن‌طور که clustering انتظار دارد قابل مصرف نبوده باشند.

این حالت ممکن است از اینجا آمده باشد:

- داده خام provider برای این محدوده/بازه تهی یا نامعتبر بوده
- masking یا aggregation مقدار usable تولید نکرده
- یا در persistence، featureهای محاسبه‌شده به‌درستی به ستون‌های clustering نرسیده‌اند

### علت 3: progress tracker با metricهای واقعی sync نیست

در payload:

- `completed_metrics` شامل `soil_vv` است
- ولی `states` شامل `soil_vv_db` به حالت `pending` است

یعنی tracker با metric واقعی اجراشده sync نیست:

- metric واقعی remote: `soil_vv`
- metric مشتق‌شده محلی: `soil_vv_db`

این باعث می‌شود progress ظاهراً ناقص دیده شود، حتی وقتی داده remote کامل شده است.

### علت 4: وضعیت DB و Celery با هم متناقض‌اند

در payload:

- `data.status = failed`
- ولی `task.celery.state = RETRY`

یعنی:

- از نظر DB، run شکست خورده ثبت شده
- از نظر Celery، task هنوز در حال retry است و failure نهایی نشده

این برای client گیج‌کننده است، چون معلوم نیست باید run را تمام‌شده و fail شده بداند یا منتظر retry بماند.

---

## 11) مشکلات فعلی `location_data` که از کد مشخص هستند

### مشکل 1: `DEFAULT_CLUSTER_FEATURES` هنوز metricهای حذف‌شده را نگه داشته

فایل:

- `location_data/data_driven_subdivision.py`

وضعیت فعلی:

- `dem_m`
- `slope_deg`

هنوز داخل `DEFAULT_CLUSTER_FEATURES` هستند.

اثر:

- `selected_features` اشتباه در run metadata
- progress tracker اشتباه
- clustering contract قدیمی باقی می‌ماند

### مشکل 2: serializer و summary هنوز `dem_m` و `slope_deg` را به API برمی‌گردانند

فایل‌ها:

- `location_data/serializers.py`
- `location_data/views.py`

اثر:

- response API هنوز metricهای حذف‌شده را نمایش می‌دهد
- summary میانگین `dem_m_mean` و `slope_deg_mean` می‌سازد
- مصرف‌کننده API فکر می‌کند این metricها هنوز پشتیبانی می‌شوند

### مشکل 3: `_upsert_grid_observations` هنوز فیلدهای حذف‌شده را persist می‌کند

فایل:

- `location_data/tasks.py`

اثر:

- ستون‌های `dem_m` و `slope_deg` همچنان نوشته می‌شوند، اما عملاً با `None`
- metadata هنوز `slope_supported` را ذخیره می‌کند، در حالی که openEO service دیگر این مفهوم را برنمی‌گرداند

### مشکل 4: progress metricها با metricهای واقعی اجراشده mismatch دارد

فایل:

- `location_data/tasks.py`

نمونه mismatch:

- remote metric واقعی: `soil_vv`
- feature مورد انتظار برای clustering: `soil_vv_db`

اثر:

- `completed_metrics` و `states` با هم ناسازگار می‌شوند
- `soil_vv_db` در response به شکل `pending` دیده می‌شود، با اینکه از `soil_vv` مشتق می‌شود

### مشکل 5: stage و status همیشه هماهنگ نیستند

در payload فعلی:

- `run.status_label = failed`
- `run.stage = observations_persisted`

اثر:

- client نمی‌فهمد خطا دقیقاً در کدام stage رخ داده
- `task.current_stage` هم گمراه‌کننده می‌شود

### مشکل 6: DB failure و Celery retry همزمان به client داده می‌شود

اثر:

- API برای یک run همزمان پیام `failed` و `RETRY` می‌دهد
- از نگاه UX، کاربر نمی‌داند باید retry خودکار را صبر کند یا خطا را final بداند

---

## 12) جمع‌بندی عملی برای همین خطا

بر اساس payload و کد، نتیجه عملی این است:

1. openEO jobها برای `ndvi`, `ndwi`, `soil_vv` اجرا شده‌اند
2. 12 observation در DB ساخته شده‌اند
3. pipeline وارد مرحله clustering شده
4. clustering هیچ observation usable پیدا نکرده
5. run در DB به حالت `failed` رفته
6. اما Celery هنوز task را در حالت `RETRY` نگه داشته

---

## 13) پیشنهادهای اصلاح

### اصلاح ضروری

1. `dem_m` و `slope_deg` را از این نقاط هم حذف کنید:
   - `location_data/data_driven_subdivision.py`
   - `location_data/serializers.py`
   - `location_data/views.py`
   - `location_data/tasks.py`

2. `DEFAULT_CLUSTER_FEATURES` را با metricهای واقعی sync کنید:
   - `ndvi`
   - `ndwi`
   - `soil_vv_db`

3. progress tracker را طوری اصلاح کنید که:
   - completion `soil_vv` به completion `soil_vv_db` هم map شود
   - metricهای حذف‌شده اصلاً در state دیده نشوند

4. منطق status endpoint را طوری تنظیم کنید که:
   - اگر Celery در `RETRY` است، status client نهایی `failed` نباشد
   - یا حداقل یک فیلد صریح مثل `final_status=false` برگردد

5. قبل از clustering، یک diagnostic بهتر ذخیره شود:
   - تعداد observationهای usable
   - تعداد observationهای all-null
   - تعداد null برای هر feature

### اصلاح پیشنهادی برای debug بهتر

در `stage_details` این موارد اضافه شود:

- `usable_observation_count`
- `all_features_missing_cell_codes`
- `feature_null_counts`
- `selected_features_effective`

---

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

این run به احتمال زیاد به خاطر ناسازگاری بین featureهای مورد انتظار pipeline و featureهای واقعاً تولید/ذخیره‌شده شکست خورده است، و علاوه بر آن چند inconsistency مهم در `location_data` وجود دارد:

- metricهای حذف‌شده هنوز در feature contract حضور دارند
- progress report با metricهای واقعی sync نیست
- DB status و Celery status با هم تناقض دارند
- stage نهایی برای failure به شکل واضح و قابل اتکا به client نمایش داده نمی‌شود

اگر بخواهید، مرحله بعدی می‌تواند این باشد که همین موارد را در کد هم اصلاح کنیم، نه فقط مستندسازی.
