UPDATE
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
# Crop Zoning Layer Area API Changes For Frontend
|
||||
|
||||
این فایل برای تیم فرانت نوشته شده و فقط تغییرات جدیدی را توضیح میدهد که برای endpointهای لایهای ماژول `crop-zoning` اضافه شدهاند.
|
||||
|
||||
## خلاصه تغییر
|
||||
|
||||
سه API جدید اضافه شدهاند که از نظر ساختار response دقیقا شبیه `GET /area/` هستند:
|
||||
|
||||
- `GET /api/crop-zoning/water-need/`
|
||||
- `GET /api/crop-zoning/soil-quality/`
|
||||
- `GET /api/crop-zoning/cultivation-risk/`
|
||||
|
||||
هر سه endpoint:
|
||||
|
||||
- به `farm_uuid` نیاز دارند
|
||||
- از `page` و `page_size` پشتیبانی میکنند
|
||||
- در صورت نبود داده، همان روند ساخت area و zone و dispatch task را مثل `area/` انجام میدهند
|
||||
- همان ساختار `task`, `area`, `zones`, `pagination` را برمیگردانند
|
||||
|
||||
## هدف این تغییر
|
||||
|
||||
قبلا فرانت برای دادههای لایهای بیشتر به endpointهای `zones/...` متکی بود که خروجی آنها فقط لیست زونها بود.
|
||||
الان برای هر لایه یک endpoint جدید دارید که خروجی آن برای صفحه map دقیقا با `area/` همفرمت است و استفاده در UI سادهتر میشود.
|
||||
|
||||
## Base Path
|
||||
|
||||
```text
|
||||
/api/crop-zoning/
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```http
|
||||
Authorization: Bearer <access_token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
## Endpointهای جدید
|
||||
|
||||
### 1) Water Need
|
||||
|
||||
```http
|
||||
GET /api/crop-zoning/water-need/?farm_uuid=<farm_uuid>&page=1&page_size=10
|
||||
```
|
||||
|
||||
### 2) Soil Quality
|
||||
|
||||
```http
|
||||
GET /api/crop-zoning/soil-quality/?farm_uuid=<farm_uuid>&page=1&page_size=10
|
||||
```
|
||||
|
||||
### 3) Cultivation Risk
|
||||
|
||||
```http
|
||||
GET /api/crop-zoning/cultivation-risk/?farm_uuid=<farm_uuid>&page=1&page_size=10
|
||||
```
|
||||
|
||||
## Query Params
|
||||
|
||||
- `farm_uuid`: اجباری، UUID مزرعه
|
||||
- `page`: اختیاری، شماره صفحه زونها، پیشفرض `1`
|
||||
- `page_size`: اختیاری، تعداد زون در هر صفحه، پیشفرض `10`
|
||||
|
||||
## ساختار کلی response
|
||||
|
||||
ساختار کلی هر سه API:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"task": {},
|
||||
"area": {},
|
||||
"zones": [],
|
||||
"pagination": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
یعنی برای فرانت:
|
||||
|
||||
- `task` دقیقا مثل `area/` است
|
||||
- `area` دقیقا مثل `area/` است
|
||||
- `pagination` دقیقا مثل `area/` است
|
||||
- فقط shape آیتمهای داخل `zones` بر اساس نوع لایه فرق میکند
|
||||
|
||||
## تفاوت `zones` در هر endpoint
|
||||
|
||||
### `GET /water-need/`
|
||||
|
||||
هر آیتم در `zones` این فیلدها را دارد:
|
||||
|
||||
- `zoneId`
|
||||
- `zoneUuid`
|
||||
- `geometry`
|
||||
- `center`
|
||||
- `area_sqm`
|
||||
- `area_hectares`
|
||||
- `sequence`
|
||||
- `processing_status`
|
||||
- `processing_error`
|
||||
- `waterNeedLayer`
|
||||
|
||||
نمونه:
|
||||
|
||||
```json
|
||||
{
|
||||
"zoneId": "zone-0",
|
||||
"zoneUuid": "d7a9a19b-b3ec-4721-b514-9aae5c9ea940",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[[51.384258, 35.689389], [51.38536404, 35.689389], [51.38536404, 35.69028731], [51.384258, 35.69028731], [51.384258, 35.689389]]]
|
||||
},
|
||||
"center": {
|
||||
"latitude": 35.68983816,
|
||||
"longitude": 51.38481102
|
||||
},
|
||||
"area_sqm": 9999.91,
|
||||
"area_hectares": 1,
|
||||
"sequence": 0,
|
||||
"processing_status": "completed",
|
||||
"processing_error": "",
|
||||
"waterNeedLayer": {
|
||||
"level": "medium",
|
||||
"value": "4820-5820 m³/ha",
|
||||
"color": "#0ea5e9"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /soil-quality/`
|
||||
|
||||
هر آیتم در `zones` این فیلدها را دارد:
|
||||
|
||||
- `zoneId`
|
||||
- `zoneUuid`
|
||||
- `geometry`
|
||||
- `center`
|
||||
- `area_sqm`
|
||||
- `area_hectares`
|
||||
- `sequence`
|
||||
- `processing_status`
|
||||
- `processing_error`
|
||||
- `soilQualityLayer`
|
||||
|
||||
نمونه:
|
||||
|
||||
```json
|
||||
{
|
||||
"zoneId": "zone-0",
|
||||
"zoneUuid": "d7a9a19b-b3ec-4721-b514-9aae5c9ea940",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[[51.384258, 35.689389], [51.38536404, 35.689389], [51.38536404, 35.69028731], [51.384258, 35.69028731], [51.384258, 35.689389]]]
|
||||
},
|
||||
"center": {
|
||||
"latitude": 35.68983816,
|
||||
"longitude": 51.38481102
|
||||
},
|
||||
"area_sqm": 9999.91,
|
||||
"area_hectares": 1,
|
||||
"sequence": 0,
|
||||
"processing_status": "completed",
|
||||
"processing_error": "",
|
||||
"soilQualityLayer": {
|
||||
"level": "high",
|
||||
"score": 89,
|
||||
"color": "#22c55e"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /cultivation-risk/`
|
||||
|
||||
هر آیتم در `zones` این فیلدها را دارد:
|
||||
|
||||
- `zoneId`
|
||||
- `zoneUuid`
|
||||
- `geometry`
|
||||
- `center`
|
||||
- `area_sqm`
|
||||
- `area_hectares`
|
||||
- `sequence`
|
||||
- `processing_status`
|
||||
- `processing_error`
|
||||
- `cultivationRiskLayer`
|
||||
|
||||
نمونه:
|
||||
|
||||
```json
|
||||
{
|
||||
"zoneId": "zone-0",
|
||||
"zoneUuid": "d7a9a19b-b3ec-4721-b514-9aae5c9ea940",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[[51.384258, 35.689389], [51.38536404, 35.689389], [51.38536404, 35.69028731], [51.384258, 35.69028731], [51.384258, 35.689389]]]
|
||||
},
|
||||
"center": {
|
||||
"latitude": 35.68983816,
|
||||
"longitude": 51.38481102
|
||||
},
|
||||
"area_sqm": 9999.91,
|
||||
"area_hectares": 1,
|
||||
"sequence": 0,
|
||||
"processing_status": "completed",
|
||||
"processing_error": "",
|
||||
"cultivationRiskLayer": {
|
||||
"level": "low",
|
||||
"color": "#22c55e"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## نکته مهم برای فرانت
|
||||
|
||||
این endpointها عمدا شبیه `area/` طراحی شدهاند تا فرانت بتواند با یک data flow یکسان کار کند:
|
||||
|
||||
- polygon اصلی را از `data.area` بگیرد
|
||||
- task status را از `data.task` بخواند
|
||||
- pagination را از `data.pagination` بخواند
|
||||
- فقط renderer مربوط به هر لایه را روی `data.zones` اعمال کند
|
||||
|
||||
## پیشنهاد استفاده در UI
|
||||
|
||||
### اگر صفحه overview اصلی دارید
|
||||
|
||||
- همچنان `GET /area/` بهترین گزینه برای صفحه overview کامل است، چون علاوه بر layerها فیلدهای crop و recommendation را هم داخل هر zone دارد.
|
||||
|
||||
### اگر صفحه یا tab مخصوص هر layer دارید
|
||||
|
||||
- برای تب نیاز آبی: `GET /water-need/`
|
||||
- برای تب کیفیت خاک: `GET /soil-quality/`
|
||||
- برای تب ریسک کشت: `GET /cultivation-risk/`
|
||||
|
||||
این کار باعث میشود فرانت فقط داده موردنیاز همان layer را بگیرد.
|
||||
|
||||
## وضعیت backward compatibility
|
||||
|
||||
- endpoint قدیمی `GET /area/` بدون تغییر باقی مانده است
|
||||
- endpointهای جدید breaking change ایجاد نمیکنند
|
||||
- فقط سه مسیر جدید به API اضافه شده است
|
||||
|
||||
## خطاها
|
||||
|
||||
رفتار خطاها مثل `area/` است.
|
||||
|
||||
### نبودن `farm_uuid`
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "farm_uuid is required."
|
||||
}
|
||||
```
|
||||
|
||||
### پیدا نشدن مزرعه
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Farm not found."
|
||||
}
|
||||
```
|
||||
|
||||
### نامعتبر بودن `page` یا `page_size`
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "page must be a positive integer."
|
||||
}
|
||||
```
|
||||
|
||||
## جمعبندی
|
||||
|
||||
تغییر جدید برای فرانت این است که الان به جز `area/`، سه API جدید هم دارید که:
|
||||
|
||||
- از نظر query params شبیه `area/` هستند
|
||||
- از نظر response wrapper شبیه `area/` هستند
|
||||
- فقط payload داخلی `zones` را بر اساس نوع layer تخصصی میکنند
|
||||
|
||||
در نتیجه اگر UI شما برای `area/` آماده است، اتصال این سه endpoint جدید باید با کمترین تغییر انجام شود.
|
||||
+114
-37
@@ -514,20 +514,13 @@ def build_initial_zone_payload(zone):
|
||||
|
||||
|
||||
def build_area_zone_payload(zone):
|
||||
base_payload = _build_area_layer_zone_base_payload(zone)
|
||||
recommendation = getattr(zone, "recommendation", None)
|
||||
water_need_layer = getattr(zone, "water_need_layer", None)
|
||||
soil_quality_layer = getattr(zone, "soil_quality_layer", None)
|
||||
cultivation_risk_layer = getattr(zone, "cultivation_risk_layer", None)
|
||||
return {
|
||||
"zoneId": zone.zone_id,
|
||||
"zoneUuid": str(zone.uuid),
|
||||
"geometry": zone.geometry,
|
||||
"center": zone.center,
|
||||
"area_sqm": zone.area_sqm,
|
||||
"area_hectares": zone.area_hectares,
|
||||
"sequence": zone.sequence,
|
||||
"processing_status": zone.processing_status,
|
||||
"processing_error": zone.processing_error,
|
||||
base_payload.update(
|
||||
{
|
||||
"crop": recommendation.product.product_id if recommendation else "",
|
||||
"matchPercent": recommendation.match_percent if recommendation else 0,
|
||||
"waterNeed": recommendation.water_need if recommendation else "",
|
||||
@@ -547,6 +540,54 @@ def build_area_zone_payload(zone):
|
||||
"color": getattr(cultivation_risk_layer, "color", ""),
|
||||
},
|
||||
}
|
||||
)
|
||||
return base_payload
|
||||
|
||||
|
||||
def _build_area_layer_zone_base_payload(zone):
|
||||
return {
|
||||
"zoneId": zone.zone_id,
|
||||
"zoneUuid": str(zone.uuid),
|
||||
"geometry": zone.geometry,
|
||||
"center": zone.center,
|
||||
"area_sqm": zone.area_sqm,
|
||||
"area_hectares": zone.area_hectares,
|
||||
"sequence": zone.sequence,
|
||||
"processing_status": zone.processing_status,
|
||||
"processing_error": zone.processing_error,
|
||||
}
|
||||
|
||||
|
||||
def build_water_need_area_zone_payload(zone):
|
||||
base_payload = _build_area_layer_zone_base_payload(zone)
|
||||
water_need_layer = getattr(zone, "water_need_layer", None)
|
||||
base_payload["waterNeedLayer"] = {
|
||||
"level": getattr(water_need_layer, "level", ""),
|
||||
"value": getattr(water_need_layer, "value", ""),
|
||||
"color": getattr(water_need_layer, "color", ""),
|
||||
}
|
||||
return base_payload
|
||||
|
||||
|
||||
def build_soil_quality_area_zone_payload(zone):
|
||||
base_payload = _build_area_layer_zone_base_payload(zone)
|
||||
soil_quality_layer = getattr(zone, "soil_quality_layer", None)
|
||||
base_payload["soilQualityLayer"] = {
|
||||
"level": getattr(soil_quality_layer, "level", ""),
|
||||
"score": getattr(soil_quality_layer, "score", 0),
|
||||
"color": getattr(soil_quality_layer, "color", ""),
|
||||
}
|
||||
return base_payload
|
||||
|
||||
|
||||
def build_cultivation_risk_area_zone_payload(zone):
|
||||
base_payload = _build_area_layer_zone_base_payload(zone)
|
||||
cultivation_risk_layer = getattr(zone, "cultivation_risk_layer", None)
|
||||
base_payload["cultivationRiskLayer"] = {
|
||||
"level": getattr(cultivation_risk_layer, "level", ""),
|
||||
"color": getattr(cultivation_risk_layer, "color", ""),
|
||||
}
|
||||
return base_payload
|
||||
|
||||
|
||||
def persist_zone_analysis_metrics(zone, metrics):
|
||||
@@ -949,9 +990,38 @@ def _zones_queryset(zone_ids=None):
|
||||
return queryset
|
||||
|
||||
|
||||
def get_latest_area_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE):
|
||||
def _get_idle_area_payload(page, page_size):
|
||||
return {
|
||||
"task": {
|
||||
"status": "IDLE",
|
||||
"area_uuid": "",
|
||||
"total_zones": 0,
|
||||
"completed_zones": 0,
|
||||
"processing_zones": 0,
|
||||
"pending_zones": 0,
|
||||
"failed_zones": 0,
|
||||
"failed_zone_errors": [],
|
||||
"cell_side_km": round(get_default_cell_side_km(), 4),
|
||||
},
|
||||
"area": get_default_area_feature(),
|
||||
"zones": [],
|
||||
"pagination": {
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_pages": 0,
|
||||
"total_zones": 0,
|
||||
"returned_zones": 0,
|
||||
"has_next": False,
|
||||
"has_previous": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _build_latest_area_layer_payload(zone_builder, area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE):
|
||||
area = area or CropArea.objects.order_by("-created_at", "-id").first()
|
||||
if area:
|
||||
if not area:
|
||||
return _get_idle_area_payload(page, page_size)
|
||||
|
||||
status_zones = list(area.zones.only("zone_id", "task_id", "processing_status", "processing_error"))
|
||||
total_zones = len(status_zones)
|
||||
completed_zones = sum(1 for zone in status_zones if zone.processing_status == CropZone.STATUS_COMPLETED)
|
||||
@@ -1026,7 +1096,7 @@ def get_latest_area_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE)
|
||||
"cell_side_km": round(math.sqrt(max(area.chunk_area_sqm, 1)) / 1000.0, 4),
|
||||
},
|
||||
"area": area.geometry,
|
||||
"zones": [build_area_zone_payload(zone) for zone in zones],
|
||||
"zones": [zone_builder(zone) for zone in zones],
|
||||
"pagination": {
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
@@ -1037,30 +1107,37 @@ def get_latest_area_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE)
|
||||
"has_previous": page > 1 and total_pages > 0,
|
||||
},
|
||||
}
|
||||
return {
|
||||
"task": {
|
||||
"status": "IDLE",
|
||||
"area_uuid": "",
|
||||
"total_zones": 0,
|
||||
"completed_zones": 0,
|
||||
"processing_zones": 0,
|
||||
"pending_zones": 0,
|
||||
"failed_zones": 0,
|
||||
"failed_zone_errors": [],
|
||||
"cell_side_km": round(get_default_cell_side_km(), 4),
|
||||
},
|
||||
"area": get_default_area_feature(),
|
||||
"zones": [],
|
||||
"pagination": {
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_pages": 0,
|
||||
"total_zones": 0,
|
||||
"returned_zones": 0,
|
||||
"has_next": False,
|
||||
"has_previous": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def get_latest_area_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE):
|
||||
return _build_latest_area_layer_payload(build_area_zone_payload, area=area, page=page, page_size=page_size)
|
||||
|
||||
|
||||
def get_latest_water_need_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE):
|
||||
return _build_latest_area_layer_payload(
|
||||
build_water_need_area_zone_payload,
|
||||
area=area,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
|
||||
|
||||
def get_latest_soil_quality_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE):
|
||||
return _build_latest_area_layer_payload(
|
||||
build_soil_quality_area_zone_payload,
|
||||
area=area,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
|
||||
|
||||
def get_latest_cultivation_risk_payload(area=None, page=1, page_size=DEFAULT_ZONE_PAGE_SIZE):
|
||||
return _build_latest_area_layer_payload(
|
||||
build_cultivation_risk_area_zone_payload,
|
||||
area=area,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
|
||||
|
||||
def get_initial_zones_payload(crop_area):
|
||||
|
||||
+105
-1
@@ -8,7 +8,13 @@ from kombu.exceptions import OperationalError
|
||||
from rest_framework.test import APIRequestFactory, force_authenticate
|
||||
|
||||
from crop_zoning.models import CropArea, CropZone
|
||||
from crop_zoning.views import AreaView, ZonesInitialView
|
||||
from crop_zoning.views import (
|
||||
AreaView,
|
||||
CultivationRiskView,
|
||||
SoilQualityView,
|
||||
WaterNeedView,
|
||||
ZonesInitialView,
|
||||
)
|
||||
from farm_hub.models import FarmHub, FarmType
|
||||
|
||||
|
||||
@@ -313,3 +319,101 @@ class AreaViewTests(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
mock_dispatch.assert_called_once_with(zone_ids=[stale_zone.id], force=True)
|
||||
|
||||
|
||||
@override_settings(
|
||||
USE_EXTERNAL_API_MOCK=True,
|
||||
CROP_ZONE_CHUNK_AREA_SQM=200000,
|
||||
)
|
||||
class LayerAreaViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = APIRequestFactory()
|
||||
self.user = get_user_model().objects.create_user(
|
||||
username="layer-farmer",
|
||||
password="secret123",
|
||||
email="layer@example.com",
|
||||
phone_number="09120000002",
|
||||
)
|
||||
self.farm_type = FarmType.objects.create(name="باغی")
|
||||
self.farm = FarmHub.objects.create(owner=self.user, name="layer-farm", farm_type=self.farm_type)
|
||||
|
||||
def _create_area(self, **kwargs):
|
||||
defaults = {
|
||||
"farm": self.farm,
|
||||
"geometry": AREA_GEOJSON,
|
||||
"points": AREA_GEOJSON["geometry"]["coordinates"][0][:-1],
|
||||
"center": {"longitude": 51.40874867, "latitude": 35.69575533},
|
||||
"area_sqm": 300000,
|
||||
"area_hectares": 30,
|
||||
"chunk_area_sqm": 200000,
|
||||
"zone_count": 1,
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
return CropArea.objects.create(**defaults)
|
||||
|
||||
def _create_completed_zone(self):
|
||||
crop_area = self._create_area()
|
||||
CropZone.objects.create(
|
||||
crop_area=crop_area,
|
||||
zone_id="zone-0",
|
||||
geometry=AREA_GEOJSON["geometry"],
|
||||
points=AREA_GEOJSON["geometry"]["coordinates"][0][:-1],
|
||||
center={"longitude": 51.4087, "latitude": 35.6957},
|
||||
area_sqm=300000,
|
||||
area_hectares=30,
|
||||
sequence=0,
|
||||
processing_status=CropZone.STATUS_COMPLETED,
|
||||
task_id="celery-task-1",
|
||||
)
|
||||
return crop_area
|
||||
|
||||
def _request(self, path):
|
||||
request = self.factory.get(f"{path}?farm_uuid={self.farm.farm_uuid}")
|
||||
force_authenticate(request, user=self.user)
|
||||
return request
|
||||
|
||||
def test_water_need_view_requires_farm_uuid(self):
|
||||
request = self.factory.get("/api/crop-zoning/water-need/")
|
||||
force_authenticate(request, user=self.user)
|
||||
|
||||
response = WaterNeedView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.data["message"], "farm_uuid is required.")
|
||||
|
||||
def test_water_need_view_returns_area_style_payload(self):
|
||||
self._create_completed_zone()
|
||||
|
||||
response = WaterNeedView.as_view()(self._request("/api/crop-zoning/water-need/"))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["data"]["task"]["status"], "SUCCESS")
|
||||
self.assertEqual(response.data["data"]["area"], AREA_GEOJSON)
|
||||
self.assertEqual(len(response.data["data"]["zones"]), 1)
|
||||
self.assertIn("waterNeedLayer", response.data["data"]["zones"][0])
|
||||
self.assertNotIn("soilQualityLayer", response.data["data"]["zones"][0])
|
||||
self.assertNotIn("cultivationRiskLayer", response.data["data"]["zones"][0])
|
||||
|
||||
def test_soil_quality_view_returns_area_style_payload(self):
|
||||
self._create_completed_zone()
|
||||
|
||||
response = SoilQualityView.as_view()(self._request("/api/crop-zoning/soil-quality/"))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["data"]["task"]["status"], "SUCCESS")
|
||||
self.assertEqual(len(response.data["data"]["zones"]), 1)
|
||||
self.assertIn("soilQualityLayer", response.data["data"]["zones"][0])
|
||||
self.assertNotIn("waterNeedLayer", response.data["data"]["zones"][0])
|
||||
self.assertNotIn("cultivationRiskLayer", response.data["data"]["zones"][0])
|
||||
|
||||
def test_cultivation_risk_view_returns_area_style_payload(self):
|
||||
self._create_completed_zone()
|
||||
|
||||
response = CultivationRiskView.as_view()(self._request("/api/crop-zoning/cultivation-risk/"))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["data"]["task"]["status"], "SUCCESS")
|
||||
self.assertEqual(len(response.data["data"]["zones"]), 1)
|
||||
self.assertIn("cultivationRiskLayer", response.data["data"]["zones"][0])
|
||||
self.assertNotIn("waterNeedLayer", response.data["data"]["zones"][0])
|
||||
self.assertNotIn("soilQualityLayer", response.data["data"]["zones"][0])
|
||||
|
||||
@@ -2,7 +2,10 @@ from django.urls import path
|
||||
|
||||
from .views import (
|
||||
AreaView,
|
||||
CultivationRiskView,
|
||||
ProductsView,
|
||||
SoilQualityView,
|
||||
WaterNeedView,
|
||||
ZoneDetailsView,
|
||||
ZonesCultivationRiskView,
|
||||
ZonesInitialView,
|
||||
@@ -12,6 +15,9 @@ from .views import (
|
||||
|
||||
urlpatterns = [
|
||||
path("area/", AreaView.as_view(), name="crop-zoning-area"),
|
||||
path("water-need/", WaterNeedView.as_view(), name="crop-zoning-water-need"),
|
||||
path("soil-quality/", SoilQualityView.as_view(), name="crop-zoning-soil-quality"),
|
||||
path("cultivation-risk/", CultivationRiskView.as_view(), name="crop-zoning-cultivation-risk"),
|
||||
path("products/", ProductsView.as_view(), name="crop-zoning-products"),
|
||||
# path("zones/initial/", ZonesInitialView.as_view(), name="crop-zoning-zones-initial"),
|
||||
# path(
|
||||
|
||||
+75
-12
@@ -10,10 +10,13 @@ from config.swagger import status_response
|
||||
from .services import (
|
||||
create_zones_and_dispatch,
|
||||
ensure_latest_area_ready_for_processing,
|
||||
get_latest_cultivation_risk_payload,
|
||||
get_cultivation_risk_payload,
|
||||
get_default_area_feature,
|
||||
get_initial_zones_payload,
|
||||
get_latest_area_payload,
|
||||
get_latest_soil_quality_payload,
|
||||
get_latest_water_need_payload,
|
||||
get_products_payload,
|
||||
get_soil_quality_payload,
|
||||
get_water_need_payload,
|
||||
@@ -22,10 +25,7 @@ from .services import (
|
||||
)
|
||||
|
||||
|
||||
class AreaView(APIView):
|
||||
@extend_schema(
|
||||
tags=["Crop Zoning"],
|
||||
parameters=[
|
||||
AREA_QUERY_PARAMETERS = [
|
||||
OpenApiParameter(
|
||||
name="farm_uuid",
|
||||
type=OpenApiTypes.UUID,
|
||||
@@ -47,13 +47,12 @@ class AreaView(APIView):
|
||||
required=False,
|
||||
description="تعداد زون در هر صفحه. مقدار پيش فرض 10 است.",
|
||||
),
|
||||
],
|
||||
responses={
|
||||
200: status_response("CropZoningAreaResponse", data=serializers.JSONField()),
|
||||
400: status_response("CropZoningAreaValidationError", data=serializers.JSONField()),
|
||||
500: status_response("CropZoningAreaServerError", data=serializers.JSONField()),
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class BaseAreaDataView(APIView):
|
||||
payload_getter = None
|
||||
|
||||
def get(self, request):
|
||||
farm_uuid = request.query_params.get("farm_uuid")
|
||||
try:
|
||||
@@ -65,11 +64,75 @@ class AreaView(APIView):
|
||||
return Response({"status": "error", "message": str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
return Response(
|
||||
{"status": "success", "data": get_latest_area_payload(crop_area, page=page, page_size=page_size)},
|
||||
{"status": "success", "data": self.payload_getter(crop_area, page=page, page_size=page_size)},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
class AreaView(BaseAreaDataView):
|
||||
payload_getter = staticmethod(get_latest_area_payload)
|
||||
|
||||
@extend_schema(
|
||||
tags=["Crop Zoning"],
|
||||
parameters=AREA_QUERY_PARAMETERS,
|
||||
responses={
|
||||
200: status_response("CropZoningAreaResponse", data=serializers.JSONField()),
|
||||
400: status_response("CropZoningAreaValidationError", data=serializers.JSONField()),
|
||||
500: status_response("CropZoningAreaServerError", data=serializers.JSONField()),
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
return super().get(request)
|
||||
|
||||
|
||||
class WaterNeedView(BaseAreaDataView):
|
||||
payload_getter = staticmethod(get_latest_water_need_payload)
|
||||
|
||||
@extend_schema(
|
||||
tags=["Crop Zoning"],
|
||||
parameters=AREA_QUERY_PARAMETERS,
|
||||
responses={
|
||||
200: status_response("CropZoningWaterNeedResponse", data=serializers.JSONField()),
|
||||
400: status_response("CropZoningWaterNeedValidationError", data=serializers.JSONField()),
|
||||
500: status_response("CropZoningWaterNeedServerError", data=serializers.JSONField()),
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
return super().get(request)
|
||||
|
||||
|
||||
class SoilQualityView(BaseAreaDataView):
|
||||
payload_getter = staticmethod(get_latest_soil_quality_payload)
|
||||
|
||||
@extend_schema(
|
||||
tags=["Crop Zoning"],
|
||||
parameters=AREA_QUERY_PARAMETERS,
|
||||
responses={
|
||||
200: status_response("CropZoningSoilQualityResponse", data=serializers.JSONField()),
|
||||
400: status_response("CropZoningSoilQualityValidationError", data=serializers.JSONField()),
|
||||
500: status_response("CropZoningSoilQualityServerError", data=serializers.JSONField()),
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
return super().get(request)
|
||||
|
||||
|
||||
class CultivationRiskView(BaseAreaDataView):
|
||||
payload_getter = staticmethod(get_latest_cultivation_risk_payload)
|
||||
|
||||
@extend_schema(
|
||||
tags=["Crop Zoning"],
|
||||
parameters=AREA_QUERY_PARAMETERS,
|
||||
responses={
|
||||
200: status_response("CropZoningCultivationRiskResponse", data=serializers.JSONField()),
|
||||
400: status_response("CropZoningCultivationRiskValidationError", data=serializers.JSONField()),
|
||||
500: status_response("CropZoningCultivationRiskServerError", data=serializers.JSONField()),
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
return super().get(request)
|
||||
|
||||
|
||||
class ProductsView(APIView):
|
||||
@extend_schema(
|
||||
tags=["Crop Zoning"],
|
||||
|
||||
Reference in New Issue
Block a user