30 KiB
Crop Zoning Code Logic
این فایل یک توضیح کامل و شفاف از منطق سه فایل زیر است:
crop_zoning/views.pycrop_zoning/services.pycrop_zoning/tests.py
هدف این داکیومنت این است که بدون نیاز به خواندن مستقیم کد، بتوان فهمید هر endpoint چه میکند، دادهها چگونه ساخته میشوند، taskها چگونه مدیریت میشوند، و تستها چه رفتارهایی را پوشش میدهند.
تصویر کلی ماژول
ماژول crop_zoning برای این ساخته شده که:
- یک polygon مربوط به زمین را دریافت یا پیدا کند.
- آن را به چند zone مربعی تقسیم کند.
- برای هر zone داده اولیه تولید کند.
- برای هر zone یک task پردازش جداگانه ثبت کند.
- خروجی مناسب برای فرانت برگرداند تا هم وضعیت پردازش را بداند و هم zoneها را روی نقشه نمایش دهد.
این ماژول دو نوع داده برای zoneها دارد:
- داده اولیه و rule-based که سریع ساخته میشود و برای خالی نبودن UI استفاده میشود.
- داده تحلیلی که بعدا از طریق task و داده خاک تکمیل میشود.
منطق crop_zoning/views.py
فایل views.py فقط لایه HTTP است.
یعنی کار اصلی را خودش انجام نمیدهد، بلکه:
- ورودی request را میخواند
- آن را validate میکند یا به serviceها میسپارد
- خروجی مناسب را به صورت JSON response برمیگرداند
1) AreaView
این مهمترین endpoint ماژول است.
کار این view
sensor_uuidرا از query params میگیرد.pageوpage_sizeرا هم از query params میگیرد.- از service میخواهد مطمئن شود برای این sensor یک area معتبر و zoneهای آن وجود دارند.
- اگر zoneها وجود نداشته باشند، ساخته میشوند.
- اگر taskهای پردازش لازم باشند، dispatch میشوند.
- در نهایت خروجی area + zoneهای همان صفحه + اطلاعات pagination را برمیگرداند.
ورودیهای AreaView
sensor_uuid: اجباریpage: اختیاری، پیشفرض1page_size: اختیاری، پیشفرض10
خروجی AreaView
خروجی سه بخش مهم دارد:
task: وضعیت پردازش کل areaarea: polygon اصلی زمینzones: فقط zoneهای مربوط به همان صفحهpagination: اطلاعات صفحهبندی zoneها
مدیریت خطا در AreaView
اگر هر کدام از این موارد رخ بدهد، خطای 400 داده میشود:
sensor_uuidارسال نشده باشدsensor_uuidمعتبر نباشد یا sensor پیدا نشودpageنامعتبر باشدpage_sizeنامعتبر باشد
اگر تنظیمات سمت سرور مشکل داشته باشند، خطای 500 داده میشود.
2) ProductsView
این endpoint لیست محصولات قابل کشت را برمیگرداند.
کار این view
- از service میخواهد محصولات پیشفرض داخل دیتابیس sync شوند.
- سپس لیست محصولات را به فرمت مناسب فرانت برمیگرداند.
این view ساده است و منطق تحلیلی ندارد.
3) ZonesInitialView
این view برای ساخت zoneها از روی یک polygon ورودی استفاده میشود.
کار این view
- polygon را از یکی از این کلیدها میگیرد:
areaarea_geojsonboundary
- اگر هیچکدام نباشد، از area پیشفرض mock استفاده میکند.
- در صورت ارسال،
cell_side_kmرا هم میگیرد. - service را صدا میزند تا area و zoneها ساخته شوند.
- response اولیه zoneها را برمیگرداند.
تفاوت با AreaView
AreaViewبر اساسsensor_uuidکار میکند و وضعیت taskها را هم برمیگرداند.ZonesInitialViewبیشتر برای ساخت اولیه zoneها از روی polygon مناسب است.
4) ZonesWaterNeedView
این view لایه نیاز آبی zoneها را برمیگرداند.
کار این view
- از request،
zoneIdsرا میگیرد. - service را صدا میزند.
- برای هر zone، level و value و color مربوط به آب را برمیگرداند.
5) ZonesSoilQualityView
این view لایه کیفیت خاک zoneها را برمیگرداند.
خروجی اصلی
برای هر zone:
levelscorecolor
6) ZonesCultivationRiskView
این view لایه ریسک کشت zoneها را برمیگرداند.
خروجی اصلی
برای هر zone:
levelcolor
7) ZoneDetailsView
این endpoint جزئیات یک zone را برمیگرداند.
کار این view
zone_idرا از URL میگیرد.- جزئیات recommendation آن zone را از service میخواند.
- اگر zone پیدا نشود،
404برمیگرداند.
خروجی اصلی
- crop پیشنهادی
- درصد تطابق
- نیاز آبی
- سود تخمینی
- reason
- criteria
- مساحت zone
منطق crop_zoning/services.py
این فایل قلب اصلی ماژول است. بیشتر منطق واقعی اینجا پیادهسازی شده.
برای فهم بهتر، این فایل را میتوان به 8 بخش تقسیم کرد.
بخش 1: تنظیمات و utilityهای اولیه
ثابتها
چند constant اصلی در ابتدای فایل تعریف شدهاند:
DEFAULT_CELL_SIDE_KM: اندازه پیشفرض ضلع هر zoneDEFAULT_ZONE_PAGE_SIZE: تعداد پیشفرض zoneها در هر صفحه responseRULE_BASED_ALGORITHM: نام الگوریتم rule-basedRULE_BASED_PRODUCTS: داده اولیه محصولات و اطلاعات نمایشی آنها
get_default_cell_side_km()
این تابع اندازه پیشفرض ضلع هر zone را مشخص میکند.
اولویتها:
- اگر
CROP_ZONE_CELL_SIDE_KMدر settings وجود داشته باشد، همان استفاده میشود. - اگر نبود، از
CROP_ZONE_CHUNK_AREA_SQMاستفاده میکند و از روی آن ضلع مربع را حساب میکند. - اگر هیچکدام نباشند، از
DEFAULT_CELL_SIDE_KMاستفاده میشود.
get_task_stale_seconds()
این تابع مشخص میکند بعد از چند ثانیه یک task ممکن است stale محسوب شود. یعنی اگر task گیر کرده باشد، دوباره dispatch شود.
get_cell_side_km(cell_side_km=None)
اگر کاربر اندازه zone را داده باشد، آن را validate میکند. اگر نداده باشد، مقدار پیشفرض را برمیگرداند.
get_chunk_area_sqm(cell_side_km=None)
مساحت zone را از روی ضلع آن حساب میکند:
- ضلع بر حسب کیلومتر دریافت میشود
- به متر تبدیل میشود
- مربع آن به عنوان مساحت zone برگردانده میشود
parse_positive_int(...)
برای validate کردن پارامترهای عددی مثبت استفاده میشود.
الان برای page و page_size استفاده میشود.
get_zone_page_request_params(query_params)
این تابع پارامترهای pagination را از query params میگیرد:
pagepage_size
اگر ارسال نشده باشند، از default استفاده میکند.
اگر نامعتبر باشند، ValueError میدهد.
بخش 2: آمادهسازی polygon و محاسبات هندسی
این بخش مسئول کار با GeoJSON و polygon است.
get_default_area_feature()
یک area پیشفرض از داده mock برمیگرداند.
normalize_area_feature(area_feature)
این تابع ورودی area را normalize میکند تا همیشه ساختار Feature داشته باشد.
کارهای این تابع
- بررسی میکند ورودی null نباشد
- بررسی میکند ورودی dict باشد
- اگر ورودی از نوع
Featureنباشد، آن را بهFeatureتبدیل میکند - بررسی میکند geometry از نوع
Polygonباشد - بررسی میکند polygon حداقل 4 نقطه داشته باشد
get_polygon_ring(area_feature)
حلقه اصلی polygon را استخراج میکند.
polygon_area_sqm(ring)
مساحت polygon را به متر مربع حساب میکند. برای این کار نقاط جغرافیایی را به مختصات مسطح تقریبی تبدیل میکند و فرمول shoelace را اجرا میکند.
normalize_points(ring)
اگر آخر polygon با نقطه اول بسته شده باشد، نقطه تکراری آخر را حذف میکند.
calculate_center(points)
مرکز تقریبی polygon یا مربع را از میانگین نقاط حساب میکند.
get_bbox(points)
کمینه و بیشینه طول و عرض جغرافیایی را برمیگرداند تا محدوده کلی polygon مشخص شود.
meters_to_latitude_delta(meters) و meters_to_longitude_delta(meters, latitude)
این دو تابع فاصله متر را به اختلاف latitude و longitude تبدیل میکنند. برای ساخت grid مربعی از این دو تابع استفاده میشود.
بخش 3: تشخیص برخورد polygon و cell
این بخش مشخص میکند که آیا یک مربع grid واقعا با polygon زمین برخورد دارد یا نه.
point_in_polygon(point, polygon_points)
چک میکند یک نقطه داخل polygon هست یا نه.
_orientation, _on_segment, segments_intersect
این توابع utilityهای هندسی برای تشخیص برخورد دو خط هستند.
rectangle_contains_point(point, cell_points)
چک میکند یک نقطه داخل مربع cell قرار دارد یا نه.
polygon_intersects_cell(polygon_points, cell_points)
این مهمترین تابع تقاطع است. اگر یکی از شرایط زیر برقرار باشد، cell معتبر در نظر گرفته میشود:
- مرکز cell داخل polygon باشد
- یکی از گوشههای cell داخل polygon باشد
- یکی از نقاط polygon داخل cell باشد
- یکی از اضلاع polygon با اضلاع cell برخورد داشته باشد
نتیجه: فقط مربعهایی zone میشوند که واقعا با زمین همپوشانی داشته باشند.
بخش 4: ساخت zoneها از روی area
build_square_points(...)
چهار گوشه یک مربع را از روی مرزهای آن میسازد.
build_zone_square(area_points, center, zone_area_sqm)
اگر area خیلی کوچک باشد یا zoneی تولید نشود، یک مربع fallback حول center area ساخته میشود.
split_area_into_zones(area_feature, cell_side_km=None)
این تابع مهمترین بخش ساخت zoneها است.
مراحل اجرای آن
- polygon area را میگیرد.
- center و bbox و total area را حساب میکند.
- اندازه ضلع zone را مشخص میکند.
- روی bbox یک grid مربعی میسازد.
- هر cell را با
polygon_intersects_cellبررسی میکند. - اگر cell با polygon تقاطع داشته باشد، یک zone جدید میسازد.
- برای هر zone این دادهها تولید میشود:
zone_idgeometrypointscenterarea_sqmarea_hectaressequence
- اگر هیچ zoneی ساخته نشود، یک zone fallback میسازد.
- در نهایت area summary و لیست zoneها را برمیگرداند.
نکته مهم
در این پروژه zoneها grid-based هستند، نه بر اساس تقسیم واقعی shape زمین. یعنی ابتدا grid مربعی ساخته میشود و بعد فقط مربعهایی که با زمین برخورد دارند نگه داشته میشوند.
بخش 5: تولید recommendation و لایههای تحلیلی
این بخش داده پیشنهادی هر zone را تولید میکند.
build_rule_based_zone_metrics(index, coords)
این تابع بدون نیاز به API خارجی، برای هر zone یک recommendation اولیه میسازد.
هدف آن
وقتی zone تازه ساخته میشود، فرانت از همان ابتدا داده خالی نداشته باشد.
خروجی آن
recommended_cropmatch_percentwater_need_levelwater_need_valuesoil_quality_scoresoil_levelcultivation_risk_levelestimated_profitreasoncriteria
این دادهها از روی مختصات zone و sequence به صورت deterministic ساخته میشوند.
build_initial_zone_payload(zone)
خروجی سبک و اولیه برای endpoint ساخت zoneها تولید میکند.
build_area_zone_payload(zone)
خروجی کاملتر برای AreaView تولید میکند و این بخشها را شامل میشود:
- geometry
- center
- area
- processing status
- crop recommendation
- water layer
- soil layer
- risk layer
persist_zone_analysis_metrics(zone, metrics)
metrics را داخل مدلهای مختلف ذخیره میکند:
- recommendation
- criteria
- water need layer
- soil quality layer
- cultivation risk layer
ensure_rule_based_zone_data(zone, force=False)
اگر zone هنوز recommendation نداشته باشد، با rule-based data آن را پر میکند.
بخش 6: تحلیل خاک واقعی و ذخیره نتیجه
_get_level_color_map(...)
رنگ هر level را برای سه لایه water / soil / risk برمیگرداند.
_pick_level(...)
از روی score مشخص میکند level برابر low یا medium یا high است.
_format_range(...)
برای ساخت رشتههایی مثل 3000-4000 m³/ha استفاده میشود.
_derive_analysis_metrics(depths)
این تابع از داده depthهای خاک، recommendation نهایی را میسازد.
ورودی آن
آرایهای از depthها که از سرویس خارجی خاک میآید.
محاسبات مهم آن
از میانگین این فیلدها استفاده میکند:
phh2osocclaynitrogenwv0033
بعد از اینها محاسبه میشود:
- کیفیت خاک
- نیاز آبی
- ریسک کشت
- محصول پیشنهادی
- درصد تطابق
- reason
- criteria
fetch_soil_data_for_zone(zone)
برای یک zone به سرویس خارجی AI درخواست میزند و داده خاک میگیرد.
payload ارسالی
- longitude
- latitude
- geometry zone
- center
- area
analyze_and_store_zone_soil_data(zone_id)
این تابع منطق اصلی پردازش هر zone در worker است.
مراحل آن
- zone از دیتابیس خوانده میشود.
- اگر قبلا کامل شده باشد، دوباره کاری نمیکند.
- status روی
processingمیرود. - از API خارجی داده خاک میگیرد.
- depthها را استخراج میکند.
- recommendation واقعیتر را از روی خاک میسازد.
- نتیجه را داخل مدلهای analysis و recommendation ذخیره میکند.
- status را
completedمیکند. - اگر هر خطایی رخ دهد، status روی
failedمیرود و متن خطا ذخیره میشود.
بخش 7: مدیریت taskهای zone
چون هر zone جداگانه پردازش میشود، باید taskها مدیریت شوند.
_get_stale_zone_ids(zones)
این تابع zoneهایی را پیدا میکند که task آنها stale شده است.
چه zoneهایی stale محسوب میشوند؟
- zone کامل نشده باشد
- task_id داشته باشد
- task خیلی قدیمی شده باشد
- یا task_id آن با task یک zone completed مشترک باشد
- یا state task در celery یکی از stateهای نامعتبر برای ادامه باشد
dispatch_zone_processing_tasks(crop_area_id=None, zone_ids=None, force=False)
این تابع برای zoneهای انتخابشده task celery ثبت میکند.
رفتار آن
- zoneهای completed را رد میکند
- اگر zone pending/processing باشد و task_id معتبر داشته باشد، دوباره dispatch نمیکند مگر
force=True - برای هر zone یک task جدا ثبت میکند
- اگر celery broker در دسترس نباشد، باز هم یک
task_idمحلی تولید میکند - متن خطا را در
processing_errorذخیره میکند
اهمیت این طراحی
این باعث میشود:
- هر zone مستقل پردازش شود
- fail شدن یک zone بقیه را متوقف نکند
- فرانت بتواند وضعیت هر zone را جدا ببیند
بخش 8: ساخت area، بازیابی area و ساخت payload response
create_missing_zones_for_area(crop_area)
اگر area در دیتابیس وجود داشته باشد ولی zoneهایش از بین رفته باشند یا ساخته نشده باشند، دوباره از روی geometry آن zoneها را میسازد.
get_sensor_for_uuid(sensor_uuid)
اعتبارسنجی میکند که:
sensor_uuidارسال شده باشد- sensor واقعا در دیتابیس وجود داشته باشد
ensure_latest_area_ready_for_processing(sensor_uuid, area_feature=None)
این یکی از مهمترین توابع کل فایل است.
منطق آن
- sensor را پیدا میکند.
- آخرین area مربوط به آن sensor را میگیرد.
- اگر area وجود نداشته باشد:
- area پیشفرض یا area ورودی را میگیرد
- area و zoneها را میسازد
- taskها را dispatch میکند
- اگر area وجود داشته باشد:
- مطمئن میشود zoneها وجود دارند
- برای هر zone، rule-based data را در صورت نبود ایجاد میکند
- zoneهای stale را پیدا میکند
- zoneهای لازم را دوباره dispatch میکند
- area تازه از دیتابیس خوانده میشود و برگردانده میشود
نتیجه این تابع
وقتی AreaView این تابع را صدا میزند، همیشه یک area آماده برای نمایش و پردازش دارد.
create_zones_and_dispatch(area_feature, cell_side_km=None, sensor=None)
این تابع area جدید را میسازد.
مراحل آن
- productها sync میشوند.
- area normalize میشود.
- area به zoneها تقسیم میشود.
- داخل transaction:
- یک
CropAreaساخته میشود - همه
CropZoneها bulk create میشوند
- یک
- zoneها دوباره از دیتابیس خوانده میشوند
- rule-based data برای هر zone ساخته میشود
- taskهای پردازش dispatch میشوند
- area و zones برگردانده میشوند
_zones_queryset(zone_ids=None)
یک queryset آماده برمیگرداند که relationهای لازم را از قبل load میکند:
- recommendation
- product
- criteria
- water layer
- soil layer
- risk layer
این باعث میشود responseسازی سریعتر و با query کمتر انجام شود.
get_latest_area_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE)
این تابع خروجی نهایی AreaView را میسازد.
کارهای این تابع
- area را پیدا میکند.
- وضعیت همه zoneها را میخواند.
- تعداد completed / pending / processing / failed را حساب میکند.
task.statusرا تعیین میکند.stageوstage_labelرا تعیین میکند.- درصد پیشرفت را حساب میکند.
- zoneهای همان صفحه را با slicing برمیدارد.
paginationرا میسازد.- payload نهایی را برمیگرداند.
منطق task.status
- اگر zone failed داشته باشیم:
FAILURE - اگر همه complete باشند:
SUCCESS - اگر بخشی complete یا processing باشند:
PROCESSING - در غیر این صورت:
PENDING
منطق pagination
pageوpage_sizeاز request گرفته میشوندtotal_pagesاز تقسیم تعداد کل zoneها برpage_sizeمحاسبه میشود- فقط zoneهای همان بازه برگردانده میشوند
- اطلاعات page فعلی، تعداد صفحات و وجود صفحه قبل/بعد در body قرار میگیرد
get_initial_zones_payload(crop_area)
payload سادهتر برای endpoint اولیه zoneها میسازد.
get_water_need_payload(zone_ids=None)
خروجی لایه نیاز آبی را برمیگرداند.
get_soil_quality_payload(zone_ids=None)
خروجی لایه کیفیت خاک را برمیگرداند.
get_cultivation_risk_payload(zone_ids=None)
خروجی لایه ریسک کشت را برمیگرداند.
get_zone_details_payload(zone_id)
خروجی دیتیل یک zone را میسازد.
جریان کامل اجرای GET /api/crop-zoning/area/
اگر بخواهیم کل flow را از ابتدا تا انتها خیلی ساده توضیح بدهیم:
- فرانت
sensor_uuidو احتمالاpageوpage_sizeرا میفرستد. AreaViewپارامترها را میخواند.ensure_latest_area_ready_for_processingاجرا میشود.- اگر area وجود نداشته باشد، area و zoneها ساخته میشوند.
- اگر zoneها ناقص باشند، کامل میشوند.
- اگر recommendation اولیه نباشد، ساخته میشود.
- اگر taskهای لازم وجود نداشته باشند یا stale باشند، dispatch میشوند.
get_latest_area_payloadاجرا میشود.- وضعیت کلی task و zoneهای صفحه فعلی ساخته میشود.
- response نهایی به فرانت برمیگردد.
منطق crop_zoning/tests.py
این فایل تست رفتار کلیدی API را پوشش میدهد.
تستها با Django TestCase و APIRequestFactory نوشته شدهاند.
تنظیمات مشترک تستها
در تستها از این تنظیمات استفاده شده:
USE_EXTERNAL_API_MOCK=TrueCROP_ZONE_CHUNK_AREA_SQM=200000
هدف این است که:
- وابستگی به API خارجی واقعی حذف شود
- zoneها با اندازه مشخص و قابل پیشبینی ساخته شوند
کلاس ZonesInitialViewTests
test_post_accepts_area_geojson_alias
این تست بررسی میکند که اگر polygon با کلید area_geojson ارسال شود:
- endpoint آن را قبول کند
- پاسخ
200بدهد - zone ساخته شود
- تعداد zoneهای خروجی با
zone_countیکسان باشد
این تست در عمل alias بودن area_geojson را validate میکند.
کلاس AreaViewTests
این کلاس رفتارهای اصلی AreaView را تست میکند.
setUp
در شروع هر تست:
- یک user ساخته میشود
- یک sensor برای آن user ساخته میشود
APIRequestFactoryآماده میشود
_create_area(...)
یک helper برای ساخت سریع CropArea در تستها است.
_request()
یک request استاندارد برای AreaView با sensor_uuid معتبر میسازد.
_request_with_pagination(page, page_size)
یک request برای تست pagination میسازد.
تستهای اصلی AreaView
test_get_requires_sensor_uuid
بررسی میکند اگر sensor_uuid ارسال نشود، پاسخ 400 برگردد.
test_get_returns_pending_task_status_until_all_zones_complete
بررسی میکند اگر zoneها pending و processing باشند:
- status کلی
PROCESSINGباشد - area برگردد
- zoneها در response باشند
- فیلد
processing_statusبرای zone موجود باشد
test_get_returns_area_when_all_tasks_complete
بررسی میکند وقتی همه zoneها complete باشند:
- status کلی
SUCCESSباشد - zoneها برگردند
- فیلدهای recommendation و layerها موجود باشند
test_get_returns_paginated_zones
تست جدید pagination است.
بررسی میکند که:
- با
page=2وpage_size=1 - فقط zone دوم برگردد
- اطلاعات pagination درست باشد
total_pages,has_next,has_previousدرست باشند
test_get_rejects_invalid_pagination_params
بررسی میکند اگر page=0 باشد:
- پاسخ
400بدهد - پیام خطا مناسب برگردد
test_get_dispatches_zone_task_when_task_id_is_missing
با mock کردن dispatch_zone_processing_tasks بررسی میکند که:
- اگر zone task_id نداشته باشد
- در زمان فراخوانی
AreaView - dispatch انجام شود
test_get_creates_area_when_sensor_has_no_data
با mock کردن create_zones_and_dispatch بررسی میکند که:
- اگر sensor هنوز area نداشته باشد
- سیستم area جدید بسازد
- همان sensor را به service پاس بدهد
test_each_zone_gets_its_own_task
بررسی میکند برای دو zone جدا:
- دو task مستقل ایجاد شود
- هر zone task_id جدا داشته باشد
این تست خیلی مهم است چون تایید میکند taskها shared نیستند و per-zone هستند.
test_get_generates_local_task_id_when_broker_is_unavailable
با mock کردن celery و ایجاد OperationalError بررسی میکند که:
- حتی وقتی broker در دسترس نیست
- سیستم task_id محلی بسازد
- response خراب نشود
- وضعیت کلی درست بماند
test_get_stores_task_id_and_reuses_it_on_next_request
بررسی میکند:
- وقتی اولین request task_id را ثبت کرد
- request بعدی دوباره task تازه نسازد
- همان task_id قبلی reuse شود
این تست جلوی dispatch تکراری را میگیرد.
test_get_redispatches_pending_zone_when_shared_task_already_completed
این تست سناریوی قدیمی یا خراب را پوشش میدهد.
سناریو:
- یک zone completed شده
- zone دیگر pending مانده
- هر دو task_id یکسان دارند
در این حالت سیستم باید zone stale را دوباره dispatch کند.
این تست نشان میدهد منطق stale detection واقعا کار میکند.
جمعبندی معماری
اگر خیلی خلاصه بخواهیم نقش هر فایل را بگوییم:
views.py
لایه HTTP است.
- request را میگیرد
- service مناسب را صدا میزند
- response برمیگرداند
services.py
لایه business logic است.
- area را validate میکند
- polygon را به zone تبدیل میکند
- recommendation اولیه و نهایی میسازد
- taskها را مدیریت میکند
- payload response را میسازد
tests.py
لایه اطمینان از رفتار سیستم است.
- ساخت area
- ساخت zone
- status task
- dispatch task
- stale task
- pagination
- خطاهای ورودی
را تست میکند.
نکات مهم برای فرانت
- endpoint
areaالان pagination دارد وzonesهمیشه همه zoneها را برنمیگرداند. - تعداد کل zoneها از
task.total_zonesیاpagination.total_zonesقابل خواندن است. - تعداد کل صفحهها از
pagination.total_pagesقابل خواندن است. - برای نمایش progress باید از
task.progress_percentوtask.statusاستفاده شود. task.statusوضعیت کلی area است، نه وضعیت تکتک zoneها.- وضعیت هر zone داخل
processing_statusقرار دارد. - در صورت نیاز به جزئیات بیشتر برای یک zone باید
ZoneDetailsViewصدا زده شود.
نکات مهم برای بکاند
- منطق grid سازی و پردازش zoneها تقریبا کامل داخل
services.pyمتمرکز شده است. AreaViewعمدا thin نگه داشته شده تا business logic وارد view نشود.- rule-based data نقش fallback سریع برای UI را دارد.
- data واقعیتر بعدا با taskهای تحلیل خاک جایگزین یا تکمیل میشود.
- تستها بیشتر روی پایداری flow پردازش و task dispatch تمرکز دارند.