Update Docker Compose ports to 8081 and add new apps and URL routes for crop zoning, plant simulator, pest detection, irrigation recommendation, fertilization recommendation, and farm AI assistant.

This commit is contained in:
2026-02-25 12:21:53 +03:30
parent 608252714b
commit 2a77f90ccd
46 changed files with 4142 additions and 2 deletions
+1836
View File
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
# مشخصات Response های API بخش Crop Zoning
این سند فقط **فرمت و ساختار response**هایی را که فرانت‌اند از API انتظار دارد شرح می‌دهد.
---
## ۱. API بهینه‌سازی زون‌بندی (Optimize Zoning)
وقتی کاربر منطقه را روی نقشه انتخاب می‌کند و دکمه «بهینه‌سازی مجدد» را می‌زند، فرانت‌اند یک **GeoJSON Polygon** (مختصات منطقه) به API می‌فرستد و انتظار دارد سرور یک **GeoJSON FeatureCollection** برگرداند که هر feature آن یک زون با geometry (چندضلعی) و properties (داده‌های پیشنهاد محصول) دارد.
### Request (خلاصه)
- **ورودی:** یک GeoJSON به صورت `Feature` با `geometry.type: "Polygon"` (مختصات به صورت `[lng, lat]`).
### Response مورد انتظار
یک **GeoJSON FeatureCollection** با این ساختار:
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[lng, lat], [lng, lat], ...]]
},
"properties": {
"zoneId": "string",
"crop": "wheat" | "canola" | "saffron",
"matchPercent": number,
"waterNeed": "string",
"estimatedProfit": "string",
"reason": "string",
"criteria": [
{ "name": "string", "value": number },
...
]
}
}
]
}
```
### توضیح فیلدهای `properties` هر زون
| فیلد | نوع | توضیح |
|------|-----|--------|
| `zoneId` | `string` | شناسه یکتا برای زون (مثلاً `"zone-0"`, `"zone-1"`) |
| `crop` | `"wheat" \| "canola" \| "saffron"` | نوع محصول پیشنهادی برای این زون |
| `matchPercent` | `number` | درصد تطابق (۰–۱۰۰) برای پیشنهاد محصول |
| `waterNeed` | `string` | نیاز آبی (مثلاً `"۴۵۰۰-۵۵۰۰ m³/ha"`) |
| `estimatedProfit` | `string` | سود تخمینی (مثلاً `"۱۵-۲۵ میلیون/هکتار"`) |
| `reason` | `string` | توضیح کوتاه دلیل پیشنهاد این محصول |
| `criteria` | `Array<{ name: string, value: number }>` | معیارهای امتیازدهی برای نمودار راداری؛ `value` بین ۰ تا ۱۰۰ (مثلاً دما، بارش، خاک، آب) |
### نمونه response (یک feature)
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[51.38, 35.68],
[51.381, 35.68],
[51.381, 35.681],
[51.38, 35.681],
[51.38, 35.68]
]
]
},
"properties": {
"zoneId": "zone-0",
"crop": "wheat",
"matchPercent": 78,
"waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha",
"estimatedProfit": "۱۵-۲۵ میلیون/هکتار",
"reason": "دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی",
"criteria": [
{ "name": "دما", "value": 85 },
{ "name": "بارش", "value": 72 },
{ "name": "خاک", "value": 80 },
{ "name": "آب", "value": 65 }
]
}
}
]
}
```
---
## ۲. API منطقه اولیه (اختیاری)
اگر بخواهید منطقه اولیه نقشه از سرور بیاید (به‌جای ماک ثابت)، response باید یک **GeoJSON Feature** با Polygon باشد:
```json
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[51.38, 35.68],
[51.40, 35.68],
[51.40, 35.70],
[51.38, 35.70],
[51.38, 35.68]
]
]
}
}
```
- مختصات به صورت `[longitude, latitude]` (lng, lat).
- آرایه اول `coordinates` حلقه بیرونی چندضلعی است؛ نقطه اول و آخر باید یکسان باشند.
---
## ۳. خلاصه نوع‌های TypeScript (برای تطبیق با بک‌اند)
```ts
type CropType = 'wheat' | 'canola' | 'saffron'
interface ZoneFeatureProperties {
zoneId: string
crop: CropType
matchPercent: number
waterNeed: string
estimatedProfit: string
reason: string
criteria: { name: string; value: number }[]
}
// Response بهینه‌سازی = GeoJSON FeatureCollection
// با Feature<Polygon, ZoneFeatureProperties>
```
---
## ۴. نکات
- **Layer فعلی:** فرانت‌اند لایه‌های مختلف (`crops`, `waterNeed`, `soilQuality`, `cultivationRisk`) دارد؛ در صورت نیاز می‌توان برای هر لایه response جدا یا فیلدهای اضافه در `properties` تعریف کرد.
- **دکمه «تغییر محصول»:** در پنل جزئیات زون، کاربر می‌تواند محصول را بین `wheat`, `canola`, `saffron` عوض کند؛ در صورت نیاز می‌توان API جدا برای ذخیره این تغییر تعریف کرد.
- **بخش آب و هوا:** داده‌های آب و هوا از سرویس جدا (`farmDashboardService.getAllCards()``farmWeatherCard`) گرفته می‌شوند و در این سند پوشش داده نشده‌اند.
+190
View File
@@ -0,0 +1,190 @@
# مستندات APIهای دستیار هوشمند مزرعه (Farm AI Assistant)
این سند تمام APIهای مورد نیاز برای صفحه **Farm AI Assistant** را شرح می‌دهد: ورودی‌ها، خروجی‌ها و استفاده در UI.
**مسیر صفحه:** `(dashboard)/(private)/farm-ai-assistant`
**کامپوننت اصلی:** `FarmAiAssistantChat`
---
## نمای کلی
دستیار هوشمند مزرعه برای کار به موارد زیر نیاز دارد:
| ردیف | API | هدف |
|------|-----|------|
| ۱ | **ارسال پیام به دستیار (Chat/Complete)** | دریافت پاسخ ساخت‌یافته (توصیه، لیست، هشدار) بر اساس پیام کاربر و زمینه مزرعه |
| ۲ | **دریافت زمینه مزرعه (Farm Context)** | پر کردن نوار «زمینه مزرعه» (نوع خاک، EC آب، محصول، مرحله رشد، آخرین آبیاری) |
| ۳ | **توصیه آبیاری** | در صورت درخواست کاربر یا تصمیم دستیار برای توصیه آبیاری |
| ۴ | **توصیه کوددهی** | در صورت درخواست کاربر یا توصیه کود |
| ۵ | **تشخیص آفت از تصویر** | وقتی کاربر تصویر گیاه را ارسال می‌کند |
---
## ۱. API ارسال پیام به دستیار (Farm AI Chat)
این API هسته اصلی دستیار است و در حال حاضر در فرانت با پاسخ دمو شبیه‌سازی شده است؛ باید با API واقعی جایگزین شود.
### ۱.۱ مشخصات
- **متد:** `POST`
- **آدرس پیشنهادی:** `POST /api/farm-ai-assistant/chat/` یا `POST /api/farm-ai-assistant/messages/`
- **هدف:** ارسال پیام کاربر (و در صورت وجود تصویر) به همراه زمینه مزرعه و دریافت پاسخ ساخت‌یافته دستیار.
### ۱.۲ ورودی (Request Body)
| فیلد | نوع | اجباری | توضیح |
|------|-----|--------|--------|
| `content` | string | بله | متن پیام کاربر |
| `images` | string[] یا base64[] | خیر | آرایه آدرس تصاویر یا داده base64 (در صورت استفاده از آپلود تصویر دوربین در چت) |
| `conversation_id` | string | خیر | شناسه مکالمه برای ادامه گفتگو؛ در اولین پیام ارسال نشود |
| `farm_context` | object | توصیه | زمینه مزرعه برای پاسخ شخصی‌سازی‌شده (در صورت نبودن، بک‌اند می‌تواند از پیش‌فرض استفاده کند) |
ساختار پیشنهادی `farm_context` (هم‌خوان با `FarmContext` در فرانت):
```json
{
"content": "برنامه آبیاری برای گوجه در مرحله گلدهی چطور باشد؟",
"farm_context": {
"soilType": "Loamy",
"waterEC": "1.2 dS/m",
"selectedCrop": "Tomato",
"growthStage": "Flowering",
"lastIrrigationStatus": "2 days ago"
}
}
```
اگر از **تصویر** استفاده شود (دکمه دوربین در input):
```json
{
"content": "این برگ زرد شده، چه مشکلی داره؟",
"images": ["data:image/jpeg;base64,..."],
"farm_context": { ... }
}
```
### ۱.۳ خروجی (Response Body)
پاسخ باید شامل **بخش‌های ساخت‌یافته** (sections) باشد تا در UI به صورت کارت (توصیه، لیست، هشدار) رندر شود.
**قالب پیشنهادی:**
```json
{
"status": "success",
"data": {
"message_id": "a-1739123456789",
"conversation_id": "conv-abc123",
"content": "",
"sections": [
{
"type": "recommendation",
"title": "Irrigation recommendation",
"icon": "droplet",
"frequency": "3 times per week",
"amount": "1520 L per plant",
"timing": "Early morning (05:0007:00)",
"expandableExplanation": "Your loamy soil holds moisture well..."
},
{
"type": "list",
"title": "Key points",
"icon": "leaf",
"items": [
"Avoid midday watering to reduce evaporation",
"Drip irrigation preferred for root zone targeting"
]
},
{
"type": "warning",
"title": "Weather advisory",
"icon": "warning",
"content": "High temps forecasted next week. Consider increasing frequency."
}
]
}
}
```
**ساختار هر بخش (Section) مطابق `AIResponseSection` در فرانت:**
| فیلد | نوع | اجباری | توضیح |
|------|-----|--------|--------|
| `type` | string | بله | یکی از: `text` \| `list` \| `recommendation` \| `warning` |
| `title` | string | خیر | عنوان بخش |
| `content` | string | خیر | برای `type: "text"` یا `type: "warning"` |
| `items` | string[] | خیر | برای `type: "list"` |
| `icon` | string | خیر | یکی از: `droplet` \| `leaf` \| `warning` \| `fertilizer` \| `calendar` |
| `frequency` | string | خیر | فقط برای `recommendation`: تعداد دفعات (مثلاً در هفته) |
| `amount` | string | خیر | فقط برای `recommendation`: مقدار (مثلاً لیتر یا کیلوگرم) |
| `timing` | string | خیر | فقط برای `recommendation`: زمان پیشنهادی |
| `expandableExplanation` | string | خیر | فقط برای `recommendation`: توضیح قابل گسترش «چرا این توصیه» |
- اگر `content` خالی باشد و فقط `sections` برگردد، در UI فقط کارت‌ها نمایش داده می‌شوند (مطابق پیاده‌سازی فعلی).
- در صورت خطا انتظار می‌رود پاسخ با `status: "error"` و پیام مناسب برگردد.
---
## ۲. API دریافت زمینه مزرعه (Farm Context)
برای پر کردن نوار «زمینه مزرعه» در بالای چت (نوع خاک، EC آب، محصول انتخاب‌شده، مرحله رشد، آخرین آبیاری).
- **وضعیت:** در بک‌اند فعلی endpoint اختصاصی برای «یک جا گرفتن» زمینه مزرعه وجود ندارد.
- **راه‌حل فعلی:** فرانت می‌تواند داده را از منابع موجود جمع کند:
- **توصیه آبیاری:** `GET /api/irrigation-recommendation/config/``farmInfo` (soilType, waterQuality, climateZone) و `cropOptions`
- **توصیه کوددهی:** `GET /api/fertilization-recommendation/config/``farmData` (soilType, organicMatter, waterEC)، `growthStages`، `cropOptions`
- **پیشنهاد برای آینده:** یک endpoint مثل `GET /api/farm-ai-assistant/context/` یا `GET /api/farm-dashboard-config/...` که یک آبجکت هم‌خوان با `FarmContext` فرانت برگرداند (مثلاً `soilType`, `waterEC`, `selectedCrop`, `growthStage`, `lastIrrigationStatus`).
---
## ۳. API توصیه آبیاری
وقتی کاربر در چت درخواست توصیه آبیاری می‌کند (یا دستیار تصمیم می‌گیرد توصیه آبیاری بدهد)، می‌توان از API موجود استفاده کرد.
- **Config (برای فرم/گزینه‌ها):** `GET /api/irrigation-recommendation/config/`
- **توصیه (برنامه آبیاری):** `POST /api/irrigation-recommendation/recommend/`
ورودی پیشنهادی برای `recommend`: `crop_id`, `soilType`, `waterQuality`, `climateZone` (در نسخه فعلی mock است و در پاسخ استفاده نمی‌شود).
جزئیات و نمونه پاسخ: [RECOMMENDATION_APIS.md](./RECOMMENDATION_APIS.md#۱-توصیه-آبیاری-irrigation-recommendation).
---
## ۴. API توصیه کوددهی
در صورت درخواست کاربر برای توصیه کود یا تصمیم دستیار برای دادن توصیه کود.
- **Config:** `GET /api/fertilization-recommendation/config/`
- **توصیه:** `POST /api/fertilization-recommendation/recommend/`
ورودی پیشنهادی برای `recommend`: `crop_id`, `growth_stage`, `soilType`, `organicMatter`, `waterEC` (در نسخه فعلی mock است).
جزئیات و نمونه پاسخ: [RECOMMENDATION_APIS.md](./RECOMMENDATION_APIS.md#۳-توصیه-کوددهی-fertilization-recommendation).
---
## ۵. API تشخیص آفت از تصویر
وقتی کاربر در چت تصویر گیاه را ارسال می‌کند (دکمه دوربین یا آپلود).
- **تحلیل تصویر:** `POST /api/pest-detection/analyze/`
- **ورودی:** بدن درخواست می‌تواند شامل تصویر (مثلاً form-data با فایل یا JSON با base64) باشد. در نسخه فعلی پاسخ ثابت (mock) است.
- **خروجی:** `pest`, `confidence`, `description`, `treatment`
جزئیات و نمونه پاسخ: [RECOMMENDATION_APIS.md](./RECOMMENDATION_APIS.md#۲-تشخیص-آفت-pest-detection).
---
## خلاصه Endpointها برای Farm AI Assistant
| ردیف | API | متد | Endpoint | وضعیت |
|------|-----|------|----------|--------|
| ۱ | Farm AI Chat | POST | `/api/farm-ai-assistant/chat/` (پیشنهادی) | **پیاده‌سازی نشده** |
| ۲ | Farm Context | GET | `/api/farm-ai-assistant/context/` (پیشنهادی) | **پیاده‌سازی نشده**؛ استفاده از configهای آبیاری/کوددهی |
| ۳ | توصیه آبیاری | GET | `/api/irrigation-recommendation/config/` | موجود (mock) |
| ۳ | توصیه آبیاری | POST | `/api/irrigation-recommendation/recommend/` | موجود (mock) |
| ۴ | توصیه کوددهی | GET | `/api/fertilization-recommendation/config/` | موجود (mock) |
| ۴ | توصیه کوددهی | POST | `/api/fertilization-recommendation/recommend/` | موجود (mock) |
| ۵ | تشخیص آفت | POST | `/api/pest-detection/analyze/` | موجود (mock) |
+465
View File
@@ -0,0 +1,465 @@
# قرارداد API شبیه‌ساز گیاه (Plant Simulator)
این سند تمام داده‌هایی را که باید **از بکند بیاید**، **به بکند فرستاده شود** و دوباره **از بکند برگردد** به‌همراه ساختار و داده‌های ماک توصیف می‌کند.
---
## ۱. داده‌های اولیه (از بکند → فرانت)
این داده‌ها یک‌بار هنگام لود صفحه (یا هنگام باز کردن شبیه‌ساز) از بکند گرفته می‌شوند.
### ۱.۱ تنظیمات اسلایدرها (کنترل‌های محیطی)
هر اسلایدر (نور، آب، pH خاک، سرعت رشد و غیره) باید از بکند **حداقل، حداکثر، گام، واحد و برچسب** بگیرد. واحد می‌تواند درصد (`percent`) یا عدد با واحد متنی (`number` + `unit`) باشد.
```ts
// GET /api/plant-simulator/config یا بخشی از همین endpoint
interface SliderConfig {
key: string // مثلاً: "light" | "water" | "soil_ph" | "growth_speed"
label: string // برچسب نمایشی (مثلاً "نور"، "آب")
min: number
max: number
step: number
unit_type: 'percent' | 'number'
unit?: string // وقتی unit_type === 'number' مثلاً "g/s", "ppm", "°C"
default_value: number
icon?: string // اختیاری: مثلاً "☀️", "💧"
}
// پاسخ نمونه
{
"sliders": [
{
"key": "light",
"label": "نور",
"min": 0,
"max": 100,
"step": 5,
"unit_type": "percent",
"default_value": 75,
"icon": "☀️"
},
{
"key": "water",
"label": "آب",
"min": 0,
"max": 100,
"step": 5,
"unit_type": "percent",
"default_value": 65,
"icon": "💧"
},
{
"key": "soil_ph",
"label": "pH خاک",
"min": 4,
"max": 9,
"step": 0.5,
"unit_type": "number",
"unit": "",
"default_value": 6.5
},
{
"key": "growth_speed",
"label": "سرعت رشد",
"min": 0.5,
"max": 5,
"step": 0.5,
"unit_type": "number",
"unit": "×",
"default_value": 1.5
}
]
}
```
### ۱.۲ ثابت‌های شبیه‌ساز (حداکثرها و محدوده چارت)
برای نمایش صحیح ارتفاع، برگ، شاخه، میوه، محصول و محورهای چارت باید از بکند بیاید.
```ts
// همان config یا GET /api/plant-simulator/constants
interface SimulatorConstants {
max_height: number // مثلاً 280 (px یا واحد ارتفاع)
max_leaves: number // مثلاً 14
max_branches: number // مثلاً 6
max_yield: number // مثلاً 500 (گرم)
yield_unit: string // مثلاً "g"
yield_rate_unit: string // مثلاً "g/s"
height_unit?: string // مثلاً "px" یا "cm"
}
// پاسخ نمونه
{
"max_height": 280,
"max_leaves": 14,
"max_branches": 6,
"max_yield": 500,
"yield_unit": "g",
"yield_rate_unit": "g/s",
"height_unit": "px"
}
```
### ۱.۳ تنظیمات چارت (محورها و سری‌ها)
عنوان چارت، برچسب محورها و محدوده min/max هر محور تا در فرانت به‌درستی رسم شود.
```ts
interface ChartConfig {
title: string
x_axis_label?: string // مثلاً "زمان (ثانیه)"
series: {
key: string // "height" | "leaves" | "yield" | "yield_rate"
label: string
y_axis_id: string // "yHeight" | "yLeaf" | "yYield" | "yYieldRate"
min: number
max: number
unit?: string
}[]
}
// پاسخ نمونه
{
"chart": {
"title": "پیشرفت رشد",
"x_axis_label": "زمان (ثانیه)",
"series": [
{ "key": "height", "label": "ارتفاع (px)", "y_axis_id": "yHeight", "min": 0, "max": 280, "unit": "px" },
{ "key": "leaves", "label": "تعداد برگ", "y_axis_id": "yLeaf", "min": 0, "max": 14 },
{ "key": "yield", "label": "محصول (g)", "y_axis_id": "yYield", "min": 0, "max": 500, "unit": "g" },
{ "key": "yield_rate", "label": "نرخ محصول (g/s)", "y_axis_id": "yYieldRate", "min": 0, "unit": "g/s" }
]
}
}
```
---
## ۲. داده‌هایی که به بکند فرستاده می‌شوند
### ۲.۱ شروع شبیه‌سازی
وقتی کاربر دکمه «شروع» را می‌زند، مقادیر فعلی محیط و سرعت به بکند ارسال می‌شود.
```ts
// POST /api/plant-simulator/start
interface StartSimulationRequest {
environment: Record<string, number> // key مطابق slider.key ها
growth_speed: number
}
// مثال
{
"environment": {
"light": 75,
"water": 65,
"soil_ph": 6.5
},
"growth_speed": 1.5
}
```
### ۲.۲ توقف شبیه‌سازی
```ts
// POST /api/plant-simulator/stop
// بدنه خالی یا فقط session_id در صورت نیاز
{}
```
### ۲.۳ ریست
```ts
// POST /api/plant-simulator/reset
// بدنه خالی یا session_id
{}
```
### ۲.۴ به‌روزرسانی محیط (تغییر اسلایدرها)
هر بار کاربر نور، آب، pH یا سرعت را عوض کند، در حالت real-time می‌توان این را به بکند فرستاد (اختیاری؛ بکند می‌تواند فقط با start این مقادیر را بگیرد).
```ts
// PATCH /api/plant-simulator/environment
interface UpdateEnvironmentRequest {
environment: Record<string, number>
growth_speed?: number
}
// مثال
{
"environment": { "light": 80, "water": 70, "soil_ph": 6.5 },
"growth_speed": 2
}
```
---
## ۳. داده‌هایی که از بکند برمی‌گردند (حین / بعد شبیه‌سازی)
این داده‌ها یا با **polling** (مثلاً هر ۱ ثانیه) یا با **WebSocket/SSE** از بکند گرفته می‌شوند و در UI و چارت به‌صورت تدریجی به‌روز می‌شوند (مثلاً آرایه‌های چارت کم‌کم طولانی می‌شوند).
### ۳.۱ وضعیت گیاه (آمار کارت بالا)
تعداد برگ، شاخه، میوه، ارتفاع، محصول و نرخ محصول باید از بکند بیاید تا با سرعت رشد واقعی همگام باشد.
```ts
// GET /api/plant-simulator/state یا از طریق WebSocket
interface PlantStateResponse {
height: number
leaves_count: number
branches_count: number
fruits_count: number
yield: number
yield_rate: number
tick: number
is_healthy: boolean // آیا گیاه می‌تواند به سلامت به رشد ادامه دهد؟
can_continue: boolean // معادل is_healthy یا منطق جدا (مثلاً رسیدن به حداکثر ارتفاع)
}
// پاسخ نمونه (یک لحظه از زمان)
{
"height": 120,
"leaves_count": 4,
"branches_count": 2,
"fruits_count": 0,
"yield": 0,
"yield_rate": 0.012,
"tick": 340,
"is_healthy": true,
"can_continue": true
}
```
### ۳.۲ پیشرفت و وضعیت نوارهای پیشرفت
پیشرفت رشد، وضعیت نور، وضعیت آب و محصول‌دهی برای نوارهای پیشرفت (Progress) و نمایش درصد/عدد.
```ts
interface ProgressResponse {
growth_progress: number // 0..100 (بر اساس height / max_height)
light_status: number // مقدار فعلی نور (مثلاً 0..100 درصد)
water_status: number // مقدار فعلی آب (مثلاً 0..100 درصد)
yield_progress: number // 0..100 (بر اساس yield / max_yield)
yield_current: number
yield_rate_current: number
}
// پاسخ نمونه
{
"growth_progress": 42,
"light_status": 75,
"water_status": 65,
"yield_progress": 8,
"yield_current": 42.5,
"yield_rate_current": 0.093
}
```
این فیلدها می‌توانند داخل همان `PlantStateResponse` یا در یک endpoint جدا برگردانده شوند.
### ۳.۳ داده‌های چارت (تاریخچهٔ زمانی)
داده‌های چارت به‌صورت آرایه‌هایی هستند که **بر اساس زمان (مثلاً هر ثانیه) از بکند پر می‌شوند**. فرانت این آرایه‌ها را مستقیم به نمودار می‌دهد؛ هر نقطهٔ جدید با سرعت رشد گیاه اضافه می‌شود.
مثال: اگر بکند هر ثانیه یک بار state بفرستد، آرایه‌ها به این شکل طول می‌کشند:
- ثانیه ۰: `[0]`
- ثانیه ۱: `[0, 5]`
- ثانیه ۲: `[0, 5, 10]`
- ثانیه ۳: `[0, 5, 10, 30]`
- ثانیه ۴: `[0, 5, 10, 30, 40]`
یعنی **مقادیر با سرعت رشد گیاه کم‌کم به آرایه اضافه می‌شوند**.
```ts
// GET /api/plant-simulator/state یا بخشی از همان پاسخ
interface ChartHistoryResponse {
labels: string[] // مثلاً ["0s", "1s", "2s", ...]
height_history: number[]
leaf_history: number[]
yield_history: number[]
yield_rate_history: number[]
}
// پاسخ نمونه (بعد از چند ثانیه شبیه‌سازی)
{
"labels": ["0s", "1s", "2s", "3s", "4s", "5s"],
"height_history": [0, 5, 12, 28, 45, 68],
"leaf_history": [0, 0, 1, 2, 3, 4],
"yield_history": [0, 0, 0, 0.1, 0.5, 1.2],
"yield_rate_history": [0, 0, 0, 0.01, 0.03, 0.06]
}
```
- **ارتفاع (height_history)**: مقدار ارتفاع در هر نمونه (مثلاً هر ثانیه).
- **برگ (leaf_history)**: تعداد برگ در هر نمونه.
- **محصول (yield_history)**: مقدار تجمعی محصول (g) در هر نمونه.
- **نرخ محصول (yield_rate_history)**: نرخ لحظه‌ای (g/s) در هر نمونه.
محتوای داخل چارت دقیقاً همین آرایه‌هاست؛ فرانت فقط این‌ها را روی محور زمان رسم می‌کند و با هر پاسخ جدید یک (یا چند) نقطه به انتهای آرایه اضافه می‌شود.
---
## ۴. خلاصهٔ جریان داده
| جهت | زمان | داده |
|-----|------|------|
| بکند → فرانت | لود صفحه | Config: اسلایدرها (min, max, step, unit)، ثابت‌ها (max_height, max_leaves, ...)، تنظیمات چارت |
| فرانت → بکند | کلیک شروع | محیط (light, water, soil_ph, ...) و growth_speed |
| فرانت → بکند | کلیک توقف / ریست | stop یا reset |
| فرانت → بکند | (اختیاری) تغییر اسلایدر | environment + growth_speed |
| بکند → فرانت | حین شبیه‌سازی (polling/WS) | PlantState (height, leaves_count, branches_count, fruits_count, yield, yield_rate, is_healthy, can_continue) |
| بکند → فرانت | همان پاسخ | Progress (growth_progress, light_status, water_status, yield_progress, yield_current, yield_rate_current) |
| بکند → فرانت | همان پاسخ | ChartHistory (labels, height_history, leaf_history, yield_history, yield_rate_history) |
---
## ۵. داده‌های ماک (Mock) کامل
برای توسعه و تست فرانت بدون بکند می‌توان از این ماک استفاده کرد.
### ۵.۱ ماک Config (اسلایدرها + ثابت‌ها + چارت)
```json
{
"sliders": [
{
"key": "light",
"label": "نور",
"min": 0,
"max": 100,
"step": 5,
"unit_type": "percent",
"default_value": 75,
"icon": "☀️"
},
{
"key": "water",
"label": "آب",
"min": 0,
"max": 100,
"step": 5,
"unit_type": "percent",
"default_value": 65,
"icon": "💧"
},
{
"key": "soil_ph",
"label": "pH خاک",
"min": 4,
"max": 9,
"step": 0.5,
"unit_type": "number",
"unit": "",
"default_value": 6.5
},
{
"key": "growth_speed",
"label": "سرعت رشد",
"min": 0.5,
"max": 5,
"step": 0.5,
"unit_type": "number",
"unit": "×",
"default_value": 1.5
}
],
"constants": {
"max_height": 280,
"max_leaves": 14,
"max_branches": 6,
"max_yield": 500,
"yield_unit": "g",
"yield_rate_unit": "g/s",
"height_unit": "px"
},
"chart": {
"title": "پیشرفت رشد",
"x_axis_label": "زمان (ثانیه)",
"series": [
{ "key": "height", "label": "ارتفاع (px)", "y_axis_id": "yHeight", "min": 0, "max": 280, "unit": "px" },
{ "key": "leaves", "label": "تعداد برگ", "y_axis_id": "yLeaf", "min": 0, "max": 14 },
{ "key": "yield", "label": "محصول (g)", "y_axis_id": "yYield", "min": 0, "max": 500, "unit": "g" },
{ "key": "yield_rate", "label": "نرخ محصول (g/s)", "y_axis_id": "yYieldRate", "min": 0, "unit": "g/s" }
]
}
}
```
### ۵.۲ ماک State + Progress + Chart (یک پاسخ ترکیبی)
```json
{
"plant": {
"height": 142,
"leaves_count": 5,
"branches_count": 2,
"fruits_count": 0,
"yield": 12.4,
"yield_rate": 0.087,
"tick": 520,
"is_healthy": true,
"can_continue": true
},
"progress": {
"growth_progress": 50,
"light_status": 75,
"water_status": 65,
"yield_progress": 2.5,
"yield_current": 12.4,
"yield_rate_current": 0.087
},
"chart": {
"labels": ["0s", "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", "10s"],
"height_history": [0, 5, 12, 28, 45, 68, 92, 110, 125, 135, 142],
"leaf_history": [0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5],
"yield_history": [0, 0, 0, 0.1, 0.5, 1.2, 3.2, 5.8, 8.2, 10.1, 12.4],
"yield_rate_history": [0, 0, 0, 0.01, 0.03, 0.05, 0.06, 0.07, 0.08, 0.085, 0.087]
}
}
```
### ۵.۳ ماک برای نمایش تدریجی چارت
چارت باید بر اساس همین آرایه‌ها به‌صورت تدریجی پر شود؛ هر بار بکند state جدید می‌فرستد، یک نقطه به انتهای هر آرایه اضافه می‌شود. مثال برای چند لحظهٔ متوالی:
```json
// t=0
{ "labels": ["0s"], "height_history": [0], "leaf_history": [0], "yield_history": [0], "yield_rate_history": [0] }
// t=1s
{ "labels": ["0s", "1s"], "height_history": [0, 5], "leaf_history": [0, 0], "yield_history": [0, 0], "yield_rate_history": [0, 0] }
// t=2s
{ "labels": ["0s", "1s", "2s"], "height_history": [0, 5, 12], "leaf_history": [0, 0, 1], "yield_history": [0, 0, 0], "yield_rate_history": [0, 0, 0] }
// t=3s
{ "labels": ["0s", "1s", "2s", "3s"], "height_history": [0, 5, 12, 30], "leaf_history": [0, 0, 1, 2], "yield_history": [0, 0, 0, 0.1], "yield_rate_history": [0, 0, 0, 0.01] }
// t=4s
{ "labels": ["0s", "1s", "2s", "3s", "4s"], "height_history": [0, 5, 12, 30, 48], "leaf_history": [0, 0, 1, 2, 3], "yield_history": [0, 0, 0, 0.1, 0.5], "yield_rate_history": [0, 0, 0, 0.01, 0.03] }
```
ارتفاع و بقیهٔ مقادیر باید متناسب با سرعت رشد و منطق بکند افزایش یابند؛ اعداد بالا فقط نمونهٔ ماک هستند.
---
## ۶. نکات پیاده‌سازی فرانت
- **اسلایدرها**: با استفاده از `sliders` از config، هر کنترل با `min`, `max`, `step`, `unit_type`, `unit` و `default_value` رندر شود.
- **آمار کارت (ارتفاع، برگ، شاخه، میوه، محصول، نرخ)**: مستقیم از `plant` در پاسخ state.
- **سلامت گیاه**: با `is_healthy` و `can_continue` می‌توان پیام یا استایل متفاوت نشان داد (مثلاً هشدار وقتی `can_continue === false`).
- **نوارهای پیشرفت**: از `progress` برای پیشرفت رشد، وضعیت نور، وضعیت آب و محصول‌دهی.
- **چارت**: فقط `chart.labels` و آرایه‌های `height_history`, `leaf_history`, `yield_history`, `yield_rate_history` را به کامپوننت نمودار بدهید؛ با هر پاسخ جدید آرایه‌ها طولانی می‌شوند و نمودار به‌صورت تدریجی به‌روز می‌شود.
اگر endpointها یا فیلدهای اضافه‌ای در بکند دارید، می‌توان آنها را به همین سند اضافه کرد تا فرانت و بکند همیشه هم‌خوان باشند.
+223
View File
@@ -0,0 +1,223 @@
# مستندات APIهای توصیه و تشخیص
این سند سه گروه API را شرح می‌دهد: **توصیه آبیاری**، **تشخیص آفت** و **توصیه کوددهی**. همهٔ پاسخ‌ها در حال حاضر از دادهٔ ثابت (mock) برگردانده می‌شوند و پارامترهای ورودی در پاسخ استفاده نمی‌شوند.
**پایهٔ آدرس API:** `/api/`
**قالب کلی پاسخ:**
`{"status": "success", "data": <payload>}` — فقط با کد وضعیت HTTP 200.
---
## ۱. توصیه آبیاری (Irrigation Recommendation)
**پیشوند:** `api/irrigation-recommendation/`
### ۱.۱ دریافت تنظیمات (Config)
- **متد:** `GET`
- **آدرس:** `api/irrigation-recommendation/config/`
- **هدف:** برگرداندن اطلاعات مزرعه و لیست گزینه‌های محصول برای فرم توصیه آبیاری (هنگام بارگذاری صفحه).
- **ورودی:** ندارد. پارامترهای query خوانده یا استفاده نمی‌شوند.
**نمونه پاسخ:**
```json
{
"status": "success",
"data": {
"farmInfo": {
"soilType": "Loamy",
"waterQuality": "Medium EC",
"climateZone": "Temperate"
},
"cropOptions": [
{"id": "wheat", "labelKey": "wheat", "icon": "tabler-wheat"},
{"id": "corn", "labelKey": "corn", "icon": "tabler-plant-2"},
{"id": "cotton", "labelKey": "cotton", "icon": "tabler-flower"},
{"id": "saffron", "labelKey": "saffron", "icon": "tabler-flower-2"},
{"id": "canola", "labelKey": "canola", "icon": "tabler-leaf"},
{"id": "vegetables", "labelKey": "vegetables", "icon": "tabler-carrot"}
]
}
}
```
| فیلد | نوع | توضیح |
|------|-----|--------|
| `farmInfo.soilType` | string | نوع خاک |
| `farmInfo.waterQuality` | string | کیفیت آب (مثلاً EC) |
| `farmInfo.climateZone` | string | منطقه اقلیمی |
| `cropOptions` | array | لیست محصولات: `id`, `labelKey`, `icon` |
---
### ۱.۲ دریافت توصیه آبیاری (Recommend)
- **متد:** `POST`
- **آدرس:** `api/irrigation-recommendation/recommend/`
- **هدف:** برگرداندن یک برنامهٔ آبیاری ثابت (تعداد در هفته، مدت، بهترین زمان، رطوبت، هشدار).
- **ورودی (بدن درخواست، اختیاری):** می‌توانید JSON با فیلدهایی مثل `crop_id`, `soilType`, `waterQuality`, `climateZone` بفرستید؛ در پاسخ فعلی استفاده نمی‌شوند.
- **CSRF:** این endpoint از CSRF معاف است (برای فراخوانی از فرانت بدون توکن).
**نمونه پاسخ:**
```json
{
"status": "success",
"data": {
"plan": {
"frequencyPerWeek": 4,
"durationMinutes": 45,
"bestTimeOfDay": "05:00 - 07:00",
"moistureLevel": 72,
"warning": "Avoid irrigation during midday hours in the coming week due to forecasted high temperatures."
}
}
}
```
| فیلد | نوع | توضیح |
|------|-----|--------|
| `plan.frequencyPerWeek` | number | تعداد آبیاری در هفته |
| `plan.durationMinutes` | number | مدت هر نوبت (دقیقه) |
| `plan.bestTimeOfDay` | string | بهترین بازه زمانی روز |
| `plan.moistureLevel` | number | سطح رطوبت هدف (درصد) |
| `plan.warning` | string | هشدار یا توصیه اضافه |
---
## ۲. تشخیص آفت (Pest Detection)
**پیشوند:** `api/pest-detection/`
### ۲.۱ تحلیل تصویر (Analyze)
- **متد:** `POST`
- **آدرس:** `api/pest-detection/analyze/`
- **هدف:** برگرداندن نتیجهٔ ثابت تشخیص آفت (نام آفت، اطمینان، توضیح، درمان) — برای زمانی که کاربر تصویر گیاه را آپلود و درخواست تحلیل می‌کند.
- **ورودی (بدن درخواست، اختیاری):** JSON یا form-data (مثلاً شامل `image` یا `file`). در پاسخ فعلی استفاده نمی‌شود.
- **CSRF:** این endpoint از CSRF معاف است.
**نمونه پاسخ:**
```json
{
"status": "success",
"data": {
"pest": "شپشک",
"confidence": 92,
"description": "حشرات کوچک مکنده شیره که باعث پیچ خوردگی برگ می‌شوند.",
"treatment": "یک بار در هفته از اسپری روغن نیم استفاده کنید."
}
}
```
| فیلد | نوع | توضیح |
|------|-----|--------|
| `pest` | string | نام آفت |
| `confidence` | number | درصد اطمینان (۰–۱۰۰) |
| `description` | string | توضیح کوتاه آفت |
| `treatment` | string | توصیه درمان |
---
## ۳. توصیه کوددهی (Fertilization Recommendation)
**پیشوند:** `api/fertilization-recommendation/`
### ۳.۱ دریافت تنظیمات (Config)
- **متد:** `GET`
- **آدرس:** `api/fertilization-recommendation/config/`
- **هدف:** برگرداندن دادهٔ مزرعه، مراحل رشد و گزینه‌های محصول برای فرم توصیه کوددهی (هنگام بارگذاری صفحه).
- **ورودی:** ندارد.
**نمونه پاسخ:**
```json
{
"status": "success",
"data": {
"farmData": {
"soilType": "Loamy",
"organicMatter": "Medium (2.5%)",
"waterEC": "1.2 dS/m"
},
"growthStages": [
{"id": "prePlanting", "icon": "tabler-seedling"},
{"id": "earlyGrowth", "icon": "tabler-leaf"},
{"id": "flowering", "icon": "tabler-flower"},
{"id": "fruiting", "icon": "tabler-apple"},
{"id": "postHarvest", "icon": "tabler-basket"}
],
"cropOptions": [
{"id": "wheat", "labelKey": "wheat", "icon": "tabler-wheat"},
{"id": "corn", "labelKey": "corn", "icon": "tabler-plant-2"},
{"id": "cotton", "labelKey": "cotton", "icon": "tabler-flower"},
{"id": "saffron", "labelKey": "saffron", "icon": "tabler-flower-2"},
{"id": "canola", "labelKey": "canola", "icon": "tabler-leaf"},
{"id": "vegetables", "labelKey": "vegetables", "icon": "tabler-carrot"}
]
}
}
```
| فیلد | نوع | توضیح |
|------|-----|--------|
| `farmData.soilType` | string | نوع خاک |
| `farmData.organicMatter` | string | ماده آلی خاک |
| `farmData.waterEC` | string | EC آب |
| `growthStages` | array | مراحل رشد: `id`, `icon` |
| `cropOptions` | array | لیست محصولات: `id`, `labelKey`, `icon` |
---
### ۳.۲ دریافت توصیه کوددهی (Recommend)
- **متد:** `POST`
- **آدرس:** `api/fertilization-recommendation/recommend/`
- **هدف:** برگرداندن یک برنامهٔ کوددهی ثابت (نسبت NPK، مقدار در هکتار، روش و فاصله مصرف، استدلال).
- **ورودی (بدن درخواست، اختیاری):** JSON با فیلدهایی مثل `crop_id`, `growth_stage`, `soilType`, `organicMatter`, `waterEC`. در پاسخ فعلی استفاده نمی‌شوند.
- **CSRF:** این endpoint از CSRF معاف است.
**نمونه پاسخ:**
```json
{
"status": "success",
"data": {
"plan": {
"npkRatio": "20-20-20 (NPK)",
"amountPerHectare": "150 kg/ha",
"applicationMethod": "Foliar spray + soil broadcast",
"applicationInterval": "Every 14 days",
"reasoning": "Your loamy soil with medium organic matter (2.5%) provides good nutrient retention. Water EC of 1.2 dS/m indicates low salinity—suitable for most crops. At the flowering stage, increased phosphorus supports bloom development. We recommend a balanced NPK to maintain nitrogen for vegetative growth while boosting phosphorous for flowering."
}
}
}
```
| فیلد | نوع | توضیح |
|------|-----|--------|
| `plan.npkRatio` | string | نسبت NPK پیشنهادی |
| `plan.amountPerHectare` | string | مقدار مصرف در هکتار |
| `plan.applicationMethod` | string | روش مصرف (مثلاً محلول‌پاشی، خاکی) |
| `plan.applicationInterval` | string | فاصله بین مصرف |
| `plan.reasoning` | string | توضیح/استدلال توصیه |
---
## خلاصه Endpointها
| ماژول | متد | Endpoint |
|--------|------|----------|
| Irrigation | GET | `/api/irrigation-recommendation/config/` |
| Irrigation | POST | `/api/irrigation-recommendation/recommend/` |
| Pest Detection | POST | `/api/pest-detection/analyze/` |
| Fertilization | GET | `/api/fertilization-recommendation/config/` |
| Fertilization | POST | `/api/fertilization-recommendation/recommend/` |
---
**توجه:** در نسخهٔ فعلی هیچ پردازش، اعتبارسنجی یا استفاده از پارامترهای ورودی در پاسخ انجام نمی‌شود؛ همهٔ خروجی‌ها از دادهٔ ثابت (mock) هستند.
+6
View File
@@ -22,6 +22,12 @@ INSTALLED_APPS = [
"account", "account",
"sensor_hub", "sensor_hub",
"dashboard", "dashboard",
"crop_zoning",
"plant_simulator",
"pest_detection",
"irrigation_recommendation",
"fertilization_recommendation",
"farm_ai_assistant",
"rest_framework", "rest_framework",
"corsheaders", "corsheaders",
] ]
+6
View File
@@ -8,4 +8,10 @@ urlpatterns = [
path("api/sensor-hub/", include("sensor_hub.urls")), path("api/sensor-hub/", include("sensor_hub.urls")),
path("api/farm-dashboard-config/", include("dashboard.urls_config")), path("api/farm-dashboard-config/", include("dashboard.urls_config")),
path("api/farm-dashboard/", include("dashboard.urls")), path("api/farm-dashboard/", include("dashboard.urls")),
path("api/crop-zoning/", include("crop_zoning.urls")),
path("api/plant-simulator/", include("plant_simulator.urls")),
path("api/pest-detection/", include("pest_detection.urls")),
path("api/irrigation-recommendation/", include("irrigation_recommendation.urls")),
path("api/fertilization-recommendation/", include("fertilization_recommendation.urls")),
path("api/farm-ai-assistant/", include("farm_ai_assistant.urls")),
] ]
+1
View File
@@ -0,0 +1 @@
+7
View File
@@ -0,0 +1,7 @@
from django.apps import AppConfig
class CropZoningConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "crop_zoning"
verbose_name = "Crop Zoning"
+116
View File
@@ -0,0 +1,116 @@
"""
Static mock data for Crop Zoning API.
Matches API_RESPONSE_SPEC.md. No database, no dynamic values.
"""
# Response for POST optimize: GeoJSON FeatureCollection (API_RESPONSE_SPEC §1)
OPTIMIZE_ZONING_RESPONSE = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[51.38, 35.68],
[51.381, 35.68],
[51.381, 35.681],
[51.38, 35.681],
[51.38, 35.68],
]
],
},
"properties": {
"zoneId": "zone-0",
"crop": "wheat",
"matchPercent": 78,
"waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha",
"estimatedProfit": "۱۵-۲۵ میلیون/هکتار",
"reason": "دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی",
"criteria": [
{"name": "دما", "value": 85},
{"name": "بارش", "value": 72},
{"name": "خاک", "value": 80},
{"name": "آب", "value": 65},
],
},
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[51.381, 35.68],
[51.382, 35.68],
[51.382, 35.681],
[51.381, 35.681],
[51.381, 35.68],
]
],
},
"properties": {
"zoneId": "zone-1",
"crop": "canola",
"matchPercent": 82,
"waterNeed": "۳۵۰۰-۴۵۰۰ m³/ha",
"estimatedProfit": "۲۰-۳۰ میلیون/هکتار",
"reason": "بارش کافی، خاک با بافت مناسب",
"criteria": [
{"name": "دما", "value": 70},
{"name": "بارش", "value": 88},
{"name": "خاک", "value": 75},
{"name": "آب", "value": 90},
],
},
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[51.382, 35.68],
[51.40, 35.68],
[51.40, 35.681],
[51.382, 35.681],
[51.382, 35.68],
]
],
},
"properties": {
"zoneId": "zone-2",
"crop": "saffron",
"matchPercent": 65,
"waterNeed": "۲۵۰۰-۳۵۰۰ m³/ha",
"estimatedProfit": "۸۰-۱۲۰ میلیون/هکتار",
"reason": "آب و هوای خشک و سرد مناسب زعفران",
"criteria": [
{"name": "دما", "value": 60},
{"name": "بارش", "value": 55},
{"name": "خاک", "value": 85},
{"name": "آب", "value": 50},
],
},
},
],
}
# Response for GET initial region: GeoJSON Feature with Polygon (API_RESPONSE_SPEC §2)
INITIAL_REGION_RESPONSE = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[51.38, 35.68],
[51.40, 35.68],
[51.40, 35.70],
[51.38, 35.70],
[51.38, 35.68],
]
],
},
}
+48
View File
@@ -0,0 +1,48 @@
{
"info": {
"name": "Crop Zoning",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "Crop Zoning API. POST optimize (GeoJSON FeatureCollection). GET initial-region (GeoJSON Feature). Static mock only. No database."
},
"item": [
{
"name": "Optimize zoning (POST)",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {
"mode": "raw",
"raw": "{\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.40, 35.68], [51.40, 35.70], [51.38, 35.70], [51.38, 35.68]]]\n }\n}"
},
"url": "{{baseUrl}}/api/crop-zoning/optimize/",
"description": "POST with GeoJSON Polygon (region). Returns static FeatureCollection. Input not processed."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.381, 35.68], [51.381, 35.681], [51.38, 35.681], [51.38, 35.68]]]\n },\n \"properties\": {\n \"zoneId\": \"zone-0\",\n \"crop\": \"wheat\",\n \"matchPercent\": 78,\n \"waterNeed\": \"۴۵۰۰-۵۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۱۵-۲۵ میلیون/هکتار\",\n \"reason\": \"دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 85}, {\"name\": \"بارش\", \"value\": 72}, {\"name\": \"خاک\", \"value\": 80}, {\"name\": \"آب\", \"value\": 65}]\n }\n },\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.381, 35.68], [51.382, 35.68], [51.382, 35.681], [51.381, 35.681], [51.381, 35.68]]]\n },\n \"properties\": {\n \"zoneId\": \"zone-1\",\n \"crop\": \"canola\",\n \"matchPercent\": 82,\n \"waterNeed\": \"۳۵۰۰-۴۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۲۰-۳۰ میلیون/هکتار\",\n \"reason\": \"بارش کافی، خاک با بافت مناسب\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 70}, {\"name\": \"بارش\", \"value\": 88}, {\"name\": \"خاک\", \"value\": 75}, {\"name\": \"آب\", \"value\": 90}]\n }\n },\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.382, 35.68], [51.40, 35.68], [51.40, 35.681], [51.382, 35.681], [51.382, 35.68]]]\n },\n \"properties\": {\n \"zoneId\": \"zone-2\",\n \"crop\": \"saffron\",\n \"matchPercent\": 65,\n \"waterNeed\": \"۲۵۰۰-۳۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۸۰-۱۲۰ میلیون/هکتار\",\n \"reason\": \"آب و هوای خشک و سرد مناسب زعفران\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 60}, {\"name\": \"بارش\", \"value\": 55}, {\"name\": \"خاک\", \"value\": 85}, {\"name\": \"آب\", \"value\": 50}]\n }\n }\n ]\n }\n}"
}
]
},
{
"name": "Get initial region (GET)",
"request": {
"method": "GET",
"header": [{"key": "Content-Type", "value": "application/json"}],
"url": "{{baseUrl}}/api/crop-zoning/initial-region/",
"description": "GET initial map region as GeoJSON Feature with Polygon. Static mock only."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"type\": \"Feature\",\n \"properties\": {},\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.40, 35.68], [51.40, 35.70], [51.38, 35.70], [51.38, 35.68]]]\n }\n }\n}"
}
]
}
],
"variable": [{"key": "baseUrl", "value": "http://localhost:8000"}]
}
+8
View File
@@ -0,0 +1,8 @@
from django.urls import path
from .views import InitialRegionView, OptimizeZoningView
urlpatterns = [
path("optimize/", OptimizeZoningView.as_view(), name="crop-zoning-optimize"),
path("initial-region/", InitialRegionView.as_view(), name="crop-zoning-initial-region"),
]
+67
View File
@@ -0,0 +1,67 @@
"""
Crop Zoning API views.
Plain Django only; no DRF. No database. All responses are static mock data.
Response format: {"status": "success", "data": <payload>}. HTTP 200 only.
No processing, validation, or use of input parameters in responses.
"""
from django.http import JsonResponse
from django.views import View
from .mock_data import INITIAL_REGION_RESPONSE, OPTIMIZE_ZONING_RESPONSE
class OptimizeZoningView(View):
"""
POST endpoint for zoning optimization.
Purpose:
Returns a static GeoJSON FeatureCollection of zones with crop suggestions
(API_RESPONSE_SPEC §1). Used when the user selects a region on the map
and triggers "optimize zoning". No processing is performed on the request.
Input parameters:
- body (optional): JSON body; may contain a GeoJSON Feature with Polygon.
Data type: object. Location: body. Not read or validated; not used in response.
Response structure:
- status: string, always "success".
- data: object, GeoJSON FeatureCollection with features containing
geometry (Polygon) and properties (zoneId, crop, matchPercent, waterNeed,
estimatedProfit, reason, criteria).
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(
{"status": "success", "data": OPTIMIZE_ZONING_RESPONSE},
status=200,
)
class InitialRegionView(View):
"""
GET endpoint for the initial map region.
Purpose:
Returns a static GeoJSON Feature with Polygon defining the initial
map region (API_RESPONSE_SPEC §2). Optional; used when the initial
region is loaded from the server instead of a fixed client mock.
Input parameters:
None. Query parameters, if sent, are not read or used.
Response structure:
- status: string, always "success".
- data: object, GeoJSON Feature with geometry.type "Polygon" and
coordinates as [longitude, latitude]; first and last point equal.
No processing or validation is performed on inputs.
"""
def get(self, request):
return JsonResponse(
{"status": "success", "data": INITIAL_REGION_RESPONSE},
status=200,
)
+1 -1
View File
@@ -23,7 +23,7 @@ services:
PMA_PORT: 3306 PMA_PORT: 3306
UPLOAD_LIMIT: 64M UPLOAD_LIMIT: 64M
ports: ports:
- "8080:80" - "8081:80"
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
+1 -1
View File
@@ -22,7 +22,7 @@ services:
PMA_PORT: 3306 PMA_PORT: 3306
UPLOAD_LIMIT: 64M UPLOAD_LIMIT: 64M
ports: ports:
- "8080:80" - "8081:80"
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
View File
+44
View File
@@ -0,0 +1,44 @@
"""
Static mock data for Farm AI Assistant API.
No database, no dynamic values. All responses are fixed JSON.
"""
CHAT_RESPONSE_DATA = {
"message_id": "a-1739123456789",
"conversation_id": "conv-abc123",
"content": "",
"sections": [
{
"type": "recommendation",
"title": "Irrigation recommendation",
"icon": "droplet",
"frequency": "3 times per week",
"amount": "1520 L per plant",
"timing": "Early morning (05:0007:00)",
"expandableExplanation": "Your loamy soil holds moisture well...",
},
{
"type": "list",
"title": "Key points",
"icon": "leaf",
"items": [
"Avoid midday watering to reduce evaporation",
"Drip irrigation preferred for root zone targeting",
],
},
{
"type": "warning",
"title": "Weather advisory",
"icon": "warning",
"content": "High temps forecasted next week. Consider increasing frequency.",
},
],
}
CONTEXT_RESPONSE_DATA = {
"soilType": "Loamy",
"waterEC": "1.2 dS/m",
"selectedCrop": "Tomato",
"growthStage": "Flowering",
"lastIrrigationStatus": "2 days ago",
}
@@ -0,0 +1,48 @@
{
"info": {
"name": "Farm AI Assistant",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "Farm AI Assistant API. GET context (farm bar data). POST chat (user message + optional farm_context/images). Static JSON only."
},
"item": [
{
"name": "Get farm context (GET)",
"request": {
"method": "GET",
"header": [{"key": "Content-Type", "value": "application/json"}],
"url": "{{baseUrl}}/api/farm-ai-assistant/context/",
"description": "Returns static farm context: soilType, waterEC, selectedCrop, growthStage, lastIrrigationStatus for the context bar."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"soilType\": \"Loamy\",\n \"waterEC\": \"1.2 dS/m\",\n \"selectedCrop\": \"Tomato\",\n \"growthStage\": \"Flowering\",\n \"lastIrrigationStatus\": \"2 days ago\"\n }\n}"
}
]
},
{
"name": "Chat (POST)",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {
"mode": "raw",
"raw": "{\n \"content\": \"برنامه آبیاری برای گوجه در مرحله گلدهی چطور باشد؟\",\n \"farm_context\": {\n \"soilType\": \"Loamy\",\n \"waterEC\": \"1.2 dS/m\",\n \"selectedCrop\": \"Tomato\",\n \"growthStage\": \"Flowering\",\n \"lastIrrigationStatus\": \"2 days ago\"\n }\n}"
},
"url": "{{baseUrl}}/api/farm-ai-assistant/chat/",
"description": "Body: content (required), optional images, conversation_id, farm_context. Returns static message with sections (recommendation, list, warning). Input not processed."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"message_id\": \"a-1739123456789\",\n \"conversation_id\": \"conv-abc123\",\n \"content\": \"\",\n \"sections\": [\n {\n \"type\": \"recommendation\",\n \"title\": \"Irrigation recommendation\",\n \"icon\": \"droplet\",\n \"frequency\": \"3 times per week\",\n \"amount\": \"1520 L per plant\",\n \"timing\": \"Early morning (05:0007:00)\",\n \"expandableExplanation\": \"Your loamy soil holds moisture well...\"\n },\n {\n \"type\": \"list\",\n \"title\": \"Key points\",\n \"icon\": \"leaf\",\n \"items\": [\n \"Avoid midday watering to reduce evaporation\",\n \"Drip irrigation preferred for root zone targeting\"\n ]\n },\n {\n \"type\": \"warning\",\n \"title\": \"Weather advisory\",\n \"icon\": \"warning\",\n \"content\": \"High temps forecasted next week. Consider increasing frequency.\"\n }\n ]\n }\n}"
}
]
}
],
"variable": [{"key": "baseUrl", "value": "http://localhost:8000"}]
}
+8
View File
@@ -0,0 +1,8 @@
from django.urls import path
from .views import ChatView, ContextView
urlpatterns = [
path("context/", ContextView.as_view(), name="farm-ai-assistant-context"),
path("chat/", ChatView.as_view(), name="farm-ai-assistant-chat"),
]
+77
View File
@@ -0,0 +1,77 @@
"""
Farm AI Assistant API views.
Plain Django only; no DRF. No database. All responses are static mock data.
Response format: {"status": "success", "data": <payload>}. HTTP 200 only.
No processing, validation, or use of input parameters in responses.
CSRF exempt on POST so frontend can call without token.
"""
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from .mock_data import CHAT_RESPONSE_DATA, CONTEXT_RESPONSE_DATA
class ContextView(View):
"""
GET endpoint for farm context (Farm AI Assistant bar).
Purpose:
Returns static farm context for the Farm AI Assistant UI bar:
soilType, waterEC, selectedCrop, growthStage, lastIrrigationStatus.
Used when loading the farm-ai-assistant page to populate the context strip.
Input parameters:
None. Query parameters, if sent, are not read or used.
Response structure:
- status: string, always "success".
- data: object with keys soilType, waterEC, selectedCrop,
growthStage, lastIrrigationStatus (all strings).
No processing or validation is performed on inputs.
"""
def get(self, request):
return JsonResponse(
{"status": "success", "data": CONTEXT_RESPONSE_DATA},
status=200,
)
@method_decorator(csrf_exempt, name="dispatch")
class ChatView(View):
"""
POST endpoint for Farm AI Assistant chat (send message, get structured reply).
Purpose:
Accepts user message (and optional images, conversation_id, farm_context)
and returns a static structured reply with sections (recommendation,
list, warning) for rendering as cards in the chat UI. No AI or
computation; response is fixed.
Input parameters (body, JSON; all optional except conceptually content):
- content: string. Location: body. User message text. Not read or used.
- images: array of strings (URLs or base64). Location: body. Not read.
- conversation_id: string. Location: body. Conversation id. Not used.
- farm_context: object (soilType, waterEC, selectedCrop, growthStage,
lastIrrigationStatus). Location: body. Not read or used.
Response structure:
- status: string, always "success".
- data: object with message_id, conversation_id, content (string),
sections (array of section objects). Each section has type, title,
icon, and type-specific fields (content, items, frequency, amount,
timing, expandableExplanation).
No processing or validation is performed on inputs. Input values are
not used in the response.
"""
def post(self, request):
return JsonResponse(
{"status": "success", "data": CHAT_RESPONSE_DATA},
status=200,
)
+7
View File
@@ -0,0 +1,7 @@
from django.apps import AppConfig
class FertilizationRecommendationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "fertilization_recommendation"
verbose_name = "Fertilization Recommendation"
+37
View File
@@ -0,0 +1,37 @@
"""
Static mock data for Fertilization Recommendation API.
No database, no dynamic values.
"""
CONFIG_RESPONSE_DATA = {
"farmData": {
"soilType": "Loamy",
"organicMatter": "Medium (2.5%)",
"waterEC": "1.2 dS/m",
},
"growthStages": [
{"id": "prePlanting", "icon": "tabler-seedling"},
{"id": "earlyGrowth", "icon": "tabler-leaf"},
{"id": "flowering", "icon": "tabler-flower"},
{"id": "fruiting", "icon": "tabler-apple"},
{"id": "postHarvest", "icon": "tabler-basket"},
],
"cropOptions": [
{"id": "wheat", "labelKey": "wheat", "icon": "tabler-wheat"},
{"id": "corn", "labelKey": "corn", "icon": "tabler-plant-2"},
{"id": "cotton", "labelKey": "cotton", "icon": "tabler-flower"},
{"id": "saffron", "labelKey": "saffron", "icon": "tabler-flower-2"},
{"id": "canola", "labelKey": "canola", "icon": "tabler-leaf"},
{"id": "vegetables", "labelKey": "vegetables", "icon": "tabler-carrot"},
],
}
RECOMMEND_RESPONSE_DATA = {
"plan": {
"npkRatio": "20-20-20 (NPK)",
"amountPerHectare": "150 kg/ha",
"applicationMethod": "Foliar spray + soil broadcast",
"applicationInterval": "Every 14 days",
"reasoning": "Your loamy soil with medium organic matter (2.5%) provides good nutrient retention. Water EC of 1.2 dS/m indicates low salinity—suitable for most crops. At the flowering stage, increased phosphorus supports bloom development. We recommend a balanced NPK to maintain nitrogen for vegetative growth while boosting phosphorous for flowering.",
},
}
@@ -0,0 +1 @@
{"info":{"name":"Fertilization Recommendation","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json","description":"Fertilization Recommendation API. GET config (farm data, growth stages, crop options). POST recommend (optional body). Returns static plan. No database."},"item":[{"name":"Get config (GET)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/fertilization-recommendation/config/","description":"Returns static farmData, growthStages, cropOptions."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"farmData\": {\n \"soilType\": \"Loamy\",\n \"organicMatter\": \"Medium (2.5%)\",\n \"waterEC\": \"1.2 dS/m\"\n },\n \"growthStages\": [\n {\"id\": \"prePlanting\", \"icon\": \"tabler-seedling\"},\n {\"id\": \"earlyGrowth\", \"icon\": \"tabler-leaf\"},\n {\"id\": \"flowering\", \"icon\": \"tabler-flower\"},\n {\"id\": \"fruiting\", \"icon\": \"tabler-apple\"},\n {\"id\": \"postHarvest\", \"icon\": \"tabler-basket\"}\n ],\n \"cropOptions\": [\n {\"id\": \"wheat\", \"labelKey\": \"wheat\", \"icon\": \"tabler-wheat\"},\n {\"id\": \"corn\", \"labelKey\": \"corn\", \"icon\": \"tabler-plant-2\"},\n {\"id\": \"cotton\", \"labelKey\": \"cotton\", \"icon\": \"tabler-flower\"},\n {\"id\": \"saffron\", \"labelKey\": \"saffron\", \"icon\": \"tabler-flower-2\"},\n {\"id\": \"canola\", \"labelKey\": \"canola\", \"icon\": \"tabler-leaf\"},\n {\"id\": \"vegetables\", \"labelKey\": \"vegetables\", \"icon\": \"tabler-carrot\"}\n ]\n }\n}"}]},{"name":"Get recommendation (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"crop_id\": \"wheat\",\n \"growth_stage\": \"flowering\",\n \"soilType\": \"Loamy\",\n \"organicMatter\": \"Medium (2.5%)\",\n \"waterEC\": \"1.2 dS/m\"\n}"},"url":"{{baseUrl}}/api/fertilization-recommendation/recommend/","description":"Optional body: crop_id, growth_stage, farm_data. Returns static plan. Input not processed."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"plan\": {\n \"npkRatio\": \"20-20-20 (NPK)\",\n \"amountPerHectare\": \"150 kg/ha\",\n \"applicationMethod\": \"Foliar spray + soil broadcast\",\n \"applicationInterval\": \"Every 14 days\",\n \"reasoning\": \"Your loamy soil with medium organic matter (2.5%) provides good nutrient retention. Water EC of 1.2 dS/m indicates low salinity—suitable for most crops. At the flowering stage, increased phosphorus supports bloom development. We recommend a balanced NPK to maintain nitrogen for vegetative growth while boosting phosphorous for flowering.\"\n }\n }\n}"}]}],"variable":[{"key":"baseUrl","value":"http://localhost:8000"}]}
+8
View File
@@ -0,0 +1,8 @@
from django.urls import path
from .views import ConfigView, RecommendView
urlpatterns = [
path("config/", ConfigView.as_view(), name="fertilization-recommendation-config"),
path("recommend/", RecommendView.as_view(), name="fertilization-recommendation-recommend"),
]
+71
View File
@@ -0,0 +1,71 @@
"""
Fertilization Recommendation API views.
Plain Django only; no DRF. No database. All responses are static mock data.
Response format: {"status": "success", "data": <payload>}. HTTP 200 only.
No processing, validation, or use of input parameters in responses.
CSRF exempt on POST so frontend can call without token.
"""
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from .mock_data import CONFIG_RESPONSE_DATA, RECOMMEND_RESPONSE_DATA
class ConfigView(View):
"""
GET endpoint for fertilization config (farm data, growth stages, crop options).
Purpose:
Returns static farm data (soilType, organicMatter, waterEC), growth
stages list, and crop options for the fertilization recommendation form.
Used when loading the fertilization recommendation page.
Input parameters:
None. Query parameters, if sent, are not read or used.
Response structure:
- status: string, always "success".
- data: object with keys farmData (object), growthStages (array of
{ id, icon }), cropOptions (array of { id, labelKey, icon }).
No processing or validation is performed on inputs.
"""
def get(self, request):
return JsonResponse(
{"status": "success", "data": CONFIG_RESPONSE_DATA},
status=200,
)
@method_decorator(csrf_exempt, name="dispatch")
class RecommendView(View):
"""
POST endpoint for fertilization recommendation.
Purpose:
Returns a static fertilization plan (npkRatio, amountPerHectare,
applicationMethod, applicationInterval, reasoning). Body may contain
crop_id, growth_stage, farm_data; not read or used in response.
Input parameters:
- body (optional): JSON. May contain "crop_id", "growth_stage",
"soilType", "organicMatter", "waterEC". Data type: object.
Location: body. Not read or validated; not used in response.
Response structure:
- status: string, always "success".
- data: object with key "plan" (object with npkRatio, amountPerHectare,
applicationMethod, applicationInterval, reasoning).
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(
{"status": "success", "data": RECOMMEND_RESPONSE_DATA},
status=200,
)
+7
View File
@@ -0,0 +1,7 @@
from django.apps import AppConfig
class IrrigationRecommendationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "irrigation_recommendation"
verbose_name = "Irrigation Recommendation"
+30
View File
@@ -0,0 +1,30 @@
"""
Static mock data for Irrigation Recommendation API.
No database, no dynamic values.
"""
CONFIG_RESPONSE_DATA = {
"farmInfo": {
"soilType": "Loamy",
"waterQuality": "Medium EC",
"climateZone": "Temperate",
},
"cropOptions": [
{"id": "wheat", "labelKey": "wheat", "icon": "tabler-wheat"},
{"id": "corn", "labelKey": "corn", "icon": "tabler-plant-2"},
{"id": "cotton", "labelKey": "cotton", "icon": "tabler-flower"},
{"id": "saffron", "labelKey": "saffron", "icon": "tabler-flower-2"},
{"id": "canola", "labelKey": "canola", "icon": "tabler-leaf"},
{"id": "vegetables", "labelKey": "vegetables", "icon": "tabler-carrot"},
],
}
RECOMMEND_RESPONSE_DATA = {
"plan": {
"frequencyPerWeek": 4,
"durationMinutes": 45,
"bestTimeOfDay": "05:00 - 07:00",
"moistureLevel": 72,
"warning": "Avoid irrigation during midday hours in the coming week due to forecasted high temperatures.",
},
}
@@ -0,0 +1 @@
{"info":{"name":"Irrigation Recommendation","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json","description":"Irrigation Recommendation API. GET config (farm info + crop options). POST recommend (optional body). Returns static plan. No database."},"item":[{"name":"Get config (GET)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/irrigation-recommendation/config/","description":"Returns static farmInfo and cropOptions."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"farmInfo\": {\n \"soilType\": \"Loamy\",\n \"waterQuality\": \"Medium EC\",\n \"climateZone\": \"Temperate\"\n },\n \"cropOptions\": [\n {\"id\": \"wheat\", \"labelKey\": \"wheat\", \"icon\": \"tabler-wheat\"},\n {\"id\": \"corn\", \"labelKey\": \"corn\", \"icon\": \"tabler-plant-2\"},\n {\"id\": \"cotton\", \"labelKey\": \"cotton\", \"icon\": \"tabler-flower\"},\n {\"id\": \"saffron\", \"labelKey\": \"saffron\", \"icon\": \"tabler-flower-2\"},\n {\"id\": \"canola\", \"labelKey\": \"canola\", \"icon\": \"tabler-leaf\"},\n {\"id\": \"vegetables\", \"labelKey\": \"vegetables\", \"icon\": \"tabler-carrot\"}\n ]\n }\n}"}]},{"name":"Get recommendation (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"crop_id\": \"wheat\",\n \"soilType\": \"Loamy\",\n \"waterQuality\": \"Medium EC\",\n \"climateZone\": \"Temperate\"\n}"},"url":"{{baseUrl}}/api/irrigation-recommendation/recommend/","description":"Optional body: crop_id, farm info. Returns static plan. Input not processed."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"plan\": {\n \"frequencyPerWeek\": 4,\n \"durationMinutes\": 45,\n \"bestTimeOfDay\": \"05:00 - 07:00\",\n \"moistureLevel\": 72,\n \"warning\": \"Avoid irrigation during midday hours in the coming week due to forecasted high temperatures.\"\n }\n }\n}"}]}],"variable":[{"key":"baseUrl","value":"http://localhost:8000"}]}
+8
View File
@@ -0,0 +1,8 @@
from django.urls import path
from .views import ConfigView, RecommendView
urlpatterns = [
path("config/", ConfigView.as_view(), name="irrigation-recommendation-config"),
path("recommend/", RecommendView.as_view(), name="irrigation-recommendation-recommend"),
]
+71
View File
@@ -0,0 +1,71 @@
"""
Irrigation Recommendation API views.
Plain Django only; no DRF. No database. All responses are static mock data.
Response format: {"status": "success", "data": <payload>}. HTTP 200 only.
No processing, validation, or use of input parameters in responses.
CSRF exempt on POST so frontend can call without token.
"""
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from .mock_data import CONFIG_RESPONSE_DATA, RECOMMEND_RESPONSE_DATA
class ConfigView(View):
"""
GET endpoint for irrigation config (farm info and crop options).
Purpose:
Returns static farm info (soilType, waterQuality, climateZone) and
crop options list for the irrigation recommendation form. Used when
loading the irrigation recommendation page.
Input parameters:
None. Query parameters, if sent, are not read or used.
Response structure:
- status: string, always "success".
- data: object with keys farmInfo (object), cropOptions (array of
{ id, labelKey, icon }).
No processing or validation is performed on inputs.
"""
def get(self, request):
return JsonResponse(
{"status": "success", "data": CONFIG_RESPONSE_DATA},
status=200,
)
@method_decorator(csrf_exempt, name="dispatch")
class RecommendView(View):
"""
POST endpoint for irrigation recommendation.
Purpose:
Returns a static irrigation plan (frequencyPerWeek, durationMinutes,
bestTimeOfDay, moistureLevel, warning). Body may contain crop_id
and farm info; not read or used in response.
Input parameters:
- body (optional): JSON. May contain "crop_id", "soilType", "waterQuality",
"climateZone". Data type: object. Location: body. Not read or validated;
not used in response.
Response structure:
- status: string, always "success".
- data: object with key "plan" (object with frequencyPerWeek,
durationMinutes, bestTimeOfDay, moistureLevel, warning).
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(
{"status": "success", "data": RECOMMEND_RESPONSE_DATA},
status=200,
)
View File
+7
View File
@@ -0,0 +1,7 @@
from django.apps import AppConfig
class PestDetectionConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "pest_detection"
verbose_name = "Pest Detection"
+11
View File
@@ -0,0 +1,11 @@
"""
Static mock data for Pest Detection API.
No database, no dynamic values. Used for analyze endpoint response.
"""
ANALYZE_RESPONSE_DATA = {
"pest": "شپشک",
"confidence": 92,
"description": "حشرات کوچک مکنده شیره که باعث پیچ خوردگی برگ می‌شوند.",
"treatment": "یک بار در هفته از اسپری روغن نیم استفاده کنید.",
}
@@ -0,0 +1 @@
{"info":{"name":"Pest Detection","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json","description":"Pest Detection API. POST analyze (optional body). Returns static pest result. No database."},"item":[{"name":"Analyze image (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{}"},"url":"{{baseUrl}}/api/pest-detection/analyze/","description":"POST with optional body (e.g. image reference). Returns static pest, confidence, description, treatment. Input not processed."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"pest\": \"شپشک\",\n \"confidence\": 92,\n \"description\": \"حشرات کوچک مکنده شیره که باعث پیچ خوردگی برگ می‌شوند.\",\n \"treatment\": \"یک بار در هفته از اسپری روغن نیم استفاده کنید.\"\n }\n}"}]}],"variable":[{"key":"baseUrl","value":"http://localhost:8000"}]}
+7
View File
@@ -0,0 +1,7 @@
from django.urls import path
from .views import AnalyzeView
urlpatterns = [
path("analyze/", AnalyzeView.as_view(), name="pest-detection-analyze"),
]
+43
View File
@@ -0,0 +1,43 @@
"""
Pest Detection API views.
Plain Django only; no DRF. No database. All responses are static mock data.
Response format: {"status": "success", "data": <payload>}. HTTP 200 only.
No processing, validation, or use of input parameters in responses.
CSRF exempt so frontend can call POST without token.
"""
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from .mock_data import ANALYZE_RESPONSE_DATA
@method_decorator(csrf_exempt, name="dispatch")
class AnalyzeView(View):
"""
POST endpoint for pest detection analysis.
Purpose:
Returns a static pest detection result (pest name, confidence,
description, treatment). Used when the user uploads a plant image
and requests analysis. No processing is performed on the request.
Input parameters:
- body (optional): JSON or form-data; may contain image or file.
Data type: object. Location: body. Not read or validated; not used in response.
Response structure:
- status: string, always "success".
- data: object with keys pest (string), confidence (number),
description (string), treatment (string).
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(
{"status": "success", "data": ANALYZE_RESPONSE_DATA},
status=200,
)
+1
View File
@@ -0,0 +1 @@
+7
View File
@@ -0,0 +1,7 @@
from django.apps import AppConfig
class PlantSimulatorConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "plant_simulator"
verbose_name = "Plant Simulator"
+176
View File
@@ -0,0 +1,176 @@
"""
Static mock data for Plant Simulator API.
Matches PLANT_SIMULATOR_API.md. No database, no dynamic values.
Smooth animation: 51 points (010s, ~5 frames per second).
"""
# ---------------------------------------------------------------------------
# GET /api/plant-simulator/config (ورود: فقط اسلایدرها)
# ---------------------------------------------------------------------------
CONFIG_SLIDERS_ONLY = {
"sliders": [
{
"key": "light",
"label": "نور",
"min": 0,
"max": 100,
"step": 5,
"unit_type": "percent",
"default_value": 75,
"icon": "☀️",
},
{
"key": "water",
"label": "آب",
"min": 0,
"max": 100,
"step": 5,
"unit_type": "percent",
"default_value": 65,
"icon": "💧",
},
{
"key": "soil_ph",
"label": "pH خاک",
"min": 4,
"max": 9,
"step": 0.5,
"unit_type": "number",
"unit": "",
"default_value": 6.5,
},
{
"key": "growth_speed",
"label": "سرعت رشد",
"min": 0.5,
"max": 5,
"step": 0.5,
"unit_type": "number",
"unit": "×",
"default_value": 1.5,
},
],
}
# ---------------------------------------------------------------------------
# POST /api/plant-simulator/start (ثابت‌ها + چارت کانفیگ + plant + progress + chart)
# ---------------------------------------------------------------------------
CONSTANTS = {
"max_height": 280,
"max_leaves": 14,
"max_branches": 6,
"max_yield": 500,
"yield_unit": "g",
"yield_rate_unit": "g/s",
"height_unit": "px",
}
CHART_CONFIG = {
"title": "پیشرفت رشد",
"x_axis_label": "زمان (ثانیه)",
"series": [
{
"key": "height",
"label": "ارتفاع (px)",
"y_axis_id": "yHeight",
"min": 0,
"max": 280,
"unit": "px",
},
{
"key": "leaves",
"label": "تعداد برگ",
"y_axis_id": "yLeaf",
"min": 0,
"max": 14,
},
{
"key": "yield",
"label": "محصول (g)",
"y_axis_id": "yYield",
"min": 0,
"max": 500,
"unit": "g",
},
{
"key": "yield_rate",
"label": "نرخ محصول (g/s)",
"y_axis_id": "yYieldRate",
"min": 0,
"unit": "g/s",
},
],
}
# 51 نقطه برای انیمیشن نرم (۰ تا ۱۰ ثانیه، هر ~۰٫۲s)
_labels = [f"{i * 0.2:.1f}s" for i in range(51)]
_height = [round(142 * (i / 50) ** 0.9) for i in range(51)] # رشد کمی شتاب‌دار
_leaf = [min(5, int(i / 10)) for i in range(51)] # 0,0..,1,1..,2,...,5
_yield = [round(12.4 * (i / 50) ** 1.2, 1) for i in range(51)] # محصول با شتاب ملایم
_yield_rate = [round(0.087 * max(0, (i - 15) / 35), 3) for i in range(51)] # نرخ از ثانیه ~۳
START_RESPONSE_DATA = {
"constants": CONSTANTS,
"chart": CHART_CONFIG,
"plant": {
"height": 142,
"leaves_count": 5,
"branches_count": 2,
"fruits_count": 0,
"yield": 12.4,
"yield_rate": 0.087,
"tick": 520,
"is_healthy": True,
"can_continue": True,
},
"progress": {
"growth_progress": 50,
"light_status": 75,
"water_status": 65,
"yield_progress": 2.5,
"yield_current": 12.4,
"yield_rate_current": 0.087,
},
"chart_history": {
"labels": _labels,
"height_history": _height,
"leaf_history": _leaf,
"yield_history": _yield,
"yield_rate_history": _yield_rate,
},
}
# ---------------------------------------------------------------------------
# GET /api/plant-simulator/state (plant + progress + chart history)
# ---------------------------------------------------------------------------
STATE_RESPONSE_DATA = {
"plant": {
"height": 142,
"leaves_count": 5,
"branches_count": 2,
"fruits_count": 0,
"yield": 12.4,
"yield_rate": 0.087,
"tick": 520,
"is_healthy": True,
"can_continue": True,
},
"progress": {
"growth_progress": 50,
"light_status": 75,
"water_status": 65,
"yield_progress": 2.5,
"yield_current": 12.4,
"yield_rate_current": 0.087,
},
"chart": {
"labels": _labels,
"height_history": _height,
"leaf_history": _leaf,
"yield_history": _yield,
"yield_rate_history": _yield_rate,
},
}
@@ -0,0 +1,101 @@
{
"info": {
"name": "Plant Simulator",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "Plant Simulator API. GET config (sliders only), GET state, POST start (body: environment + growth_speed per serializers), stop/reset/environment. Static mock only."
},
"item": [
{
"name": "Get config (GET)",
"request": {
"method": "GET",
"header": [{"key": "Content-Type", "value": "application/json"}],
"url": "{{baseUrl}}/api/plant-simulator/config/",
"description": "ورود: فقط اسلایدرها. Returns sliders only (key, label, min, max, step, unit_type, default_value, icon)."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"sliders\": [\n {\"key\": \"light\", \"label\": \"نور\", \"min\": 0, \"max\": 100, \"step\": 5, \"unit_type\": \"percent\", \"default_value\": 75, \"icon\": \"☀️\"},\n {\"key\": \"water\", \"label\": \"آب\", \"min\": 0, \"max\": 100, \"step\": 5, \"unit_type\": \"percent\", \"default_value\": 65, \"icon\": \"💧\"},\n {\"key\": \"soil_ph\", \"label\": \"pH خاک\", \"min\": 4, \"max\": 9, \"step\": 0.5, \"unit_type\": \"number\", \"unit\": \"\", \"default_value\": 6.5},\n {\"key\": \"growth_speed\", \"label\": \"سرعت رشد\", \"min\": 0.5, \"max\": 5, \"step\": 0.5, \"unit_type\": \"number\", \"unit\": \"×\", \"default_value\": 1.5}\n ]\n }\n}"
}
]
},
{
"name": "Get state (GET)",
"request": {
"method": "GET",
"header": [{"key": "Content-Type", "value": "application/json"}],
"url": "{{baseUrl}}/api/plant-simulator/state/",
"description": "Returns static plant state, progress, and chart history (plant, progress, chart)."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"plant\": {\"height\": 142, \"leaves_count\": 5, \"branches_count\": 2, \"fruits_count\": 0, \"yield\": 12.4, \"yield_rate\": 0.087, \"tick\": 520, \"is_healthy\": true, \"can_continue\": true},\n \"progress\": {\"growth_progress\": 50, \"light_status\": 75, \"water_status\": 65, \"yield_progress\": 2.5, \"yield_current\": 12.4, \"yield_rate_current\": 0.087},\n \"chart\": {\n \"labels\": [\"0s\", \"1s\", \"2s\", \"3s\", \"4s\", \"5s\", \"6s\", \"7s\", \"8s\", \"9s\", \"10s\"],\n \"height_history\": [0, 5, 12, 28, 45, 68, 92, 110, 125, 135, 142],\n \"leaf_history\": [0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5],\n \"yield_history\": [0, 0, 0, 0.1, 0.5, 1.2, 3.2, 5.8, 8.2, 10.1, 12.4],\n \"yield_rate_history\": [0, 0, 0, 0.01, 0.03, 0.05, 0.06, 0.07, 0.08, 0.085, 0.087]\n }\n }\n}"
}
]
},
{
"name": "Start simulation (POST)",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {
"mode": "raw",
"raw": "{\n \"environment\": {\n \"light\": 75,\n \"water\": 65,\n \"soil_ph\": 6.5\n },\n \"growth_speed\": 1.5\n}"
},
"url": "{{baseUrl}}/api/plant-simulator/start/",
"description": "Body per serializers.START_REQUEST_EXAMPLE_STATIC: environment (light, water, soil_ph) + growth_speed. Returns constants, chart, plant, progress, chart_history."
},
"response": [
{
"name": "Success",
"status": "OK",
"code": 200,
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"constants\": {\"max_height\": 280, \"max_leaves\": 14, \"max_branches\": 6, \"max_yield\": 500, \"yield_unit\": \"g\", \"yield_rate_unit\": \"g/s\", \"height_unit\": \"px\"},\n \"chart\": {\"title\": \"پیشرفت رشد\", \"x_axis_label\": \"زمان (ثانیه)\", \"series\": [{\"key\": \"height\", \"label\": \"ارتفاع (px)\", \"y_axis_id\": \"yHeight\", \"min\": 0, \"max\": 280, \"unit\": \"px\"}, {\"key\": \"leaves\", \"label\": \"تعداد برگ\", \"y_axis_id\": \"yLeaf\", \"min\": 0, \"max\": 14}, {\"key\": \"yield\", \"label\": \"محصول (g)\", \"y_axis_id\": \"yYield\", \"min\": 0, \"max\": 500, \"unit\": \"g\"}, {\"key\": \"yield_rate\", \"label\": \"نرخ محصول (g/s)\", \"y_axis_id\": \"yYieldRate\", \"min\": 0, \"unit\": \"g/s\"}]},\n \"plant\": {\"height\": 142, \"leaves_count\": 5, \"branches_count\": 2, \"fruits_count\": 0, \"yield\": 12.4, \"yield_rate\": 0.087, \"tick\": 520, \"is_healthy\": true, \"can_continue\": true},\n \"progress\": {\"growth_progress\": 50, \"light_status\": 75, \"water_status\": 65, \"yield_progress\": 2.5, \"yield_current\": 12.4, \"yield_rate_current\": 0.087},\n \"chart_history\": {\"labels\": [\"0.0s\", \"0.2s\", \"0.4s\", \"1.0s\", \"2.0s\", \"5.0s\", \"10.0s\"], \"height_history\": [0, 2, 5, 18, 45, 110, 142], \"leaf_history\": [0, 0, 0, 0, 0, 1, 5], \"yield_history\": [0, 0, 0, 0.2, 1.5, 6.2, 12.4], \"yield_rate_history\": [0, 0, 0, 0.01, 0.03, 0.06, 0.087]}\n }\n}"
}
]
},
{
"name": "Stop simulation (POST)",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {"mode": "raw", "raw": "{}"},
"url": "{{baseUrl}}/api/plant-simulator/stop/",
"description": "Empty body. Returns success only."
},
"response": [{"name": "Success", "status": "OK", "code": 200, "body": "{\n \"status\": \"success\"\n}"}]
},
{
"name": "Reset simulation (POST)",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {"mode": "raw", "raw": "{}"},
"url": "{{baseUrl}}/api/plant-simulator/reset/",
"description": "Empty body. Returns success only."
},
"response": [{"name": "Success", "status": "OK", "code": 200, "body": "{\n \"status\": \"success\"\n}"}]
},
{
"name": "Update environment (PATCH)",
"request": {
"method": "PATCH",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {
"mode": "raw",
"raw": "{\n \"environment\": {\n \"light\": 80,\n \"water\": 70,\n \"soil_ph\": 6.5\n },\n \"growth_speed\": 2\n}"
},
"url": "{{baseUrl}}/api/plant-simulator/environment/",
"description": "Body: environment (same keys as sliders) + optional growth_speed. Returns success only."
},
"response": [{"name": "Success", "status": "OK", "code": 200, "body": "{\n \"status\": \"success\"\n}"}]
}
],
"variable": [{"key": "baseUrl", "value": "http://localhost:8000"}]
}
+62
View File
@@ -0,0 +1,62 @@
"""
Response serialization for Plant Simulator API.
Plain Django; no DRF. Builds response envelope only. All payloads are static mock data.
Request: on POST /start the client must send a body with keys matching the sliders
from config (light, water, soil_ph) plus growth_speed. See START_REQUEST_EXAMPLE.
No validation or use of request in response; backend returns static mock only.
"""
from .mock_data import CONFIG_SLIDERS_ONLY
# ---------------------------------------------------------------------------
# POST /start — بدنه‌ای که کلاینت باید ارسال کند (مطابق اسلایدرهای config)
# ---------------------------------------------------------------------------
# کلیدهای environment (همان key هر اسلایدر به‌جز growth_speed)
START_ENVIRONMENT_KEYS = [
item["key"]
for item in CONFIG_SLIDERS_ONLY["sliders"]
if item["key"] != "growth_speed"
]
# مقدار پیش‌فرض هر اسلایدر برای ساخت نمونه درخواست
def _defaults_from_sliders():
return {
item["key"]: item["default_value"]
for item in CONFIG_SLIDERS_ONLY["sliders"]
}
# نمونه بدنه درخواست start که کلاینت باید ارسال کند
START_REQUEST_EXAMPLE = {
"environment": {
k: v for k, v in _defaults_from_sliders().items() if k != "growth_speed"
},
"growth_speed": _defaults_from_sliders().get("growth_speed", 1.5),
}
# برای سازگاری: همان ساختار به صورت ثابت (بدون وابستگی به محاسبه)
START_REQUEST_EXAMPLE_STATIC = {
"environment": {
"light": 75,
"water": 65,
"soil_ph": 6.5,
},
"growth_speed": 1.5,
}
def success_response():
"""
Response when endpoint does not return data.
Returns: {"status": "success"}
"""
return {"status": "success"}
def success_with_data(data):
"""
Response when endpoint returns data.
Returns: {"status": "success", "data": data}
"""
return {"status": "success", "data": data}
+19
View File
@@ -0,0 +1,19 @@
from django.urls import path
from .views import (
ConfigView,
EnvironmentView,
ResetView,
StartView,
StateView,
StopView,
)
urlpatterns = [
path("config/", ConfigView.as_view(), name="plant-simulator-config"),
path("state/", StateView.as_view(), name="plant-simulator-state"),
path("start/", StartView.as_view(), name="plant-simulator-start"),
path("stop/", StopView.as_view(), name="plant-simulator-stop"),
path("reset/", ResetView.as_view(), name="plant-simulator-reset"),
path("environment/", EnvironmentView.as_view(), name="plant-simulator-environment"),
]
+161
View File
@@ -0,0 +1,161 @@
"""
Plant Simulator API views.
Plain Django only; no DRF. No database. All responses are static mock data.
Response format: {"status": "success"} or {"status": "success", "data": <payload>}. HTTP 200 only.
No processing, validation, or use of input parameters in responses.
CSRF exempt so frontend (e.g. localhost:3000) can call POST/PATCH without token.
"""
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from .mock_data import CONFIG_SLIDERS_ONLY, START_RESPONSE_DATA, STATE_RESPONSE_DATA
from .serializers import success_response, success_with_data
@method_decorator(csrf_exempt, name="dispatch")
class ConfigView(View):
"""
GET endpoint for simulator configuration (ورود).
Purpose:
Returns only sliders (min, max, step, unit, label, default_value, icon).
Used when loading/entering the simulator page.
Input parameters:
None. Query parameters, if sent, are not read or used.
Response structure:
- status: string, always "success".
- data: object with key "sliders" (array of slider configs).
No processing or validation is performed on inputs.
"""
def get(self, request):
return JsonResponse(success_with_data(CONFIG_SLIDERS_ONLY), status=200)
@method_decorator(csrf_exempt, name="dispatch")
class StateView(View):
"""
GET endpoint for plant state, progress, and chart history.
Purpose:
Returns static plant state (height, leaves_count, branches_count,
fruits_count, yield, yield_rate, tick, is_healthy, can_continue),
progress (growth_progress, light_status, water_status, yield_progress,
yield_current, yield_rate_current), and chart (labels, height_history,
leaf_history, yield_history, yield_rate_history). Used during or after
simulation for UI and chart.
Input parameters:
None. Query parameters, if sent, are not read or used.
Response structure:
- status: string, always "success".
- data: object with keys "plant", "progress", "chart" per
PLANT_SIMULATOR_API.md §3.
No processing or validation is performed on inputs.
"""
def get(self, request):
return JsonResponse(success_with_data(STATE_RESPONSE_DATA), status=200)
@method_decorator(csrf_exempt, name="dispatch")
class StartView(View):
"""
POST endpoint to start simulation.
Purpose:
Returns constants, chart config, plant state, progress, and chart
history. Body may contain environment and growth_speed; not read or used.
Input parameters:
- body (optional): JSON. May contain "environment" and "growth_speed".
Location: body. Not read or validated; not used in response.
Response structure:
- status: string, always "success".
- data: object with "constants", "chart", "plant", "progress", "chart_history".
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(success_with_data(START_RESPONSE_DATA), status=200)
@method_decorator(csrf_exempt, name="dispatch")
class StopView(View):
"""
POST endpoint to stop simulation.
Purpose:
Accepts stop request. Returns success only. No processing performed.
Body may be empty or contain session_id; not read or used.
Input parameters:
- body (optional): JSON or empty. Location: body. Not read or used.
Response structure:
- status: string, always "success".
No "data" field.
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(success_response(), status=200)
@method_decorator(csrf_exempt, name="dispatch")
class ResetView(View):
"""
POST endpoint to reset simulation.
Purpose:
Accepts reset request. Returns success only. No processing performed.
Body may be empty or contain session_id; not read or used.
Input parameters:
- body (optional): JSON or empty. Location: body. Not read or used.
Response structure:
- status: string, always "success".
No "data" field.
No processing or validation is performed on inputs.
"""
def post(self, request):
return JsonResponse(success_response(), status=200)
@method_decorator(csrf_exempt, name="dispatch")
class EnvironmentView(View):
"""
PATCH endpoint to update environment (slider values).
Purpose:
Accepts environment update. Returns success only. No processing
performed. Body may contain environment and growth_speed; not
read or used in the response.
Input parameters:
- body (optional): JSON. May contain "environment" (object)
and "growth_speed" (number). Location: body. Not read or used.
Response structure:
- status: string, always "success".
No "data" field.
No processing or validation is performed on inputs.
"""
def patch(self, request):
return JsonResponse(success_response(), status=200)
+1
View File
@@ -0,0 +1 @@