first commit

This commit is contained in:
2026-03-19 22:54:29 +03:30
parent 1a178f39b7
commit 035bc6f74d
91 changed files with 3821 additions and 130 deletions
+1
View File
@@ -0,0 +1 @@
+19
View File
@@ -0,0 +1,19 @@
from django.contrib import admin
from .models import IrrigationMethod
@admin.register(IrrigationMethod)
class IrrigationMethodAdmin(admin.ModelAdmin):
list_display = (
"id",
"name",
"category",
"water_efficiency_percent",
"soil_type",
"climate_suitability",
"created_at",
)
list_filter = ("category", "climate_suitability")
search_fields = ("name", "category")
readonly_fields = ("created_at", "updated_at")
+7
View File
@@ -0,0 +1,7 @@
from django.apps import AppConfig
class IrrigationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "irrigation"
verbose_name = "Irrigation"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1 @@
@@ -0,0 +1,100 @@
"""
Management command to seed initial irrigation methods.
Run: python manage.py seed_irrigation_methods
"""
from django.core.management.base import BaseCommand
from irrigation.models import IrrigationMethod
INITIAL_METHODS = [
{
"name": "آبیاری قطره‌ای",
"category": "موضعی",
"description": "آب با دبی کم و به‌صورت قطره‌ای مستقیماً به ریشه گیاه رسانده می‌شود. مناسب‌ترین روش برای مناطق خشک و کم‌آب.",
"water_efficiency_percent": 90.0,
"water_pressure_required": "۱-۲ اتمسفر",
"flow_rate": "۲-۸ لیتر در ساعت",
"coverage_area": "بسته به طراحی سیستم",
"soil_type": "تمام انواع خاک",
"climate_suitability": "گرم و خشک",
},
{
"name": "آبیاری بارانی",
"category": "تحت فشار",
"description": "آب تحت فشار از طریق آبپاش‌ها به‌صورت قطرات ریز مانند باران پخش می‌شود.",
"water_efficiency_percent": 75.0,
"water_pressure_required": "۲-۴ اتمسفر",
"flow_rate": "۵-۲۰ لیتر در دقیقه",
"coverage_area": "۱۰-۳۰ متر شعاع پاشش",
"soil_type": "لومی، لومی شنی",
"climate_suitability": "معتدل، مرطوب",
},
{
"name": "آبیاری سطحی (غرقابی)",
"category": "سطحی",
"description": "آب در سطح زمین جاری شده و به‌صورت ثقلی زمین را آبیاری می‌کند. ساده‌ترین و قدیمی‌ترین روش.",
"water_efficiency_percent": 50.0,
"water_pressure_required": "نیاز به فشار ندارد (ثقلی)",
"flow_rate": "متغیر بر اساس شیب زمین",
"coverage_area": "وابسته به اندازه کرت",
"soil_type": "رسی، لومی رسی",
"climate_suitability": "تمام اقلیم‌ها (مناطق پرآب)",
},
{
"name": "آبیاری نشتی (تیپ)",
"category": "موضعی",
"description": "آب از طریق نوارهای تیپ با منافذ ریز به‌صورت نشتی به خاک رسانده می‌شود.",
"water_efficiency_percent": 85.0,
"water_pressure_required": "۰.۵-۱.۵ اتمسفر",
"flow_rate": "۱-۴ لیتر در ساعت به ازای هر متر",
"coverage_area": "ردیفی، مناسب زراعت",
"soil_type": "لومی، لومی شنی",
"climate_suitability": "گرم و خشک",
},
{
"name": "آبیاری زیرسطحی",
"category": "موضعی",
"description": "لوله‌های آبیاری در زیر سطح خاک کار گذاشته شده و آب مستقیماً به منطقه ریشه می‌رسد.",
"water_efficiency_percent": 95.0,
"water_pressure_required": "۱-۲ اتمسفر",
"flow_rate": "۱-۴ لیتر در ساعت",
"coverage_area": "بسته به طراحی",
"soil_type": "لومی، لومی رسی",
"climate_suitability": "تمام اقلیم‌ها",
},
{
"name": "آبیاری بابلر",
"category": "موضعی",
"description": "آب با دبی بیشتر از قطره‌ای ولی کمتر از بارانی، به‌صورت حبابی در پای درخت پخش می‌شود. مناسب درختان میوه.",
"water_efficiency_percent": 80.0,
"water_pressure_required": "۱-۲ اتمسفر",
"flow_rate": "۸-۶۰ لیتر در ساعت",
"coverage_area": "شعاع ۱-۲ متر اطراف درخت",
"soil_type": "لومی، لومی رسی",
"climate_suitability": "گرم و خشک",
},
]
class Command(BaseCommand):
help = "Seed initial irrigation methods (6 common methods)"
def handle(self, *args, **options):
created_count = 0
for method_data in INITIAL_METHODS:
_, created = IrrigationMethod.objects.get_or_create(
name=method_data["name"],
defaults=method_data,
)
if created:
created_count += 1
self.stdout.write(
self.style.SUCCESS(f" Created: {method_data['name']}")
)
self.stdout.write(
self.style.SUCCESS(
f"\nDone. Created {created_count} new irrigation methods."
)
)
+36
View File
@@ -0,0 +1,36 @@
# Generated by Django 5.2.12 on 2026-03-19 15:01
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='IrrigationMethod',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(db_index=True, help_text='نام روش آبیاری (قطره\u200cای، بارانی، سطحی و …)', max_length=255, unique=True)),
('category', models.CharField(blank=True, help_text='نوع روش (موضعی، تحت فشار، سطحی)', max_length=255)),
('description', models.TextField(blank=True, help_text='توضیحات کامل روش')),
('water_efficiency_percent', models.FloatField(blank=True, help_text='راندمان مصرف آب (%)', null=True)),
('water_pressure_required', models.CharField(blank=True, help_text='فشار مورد نیاز آب', max_length=255)),
('flow_rate', models.CharField(blank=True, help_text='دبی یا میزان جریان آب', max_length=255)),
('coverage_area', models.CharField(blank=True, help_text='مساحت قابل پوشش', max_length=255)),
('soil_type', models.CharField(blank=True, help_text='نوع خاک مناسب', max_length=255)),
('climate_suitability', models.CharField(blank=True, help_text='اقلیم مناسب', max_length=255)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'روش آبیاری',
'verbose_name_plural': 'روش\u200cهای آبیاری',
'ordering': ['name'],
},
),
]
+1
View File
@@ -0,0 +1 @@
+63
View File
@@ -0,0 +1,63 @@
from django.db import models
class IrrigationMethod(models.Model):
"""
روش‌های آبیاری شامل مشخصات فنی.
"""
name = models.CharField(
max_length=255,
unique=True,
db_index=True,
help_text="نام روش آبیاری (قطره‌ای، بارانی، سطحی و …)",
)
category = models.CharField(
max_length=255,
blank=True,
help_text="نوع روش (موضعی، تحت فشار، سطحی)",
)
description = models.TextField(
blank=True,
help_text="توضیحات کامل روش",
)
water_efficiency_percent = models.FloatField(
null=True,
blank=True,
help_text="راندمان مصرف آب (%)",
)
water_pressure_required = models.CharField(
max_length=255,
blank=True,
help_text="فشار مورد نیاز آب",
)
flow_rate = models.CharField(
max_length=255,
blank=True,
help_text="دبی یا میزان جریان آب",
)
coverage_area = models.CharField(
max_length=255,
blank=True,
help_text="مساحت قابل پوشش",
)
soil_type = models.CharField(
max_length=255,
blank=True,
help_text="نوع خاک مناسب",
)
climate_suitability = models.CharField(
max_length=255,
blank=True,
help_text="اقلیم مناسب",
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ["name"]
verbose_name = "روش آبیاری"
verbose_name_plural = "روش‌های آبیاری"
def __str__(self):
return self.name
+25
View File
@@ -0,0 +1,25 @@
from rest_framework import serializers
from .models import IrrigationMethod
class IrrigationMethodSerializer(serializers.ModelSerializer):
"""سریالایزر خروجی / ورودی برای IrrigationMethod."""
class Meta:
model = IrrigationMethod
fields = [
"id",
"name",
"category",
"description",
"water_efficiency_percent",
"water_pressure_required",
"flow_rate",
"coverage_area",
"soil_type",
"climate_suitability",
"created_at",
"updated_at",
]
read_only_fields = ["id", "created_at", "updated_at"]
+8
View File
@@ -0,0 +1,8 @@
from django.urls import path
from .views import IrrigationMethodDetailView, IrrigationMethodListCreateView
urlpatterns = [
path("", IrrigationMethodListCreateView.as_view(), name="irrigation-list-create"),
path("<int:pk>/", IrrigationMethodDetailView.as_view(), name="irrigation-detail"),
]
+180
View File
@@ -0,0 +1,180 @@
from drf_spectacular.utils import (
OpenApiExample,
OpenApiResponse,
extend_schema,
)
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import IrrigationMethod
from .serializers import IrrigationMethodSerializer
class IrrigationMethodListCreateView(APIView):
"""لیست تمام روش‌های آبیاری و ایجاد روش جدید."""
@extend_schema(
tags=["Irrigation"],
summary="لیست روش‌های آبیاری",
description="لیست تمام روش‌های آبیاری ذخیره‌شده را برمی‌گرداند.",
responses={200: IrrigationMethodSerializer(many=True)},
)
def get(self, request):
methods = IrrigationMethod.objects.all()
serializer = IrrigationMethodSerializer(methods, many=True)
return Response(
{"code": 200, "msg": "success", "data": serializer.data},
status=status.HTTP_200_OK,
)
@extend_schema(
tags=["Irrigation"],
summary="ایجاد روش آبیاری جدید",
description="یک روش آبیاری جدید ایجاد می‌کند.",
request=IrrigationMethodSerializer,
responses={
201: IrrigationMethodSerializer,
400: OpenApiResponse(description="داده نامعتبر"),
},
examples=[
OpenApiExample(
"نمونه درخواست",
value={
"name": "آبیاری قطره‌ای",
"category": "موضعی",
"description": "آبیاری با دبی کم و فشار مناسب",
"water_efficiency_percent": 90.0,
"water_pressure_required": "۱-۲ اتمسفر",
"flow_rate": "۲-۸ لیتر در ساعت",
"coverage_area": "بسته به طراحی سیستم",
"soil_type": "تمام انواع خاک",
"climate_suitability": "گرم و خشک",
},
request_only=True,
),
],
)
def post(self, request):
serializer = IrrigationMethodSerializer(data=request.data)
if not serializer.is_valid():
return Response(
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
serializer.save()
return Response(
{"code": 201, "msg": "success", "data": serializer.data},
status=status.HTTP_201_CREATED,
)
class IrrigationMethodDetailView(APIView):
"""دریافت، ویرایش و حذف یک روش آبیاری."""
def _get_method(self, pk):
return IrrigationMethod.objects.filter(pk=pk).first()
@extend_schema(
tags=["Irrigation"],
summary="جزئیات روش آبیاری",
description="مشخصات یک روش آبیاری را بر اساس شناسه برمی‌گرداند.",
responses={
200: IrrigationMethodSerializer,
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
},
)
def get(self, request, pk):
method = self._get_method(pk)
if not method:
return Response(
{"code": 404, "msg": "روش آبیاری یافت نشد.", "data": None},
status=status.HTTP_404_NOT_FOUND,
)
serializer = IrrigationMethodSerializer(method)
return Response(
{"code": 200, "msg": "success", "data": serializer.data},
status=status.HTTP_200_OK,
)
@extend_schema(
tags=["Irrigation"],
summary="ویرایش کامل روش آبیاری",
description="تمام فیلدهای یک روش آبیاری را آپدیت می‌کند.",
request=IrrigationMethodSerializer,
responses={
200: IrrigationMethodSerializer,
400: OpenApiResponse(description="داده نامعتبر"),
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
},
)
def put(self, request, pk):
method = self._get_method(pk)
if not method:
return Response(
{"code": 404, "msg": "روش آبیاری یافت نشد.", "data": None},
status=status.HTTP_404_NOT_FOUND,
)
serializer = IrrigationMethodSerializer(method, data=request.data)
if not serializer.is_valid():
return Response(
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
serializer.save()
return Response(
{"code": 200, "msg": "success", "data": serializer.data},
status=status.HTTP_200_OK,
)
@extend_schema(
tags=["Irrigation"],
summary="ویرایش جزئی روش آبیاری",
description="فقط فیلدهای ارسال‌شده آپدیت می‌شوند.",
request=IrrigationMethodSerializer,
responses={
200: IrrigationMethodSerializer,
400: OpenApiResponse(description="داده نامعتبر"),
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
},
)
def patch(self, request, pk):
method = self._get_method(pk)
if not method:
return Response(
{"code": 404, "msg": "روش آبیاری یافت نشد.", "data": None},
status=status.HTTP_404_NOT_FOUND,
)
serializer = IrrigationMethodSerializer(method, data=request.data, partial=True)
if not serializer.is_valid():
return Response(
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
serializer.save()
return Response(
{"code": 200, "msg": "success", "data": serializer.data},
status=status.HTTP_200_OK,
)
@extend_schema(
tags=["Irrigation"],
summary="حذف روش آبیاری",
description="یک روش آبیاری را حذف می‌کند.",
responses={
200: OpenApiResponse(description="حذف موفق"),
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
},
)
def delete(self, request, pk):
method = self._get_method(pk)
if not method:
return Response(
{"code": 404, "msg": "روش آبیاری یافت نشد.", "data": None},
status=status.HTTP_404_NOT_FOUND,
)
method.delete()
return Response(
{"code": 200, "msg": "روش آبیاری با موفقیت حذف شد.", "data": None},
status=status.HTTP_200_OK,
)