Files
Ai/docs/location_and_farm_data_apps_explained.md
T

513 lines
17 KiB
Markdown
Raw Normal View History

2026-04-29 23:54:30 +03:30
# توضیح `location_data/apps.py` و `farm_data/apps.py`
این فایل یک توضیح کوتاه ولی کاربردی از دو فایل تنظیمات اپ Django در پروژه می‌دهد:
- `location_data/apps.py`
- `farm_data/apps.py`
همچنین برای فهم بهتر، به فیلدهای مهم مدل‌های مرتبط هم اشاره می‌کند تا معلوم شود این دو app در عمل چه داده‌هایی را مدیریت می‌کنند.
---
## 1) فایل `location_data/apps.py`
این فایل AppConfig مربوط به اپ `location_data` را تعریف می‌کند.
کلاس اصلی:
```python
class SoilDataConfig(AppConfig):
```
### فیلدها و بخش‌ها
#### `default_auto_field = "django.db.models.BigAutoField"`
- مشخص می‌کند اگر در مدل‌های این اپ برای primary key چیزی تعریف نشده باشد، Django به‌صورت پیش‌فرض از `BigAutoField` استفاده کند.
- `BigAutoField` یک شناسه عددی auto-increment بزرگ است.
- این گزینه بیشتر برای مدل‌هایی مفید است که قرار است رکوردهای زیادی داشته باشند.
#### `name = "location_data"`
- نام کامل اپ Django است.
- Django با این مقدار اپ را register می‌کند.
- این مقدار باید با مسیر ماژول اپ یکی باشد.
#### `verbose_name = "Soil Data (SoilGrids)"`
- نام نمایشی اپ در Django admin یا جاهایی است که Django نام انسانی اپ را نشان می‌دهد.
- این مقدار بیشتر جنبه نمایشی دارد و روی منطق برنامه اثر مستقیم ندارد.
---
## propertyها و سرویس‌ها در `location_data/apps.py`
این فایل فقط metadata اپ را نگه نمی‌دارد؛ دو سرویس reusable هم از طریق AppConfig در اختیار بقیه پروژه می‌گذارد.
### `@cached_property def ndvi_health_service(self)`
- این property یک نمونه از `NdviHealthService` می‌سازد.
- import آن از فایل `.ndvi` انجام می‌شود.
- به دلیل `cached_property` فقط یک بار ساخته می‌شود و بعد همان instance دوباره استفاده می‌شود.
کاربرد:
- برای تحلیل یا سرویس‌های مرتبط با NDVI
- جلوگیری از ساخت مکرر object
### `@cached_property def soil_data_adapter(self)`
این property adapter مناسب برای داده خاک را بر اساس تنظیمات پروژه انتخاب می‌کند.
دو adapter پشتیبانی می‌شوند:
- `SoilGridsAdapter`
- `MockSoilDataAdapter`
#### منطق انتخاب provider
مقدار provider از این setting خوانده می‌شود:
```python
settings.SOIL_DATA_PROVIDER
```
اگر وجود نداشته باشد، مقدار پیش‌فرض:
```python
"mock"
```
#### حالت اول: `provider == "soilgrids"`
در این حالت:
- از `SoilGridsAdapter` استفاده می‌شود
- timeout آن از این setting می‌آید:
```python
settings.SOILGRIDS_TIMEOUT_SECONDS
```
اگر این setting هم نباشد، مقدار پیش‌فرض:
```python
60
```
یعنی درخواست به provider واقعی SoilGrids حداکثر 60 ثانیه صبر می‌کند.
#### حالت دوم: `provider == "mock"`
در این حالت:
- از `MockSoilDataAdapter` استفاده می‌شود
- delay آن از این setting می‌آید:
```python
settings.SOIL_MOCK_DELAY_SECONDS
```
اگر این setting هم نباشد، مقدار پیش‌فرض:
```python
0.8
```
یعنی adapter تستی/نمایشی با تاخیر مصنوعی 0.8 ثانیه کار می‌کند.
#### حالت نامعتبر
اگر `SOIL_DATA_PROVIDER` چیزی غیر از `soilgrids` یا `mock` باشد:
- `ValueError` رخ می‌دهد
- یعنی config پروژه اشتباه است و provider شناخته نشده
---
## ارتباط `location_data/apps.py` با فیلدهای واقعی داده
این فایل خودش مدل تعریف نمی‌کند، اما به‌صورت مستقیم برای کار با مدل‌های اپ `location_data` استفاده می‌شود؛ مهم‌ترین آن‌ها این‌ها هستند:
- `location_data.models.SoilLocation`
- `location_data.models.SoilDepthData`
- `location_data.models.NdviObservation`
### فیلدهای مهم `SoilLocation`
#### `latitude`
- عرض جغرافیایی مرکز زمین
- نوع آن `DecimalField` است
- روی آن index وجود دارد
- این نقطه معمولاً مرکز هندسی مزرعه است، نه لزوماً یکی از گوشه‌های مرز
#### `longitude`
- طول جغرافیایی مرکز زمین
- مثل `latitude` برای lookup و resolve کردن داده‌های خاک استفاده می‌شود
#### `task_id`
- شناسه تسک Celery برای پردازش‌های async
- وقتی fetch داده خاک یا پردازش مرتبط در صف باشد، می‌توان با این فیلد وضعیت را track کرد
#### `farm_boundary`
- مرز مزرعه را به‌صورت JSON نگه می‌دارد
- معمولاً به‌شکل `Polygon` یا ساختار corner-based ذخیره می‌شود
- این فیلد خیلی مهم است چون فقط یک نقطه center نگه نمی‌دارید، بلکه شکل کلی زمین هم ثبت می‌شود
#### `created_at` / `updated_at`
- زمان ایجاد و آخرین به‌روزرسانی رکورد
### propertyهای مهم `SoilLocation`
#### `center_latitude`
- فقط alias برای `latitude` است
#### `center_longitude`
- فقط alias برای `longitude` است
#### `is_complete`
- بررسی می‌کند آیا هر سه لایه خاک برای این location ثبت شده‌اند یا نه
- شرط آن این است که تعداد `depths` دقیقاً 3 باشد
### فیلدهای مهم `SoilDepthData`
این مدل برای هر location سه رکورد عمق خاک نگه می‌دارد:
- `0-5cm`
- `5-15cm`
- `15-30cm`
فیلدهای اصلی:
- `soil_location`: ارتباط به `SoilLocation`
- `depth_label`: مشخص می‌کند داده برای کدام عمق است
- `bdod`: چگالی ظاهری خاک
- `cec`: ظرفیت تبادل کاتیونی
- `cfvo`: حجم قطعات درشت خاک
- `clay`: درصد رس
- `nitrogen`: مقدار نیتروژن
- `ocd` و `ocs`: شاخص‌های کربن آلی
- `phh2o`: pH خاک
- `sand`: درصد شن
- `silt`: درصد سیلت
- `soc`: کربن آلی خاک
- `wv0010`: رطوبت حجمی در فشار 10 kPa
- `wv0033`: رطوبت در حدود ظرفیت زراعی
- `wv1500`: رطوبت در نقطه پژمردگی دائم
این فیلدها برای شبیه‌سازی، آبیاری، و تخمین وضعیت واقعی خاک مهم هستند.
### فیلدهای مهم `NdviObservation`
- `location`: ارتباط با `SoilLocation`
- `observation_date`: تاریخ مشاهده
- `mean_ndvi`: میانگین NDVI
- `ndvi_map`: داده مکانی NDVI
- `vegetation_health_class`: کلاس سلامت پوشش گیاهی
- `satellite_source`: منبع تصویر مثل `sentinel-2`
- `cloud_cover`: درصد پوشش ابر
- `metadata`: داده تکمیلی
---
## نکته مهم: grid بندی زمین انجام می‌شود
بله، در لایه داده و سنجش از دور، مفهوم grid بندی وجود دارد.
اما این grid بندی در پروژه بیشتر در این دو سطح دیده می‌شود:
### 1) grid در NDVI map
در `location_data/remote_sensing.py` داده NDVI به‌صورت grid محاسبه و ذخیره می‌شود.
یعنی:
- تصویر ماهواره‌ای به خانه‌های کوچک‌تر تقسیم می‌شود
- برای هر خانه مقدار NDVI محاسبه می‌شود
- خروجی در `ndvi_map` معمولاً به‌شکل grid نگه‌داری می‌شود
این یعنی وضعیت سلامت گیاه فقط به‌صورت یک عدد کلی نیست، بلکه می‌تواند روی بخش‌های مختلف زمین map شود.
### 2) grid/cell در adapter خاک
در `location_data/soil_adapters.py` هم منطق cell/grid دیده می‌شود، مخصوصاً در adapterهای mock یا interpolation-based.
یعنی:
- مختصات lat/lon به cellهای شبکه‌ای نگاشت می‌شود
- در بعضی محاسبات از `grid_x` و `grid_y` استفاده می‌شود
- این کمک می‌کند داده خاک برای ناحیه‌های نزدیک، رفتار مکانی منطقی‌تری داشته باشد
### نتیجه مهم
خود مدل `SoilLocation` یک مرکز زمین را نگه می‌دارد، ولی مرز مزرعه و NDVI grid باعث می‌شوند سیستم فقط point-based نباشد.
یعنی:
- مرکز زمین برای lookup سریع و اتصال به داده خاک/هوا استفاده می‌شود
- مرز مزرعه برای شکل واقعی زمین ذخیره می‌شود
- grid بندی برای تحلیل مکانی، مخصوصاً در NDVI، انجام می‌شود
---
## مرکز زمین چطور از مرز مزرعه به‌دست می‌آید
در `farm_data/services.py` از روی `farm_boundary`، مرکز هندسی polygon محاسبه می‌شود.
پس flow کلی این‌طور است:
1. مرز مزرعه ارسال می‌شود
2. polygon نرمال می‌شود
3. centroid هندسی آن محاسبه می‌شود
4. یک `SoilLocation` برای center ساخته یا پیدا می‌شود
5. بعد داده خاک، NDVI و هوا به این location متصل می‌شوند
پس زمین فقط با یک نقطه خام ثبت نمی‌شود؛ اول مرز دارد، بعد center از روی آن به‌دست می‌آید.
---
## متدهای کمکی `location_data/apps.py`
### `get_ndvi_health_service()`
- خروجی `self.ndvi_health_service` را برمی‌گرداند
- یک accessor ساده برای گرفتن سرویس NDVI است
### `get_soil_data_adapter()`
- خروجی `self.soil_data_adapter` را برمی‌گرداند
- بقیه بخش‌های پروژه از این متد برای گرفتن adapter فعال استفاده می‌کنند
/
---
## فیلدها و settingهای مهم مرتبط با `location_data/apps.py`
### فیلدهای AppConfig
- `default_auto_field`: نوع primary key پیش‌فرض مدل‌ها
- `name`: نام داخلی اپ
- `verbose_name`: نام نمایشی اپ
### settingهای استفاده‌شده
- `SOIL_DATA_PROVIDER`: انتخاب provider فعال خاک
- `SOILGRIDS_TIMEOUT_SECONDS`: timeout برای provider واقعی SoilGrids
- `SOIL_MOCK_DELAY_SECONDS`: تاخیر مصنوعی برای provider mock
---
## 2) فایل `farm_data/apps.py`
این فایل AppConfig مربوط به اپ `farm_data` را تعریف می‌کند.
کلاس اصلی:
```python
class FarmDataConfig(AppConfig):
```
### فیلدها
#### `default_auto_field = "django.db.models.BigAutoField"`
- مثل اپ قبلی، تعیین می‌کند primary key پیش‌فرض مدل‌های این اپ از نوع `BigAutoField` باشد.
#### `name = "farm_data"`
- نام داخلی و ماژول اپ Django است.
- برای شناسایی اپ در `INSTALLED_APPS` و registry داخلی Django استفاده می‌شود.
#### `label = "sensor_data"`
- label داخلی اپ در registry Django است.
- این فیلد زمانی مهم می‌شود که:
- بخواهید نام registry اپ با `name` فرق داشته باشد
- یا از تداخل نام اپ‌ها جلوگیری کنید
- در این پروژه، اپ `farm_data` با label داخلی `sensor_data` شناخته می‌شود.
نکته:
- `label` باید در کل پروژه یکتا باشد.
- این مقدار ممکن است در migrationها، relationها یا lookupهای app registry اثر داشته باشد.
#### `verbose_name = "farm-data"`
- نام نمایشی اپ است.
- بیشتر برای admin و نمایش انسانی استفاده می‌شود.
---
## نکته مهم درباره `farm_data/apps.py`
برخلاف `location_data/apps.py`، این فایل:
- `cached_property` ندارد
- service locator ندارد
- adapter یا provider انتخاب نمی‌کند
یعنی فعلاً فقط نقش config پایه اپ را دارد.
---
## ارتباط `farm_data/apps.py` با فیلدهای واقعی داده
این app بیشتر داده‌های farm-level و sensor-level را نگه می‌دارد. مهم‌ترین مدل‌هایش:
- `farm_data.models.SensorData`
- `farm_data.models.SensorParameter`
- `farm_data.models.ParameterUpdateLog`
### فیلدهای مهم `SensorData`
#### `farm_uuid`
- شناسه یکتای مزرعه
- primary key این مدل است
- هر رکورد `SensorData` نماینده یک مزرعه است
#### `center_location`
- ارتباط به `location_data.SoilLocation`
- یعنی این مزرعه به یک location مرکزی وصل است
- از همین نقطه مرکزی برای weather/soil/simulation استفاده می‌شود
#### `weather_forecast`
- ارتباط اختیاری با `weather.WeatherForecast`
- اگر موجود باشد، forecast منتخب یا آخرین forecast به مزرعه وصل می‌شود
#### `sensor_payload`
- مهم‌ترین فیلد این مدل است
- داده سنسورها به‌صورت JSON نگه‌داری می‌شود
- ساختار معمول آن شبیه این است:
```json
{
"sensor-7-1": {
"soil_moisture": 25.5,
"soil_temperature": 22.3,
"soil_ph": 7.2
}
}
```
مزیت این ساختار:
- چند سنسور در یک مزرعه پشتیبانی می‌شود
- هر سنسور می‌تواند فیلدهای خاص خودش را داشته باشد
- schema سنسورها rigid نیست
#### `plants`
- رابطه چندبه‌چند با `plant.Plant`
- یعنی یک farm می‌تواند چند گیاه مرتبط داشته باشد
#### `irrigation_method`
- روش آبیاری انتخاب‌شده برای مزرعه
- برای recommendation و planning مهم است
#### `created_at` / `updated_at`
- زمان ایجاد و آخرین ویرایش رکورد
### propertyهای مهم `SensorPayloadMixin`
مدل `SensorData` از `SensorPayloadMixin` ارث می‌گیرد و این helperها را دارد:
#### `_payload()`
- payload را فقط وقتی dict معتبر باشد برمی‌گرداند
#### `get_sensor_block(sensor_key=None)`
- اگر `sensor_key` بدهید، همان بلوک سنسور را برمی‌گرداند
- اگر ندهید، اولین بلوک معتبر را برمی‌گرداند
#### `get_metric(metric_name, sensor_key=None)`
- یک metric خاص را از payload پیدا می‌کند
- اول در sensor مشخص‌شده می‌گردد
- اگر پیدا نشد، در بقیه blockها جستجو می‌کند
#### propertyهای آماده
این propertyها shortcut هستند:
- `soil_moisture`
- `soil_temperature`
- `soil_ph`
- `electrical_conductivity`
- `nitrogen`
- `phosphorus`
- `potassium`
یعنی به‌جای parse دستی JSON، مستقیم می‌توان این متریک‌ها را خواند.
### فیلدهای مهم `SensorParameter`
این مدل dictionary پارامترهای سنسور را نگه می‌دارد.
#### `sensor_key`
- کلید سنسور مثل `sensor-7-1`
#### `code`
- کد پارامتر مثل `soil_moisture`
#### `name_fa`
- نام فارسی پارامتر
#### `unit`
- واحد پارامتر مثل `%` یا `dS/m`
#### `data_type`
- نوع داده مثل `float`, `int`, `string`, `bool`
#### `metadata`
- داده تکمیلی برای UI یا validation
- مثلاً:
- بازه مجاز
- توضیح
- تنظیمات نمایش
### فیلدهای مهم `ParameterUpdateLog`
- `parameter`: ارتباط به `SensorParameter`
- `action`: نوع عملیات مثل `added` یا `modified`
- `payload`: خلاصه تغییرات
- `updated_at`: زمان ثبت لاگ
این مدل برای audit و پیگیری تغییرات پارامترها مفید است.
---
## جمع‌بندی
### `location_data/apps.py`
- هم metadata اپ را نگه می‌دارد
- هم سرویس و adapter در اختیار پروژه می‌گذارد
- هم از settingها برای انتخاب provider واقعی یا mock استفاده می‌کند
- و در عمل با location center، مرز مزرعه، داده لایه‌های خاک و gridهای NDVI کار می‌کند
### `farm_data/apps.py`
- فقط config پایه AppConfig را تعریف می‌کند
- نقش آن بیشتر register کردن اپ با نام و label مشخص است
- اما داده‌های اصلی مزرعه مثل `farm_uuid`، `sensor_payload`، گیاه، روش آبیاری و اتصال به center location در مدل‌های همین app نگه‌داری می‌شوند