Files
Ai/location_data/tasks.py
T
2026-04-29 01:27:29 +03:30

98 lines
3.1 KiB
Python

"""
تسک‌های Celery برای واکشی داده‌های خاک.
"""
from decimal import Decimal
from config.celery import app
from django.apps import apps
from django.db import transaction
from .models import SoilDepthData, SoilLocation
from .soil_adapters import DEPTHS
try:
import requests
except ImportError: # pragma: no cover - handled in stripped envs
RequestException = Exception
else:
RequestException = requests.RequestException
def fetch_soil_data_for_coordinates(
latitude: float,
longitude: float,
task_id: str = "",
progress_callback=None,
):
"""
واکشی سنکرون داده خاک برای مختصات داده‌شده و ذخیره در DB.
این helper هم توسط Celery task و هم توسط endpointهای sync استفاده می‌شود.
"""
lat = Decimal(str(round(float(latitude), 6)))
lon = Decimal(str(round(float(longitude), 6)))
adapter = apps.get_app_config("location_data").get_soil_data_adapter()
with transaction.atomic():
location, created = SoilLocation.objects.select_for_update().get_or_create(
latitude=lat,
longitude=lon,
defaults={"task_id": task_id},
)
if not created and task_id:
location.task_id = task_id
location.save(update_fields=["task_id"])
for index, depth in enumerate(DEPTHS):
if progress_callback is not None:
progress_callback(
state="PROGRESS",
meta={
"current": index + 1,
"total": len(DEPTHS),
"message": f"در حال واکشی عمق {depth}...",
},
)
fields = adapter.fetch_depth_fields(float(lon), float(lat), depth)
with transaction.atomic():
SoilDepthData.objects.update_or_create(
soil_location=location,
depth_label=depth,
defaults=fields,
)
if task_id:
with transaction.atomic():
location.task_id = ""
location.save(update_fields=["task_id"])
return {
"status": "completed",
"location_id": location.id,
"depths": DEPTHS,
}
@app.task(bind=True)
def fetch_soil_data_task(self, latitude: float, longitude: float):
"""
واکشی داده‌های خاک برای مختصات داده‌شده و ذخیره در DB.
برای هر عمق (0-5cm, 5-15cm, 15-30cm) یک ریکوئست/شبیه‌سازی جدا انجام می‌شود.
"""
try:
return fetch_soil_data_for_coordinates(
latitude=latitude,
longitude=longitude,
task_id=self.request.id,
progress_callback=self.update_state,
)
except RequestException as exc:
lat = Decimal(str(round(float(latitude), 6)))
lon = Decimal(str(round(float(longitude), 6)))
location = SoilLocation.objects.filter(latitude=lat, longitude=lon).first()
return {
"status": "error",
"location_id": getattr(location, "id", None),
"error": str(exc),
}