# گزارش تحلیل خطای `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`, `lst_c`, `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` - `lst_c` - `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`, `lst_c`, `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` - `lst_c` - `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 نمایش داده نمی‌شود اگر بخواهید، مرحله بعدی می‌تواند این باشد که همین موارد را در کد هم اصلاح کنیم، نه فقط مستندسازی.