Files
Ai/docs/location_data_remote_sensing_failure_report.md
T

445 lines
18 KiB
Markdown
Raw Normal View History

2026-05-10 22:49:07 +03:30
# گزارش تحلیل خطای `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 |
2026-05-11 00:36:02 +03:30
| `selected_features` | `ndvi`, `ndwi`, `soil_vv_db`, `dem_m`, `slope_deg` | featureهایی که pipeline فکر می‌کند برای clustering لازم‌اند |
2026-05-10 22:49:07 +03:30
| `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 و کد، نتیجه عملی این است:
2026-05-11 00:36:02 +03:30
1. openEO jobها برای `ndvi`, `ndwi`, `soil_vv` اجرا شده‌اند
2026-05-10 22:49:07 +03:30
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 نمایش داده نمی‌شود
اگر بخواهید، مرحله بعدی می‌تواند این باشد که همین موارد را در کد هم اصلاح کنیم، نه فقط مستندسازی.