This commit is contained in:
2026-05-11 03:27:21 +03:30
parent cf7cbb937c
commit d0e68a1a56
854 changed files with 102985 additions and 76 deletions
@@ -0,0 +1,125 @@
# Generated by Django 5.2.12 on 2026-03-19 15:01
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="FarmType",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("uuid", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, unique=True)),
("name", models.CharField(db_index=True, max_length=255, unique=True)),
("description", models.TextField(blank=True, default="")),
("metadata", models.JSONField(blank=True, default=dict)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
],
options={
"db_table": "farm_types",
"ordering": ["name"],
},
),
migrations.CreateModel(
name="FarmHub",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("farm_uuid", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, unique=True)),
("name", models.CharField(max_length=255)),
("is_active", models.BooleanField(default=True)),
("customization", models.JSONField(blank=True, default=dict)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"farm_type",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="farms",
to="farm_hub.farmtype",
),
),
(
"owner",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="farm_hubs",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"db_table": "farm_hubs",
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="Product",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("uuid", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, unique=True)),
("name", models.CharField(db_index=True, max_length=255)),
("description", models.TextField(blank=True, default="")),
("metadata", models.JSONField(blank=True, default=dict)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"farm_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="products",
to="farm_hub.farmtype",
),
),
],
options={
"db_table": "products",
"ordering": ["name"],
},
),
migrations.CreateModel(
name="FarmSensor",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("uuid", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, unique=True)),
("name", models.CharField(max_length=255)),
("sensor_type", models.CharField(blank=True, default="", max_length=255)),
("is_active", models.BooleanField(default=True)),
("specifications", models.JSONField(blank=True, default=dict)),
("power_source", models.JSONField(blank=True, default=dict)),
("customization", models.JSONField(blank=True, default=dict)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"farm",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="sensors",
to="farm_hub.farmhub",
),
),
],
options={
"db_table": "farm_sensors",
"ordering": ["-created_at"],
},
),
migrations.AddField(
model_name="farmhub",
name="products",
field=models.ManyToManyField(blank=True, related_name="farms", to="farm_hub.product"),
),
migrations.AddConstraint(
model_name="product",
constraint=models.UniqueConstraint(fields=("farm_type", "name"), name="unique_product_per_farm_type"),
),
]
@@ -0,0 +1,34 @@
from django.db import migrations
FARM_TYPES = {
"زراعی": ["گندم", "ذرت", "جو", "کلزا", "پنبه"],
"درختی": ["سیب", "پسته", "انگور", "انار"],
"غرقابی": ["برنج"],
"گلخانه ای": ["گوجه فرنگی", "خیار", "فلفل دلمه ای"],
}
def seed_catalog(apps, schema_editor):
FarmType = apps.get_model("farm_hub", "FarmType")
Product = apps.get_model("farm_hub", "Product")
for farm_type_name, products in FARM_TYPES.items():
farm_type, _ = FarmType.objects.get_or_create(name=farm_type_name)
for product_name in products:
Product.objects.get_or_create(farm_type=farm_type, name=product_name)
def unseed_catalog(apps, schema_editor):
FarmType = apps.get_model("farm_hub", "FarmType")
FarmType.objects.filter(name__in=FARM_TYPES.keys()).delete()
class Migration(migrations.Migration):
dependencies = [
("farm_hub", "0001_initial"),
]
operations = [
migrations.RunPython(seed_catalog, unseed_catalog),
]
@@ -0,0 +1,31 @@
# Generated by Django 5.2.12 on 2026-03-20 00:30
import uuid
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device_hub", "0001_initial"),
("farm_hub", "0002_seed_default_catalog"),
]
operations = [
migrations.AddField(
model_name="farmsensor",
name="physical_device_uuid",
field=models.UUIDField(db_index=True, default=uuid.uuid4, unique=True),
),
migrations.AddField(
model_name="farmsensor",
name="sensor_catalog",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="farm_sensors",
to="device_hub.sensorcatalog",
),
),
]
@@ -0,0 +1,33 @@
# Generated by Django 5.2.12 on 2026-03-20 01:30
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("crop_zoning", "0004_croparea_farm"),
("farm_hub", "0003_farmsensor_catalog_and_physical_device"),
]
operations = [
migrations.RemoveField(
model_name="farmhub",
name="customization",
),
migrations.RemoveField(
model_name="farmsensor",
name="customization",
),
migrations.AddField(
model_name="farmhub",
name="current_crop_area",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="current_for_farms",
to="crop_zoning.croparea",
),
),
]
@@ -0,0 +1,163 @@
import json
from django.db import migrations, models
DEFAULT_FARM_TYPE_NAME = "زراعی"
def _table_exists(schema_editor, table_name):
with schema_editor.connection.cursor() as cursor:
existing_tables = set(schema_editor.connection.introspection.table_names(cursor))
return table_name in existing_tables
def _deserialize_json(value):
if value in (None, "", b""):
return {}
if isinstance(value, (dict, list)):
return value
if isinstance(value, bytes):
value = value.decode("utf-8")
try:
return json.loads(value)
except (TypeError, ValueError):
return {}
def migrate_plant_rows_to_products(apps, schema_editor):
if not _table_exists(schema_editor, "plant_plant"):
return
FarmType = apps.get_model("farm_hub", "FarmType")
Product = apps.get_model("farm_hub", "Product")
farm_type, _ = FarmType.objects.get_or_create(name=DEFAULT_FARM_TYPE_NAME)
with schema_editor.connection.cursor() as cursor:
cursor.execute(
"""
SELECT
name,
light,
watering,
soil,
temperature,
planting_season,
harvest_time,
spacing,
fertilizer,
health_profile,
irrigation_profile,
growth_profile,
created_at,
updated_at
FROM plant_plant
"""
)
columns = [column[0] for column in cursor.description]
rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
for row in rows:
Product.objects.update_or_create(
farm_type=farm_type,
name=row["name"],
defaults={
"light": row["light"] or "",
"watering": row["watering"] or "",
"soil": row["soil"] or "",
"temperature": row["temperature"] or "",
"planting_season": row["planting_season"] or "",
"harvest_time": row["harvest_time"] or "",
"spacing": row["spacing"] or "",
"fertilizer": row["fertilizer"] or "",
"health_profile": _deserialize_json(row["health_profile"]),
"irrigation_profile": _deserialize_json(row["irrigation_profile"]),
"growth_profile": _deserialize_json(row["growth_profile"]),
"created_at": row["created_at"],
"updated_at": row["updated_at"],
},
)
def drop_legacy_plant_table(apps, schema_editor):
if _table_exists(schema_editor, "plant_plant"):
schema_editor.execute("DROP TABLE plant_plant")
class Migration(migrations.Migration):
dependencies = [
("farm_hub", "0004_remove_customization_add_current_crop_area"),
]
operations = [
migrations.AddField(
model_name="product",
name="fertilizer",
field=models.CharField(blank=True, default="", help_text="کود مناسب", max_length=255),
),
migrations.AddField(
model_name="product",
name="growth_profile",
field=models.JSONField(
blank=True,
default=dict,
help_text='پروفایل رشد محصول برای مدل GDD. {"base_temperature": 10, "required_gdd_for_maturity": 1200, "stage_thresholds": {"flowering": 500, "fruiting": 850}, "current_cumulative_gdd": 320}',
),
),
migrations.AddField(
model_name="product",
name="harvest_time",
field=models.CharField(blank=True, default="", help_text="زمان برداشت", max_length=255),
),
migrations.AddField(
model_name="product",
name="health_profile",
field=models.JSONField(
blank=True,
default=dict,
help_text='پروفایل سلامت محصول برای KPIها. ساختار نمونه: {"moisture": {"ideal_value": 65, "min_range": 45, "max_range": 75, "weight": 0.4}}',
),
),
migrations.AddField(
model_name="product",
name="irrigation_profile",
field=models.JSONField(
blank=True,
default=dict,
help_text='پروفایل آبیاری محصول برای محاسبات ETc. {"kc_initial": 0.6, "kc_mid": 1.15, "kc_end": 0.8, "growth_stage_duration": {"initial": 20, "mid": 30, "late": 25}}',
),
),
migrations.AddField(
model_name="product",
name="light",
field=models.CharField(blank=True, default="", help_text="نور مورد نیاز", max_length=255),
),
migrations.AddField(
model_name="product",
name="planting_season",
field=models.CharField(blank=True, default="", help_text="فصل کاشت", max_length=255),
),
migrations.AddField(
model_name="product",
name="soil",
field=models.CharField(blank=True, default="", help_text="خاک مناسب", max_length=255),
),
migrations.AddField(
model_name="product",
name="spacing",
field=models.CharField(blank=True, default="", help_text="فاصله کاشت", max_length=255),
),
migrations.AddField(
model_name="product",
name="temperature",
field=models.CharField(blank=True, default="", help_text="دمای مناسب", max_length=255),
),
migrations.AddField(
model_name="product",
name="watering",
field=models.CharField(blank=True, default="", help_text="آبیاری", max_length=255),
),
migrations.RunPython(migrate_plant_rows_to_products, migrations.RunPython.noop),
migrations.RunPython(drop_legacy_plant_table, migrations.RunPython.noop),
]
@@ -0,0 +1,55 @@
from django.db import migrations
CATALOG_SEED_DATA = {
"زراعی": [
{"name": "گندم", "planting_season": "پاییز", "harvest_time": "اواخر بهار", "soil": "لومی"},
{"name": "ذرت", "planting_season": "بهار", "harvest_time": "تابستان", "soil": "لومی شنی"},
{"name": "جو", "planting_season": "پاییز", "harvest_time": "اواخر بهار", "soil": "لومی"},
{"name": "کلزا", "planting_season": "پاییز", "harvest_time": "بهار", "soil": "لومی رسی"},
{"name": "پنبه", "planting_season": "بهار", "harvest_time": "پاییز", "soil": "لومی"},
],
"درختی": [
{"name": "سیب", "planting_season": "زمستان", "harvest_time": "پاییز", "soil": "لومی"},
{"name": "پسته", "planting_season": "زمستان", "harvest_time": "اواخر تابستان", "soil": "شنی لومی"},
{"name": "انگور", "planting_season": "اواخر زمستان", "harvest_time": "تابستان", "soil": "لومی"},
{"name": "انار", "planting_season": "اواخر زمستان", "harvest_time": "پاییز", "soil": "لومی شنی"},
],
"غرقابی": [
{"name": "برنج", "planting_season": "بهار", "harvest_time": "اواخر تابستان", "soil": "رسی"},
],
"گلخانه ای": [
{"name": "گوجه فرنگی", "planting_season": "چهار فصل", "harvest_time": "چند مرحله ای", "soil": "کوکوپیت"},
{"name": "خیار", "planting_season": "چهار فصل", "harvest_time": "چند مرحله ای", "soil": "پرلیت"},
{"name": "فلفل دلمه ای", "planting_season": "چهار فصل", "harvest_time": "چند مرحله ای", "soil": "بستر هیدروپونیک"},
],
}
def seed_expanded_catalog(apps, schema_editor):
FarmType = apps.get_model("farm_hub", "FarmType")
Product = apps.get_model("farm_hub", "Product")
for farm_type_name, products in CATALOG_SEED_DATA.items():
farm_type, _ = FarmType.objects.get_or_create(name=farm_type_name)
for product_data in products:
Product.objects.update_or_create(
farm_type=farm_type,
name=product_data["name"],
defaults={key: value for key, value in product_data.items() if key != "name"},
)
def unseed_expanded_catalog(apps, schema_editor):
FarmType = apps.get_model("farm_hub", "FarmType")
FarmType.objects.filter(name__in=CATALOG_SEED_DATA.keys()).delete()
class Migration(migrations.Migration):
dependencies = [
("farm_hub", "0005_product_profiles_and_plant_migration"),
]
operations = [
migrations.RunPython(seed_expanded_catalog, unseed_expanded_catalog),
]
@@ -0,0 +1,25 @@
# Generated by Django 5.2.12 on 2026-04-03
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("farm_hub", "0006_seed_expanded_product_catalog"),
("access_control", "0002_link_subscription_plan_to_farm"),
]
operations = [
migrations.AddField(
model_name="farmhub",
name="subscription_plan",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="farms",
to="access_control.subscriptionplan",
),
),
]
@@ -0,0 +1,25 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("farm_hub", "0007_farmhub_subscription_plan"),
]
operations = [
migrations.AddField(
model_name="product",
name="growth_stage",
field=models.CharField(blank=True, default="", help_text="مرحله رشد فعلی", max_length=255),
),
migrations.AddField(
model_name="product",
name="growth_stages",
field=models.JSONField(blank=True, default=list, help_text="فهرست مراحل رشد محصول"),
),
migrations.AddField(
model_name="product",
name="icon",
field=models.CharField(blank=True, default="", help_text="آیکون محصول برای فرانت", max_length=100),
),
]
@@ -0,0 +1,20 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("farm_hub", "0008_product_plant_selector_fields"),
]
operations = [
migrations.AddField(
model_name="farmhub",
name="irrigation_method_id",
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name="farmhub",
name="irrigation_method_name",
field=models.CharField(blank=True, default="", max_length=255),
),
]
@@ -0,0 +1,17 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("device_hub", "0001_initial"),
("farm_hub", "0009_farmhub_irrigation_method_fields"),
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=[],
state_operations=[
migrations.DeleteModel(name="FarmSensor"),
],
),
]