# توضیح `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 نگه‌داری می‌شوند