UPDATE
This commit is contained in:
@@ -17,6 +17,13 @@ from rag.services.yield_harvest import YieldHarvestRAGService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READINESS_STATUS_FA = {
|
||||
"ready": "آماده",
|
||||
"approaching": "نزدیک به آمادگی",
|
||||
"monitoring": "نیازمند پایش",
|
||||
"not_ready": "آماده نیست",
|
||||
}
|
||||
|
||||
|
||||
class YieldHarvestSummaryService:
|
||||
def get_summary(
|
||||
@@ -174,8 +181,8 @@ class YieldHarvestSummaryService:
|
||||
)
|
||||
|
||||
fallback_description = (
|
||||
f"Deterministic harvest forecast for {crop_name or 'the selected crop'} "
|
||||
f"in season {season_year}."
|
||||
f"پیش بینی قطعی برداشت برای {crop_name or 'محصول انتخاب شده'} "
|
||||
f"در فصل زراعی {season_year}."
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -188,7 +195,7 @@ class YieldHarvestSummaryService:
|
||||
"optimal_window_start": result.get("optimalWindowStart"),
|
||||
"optimal_window_end": result.get("optimalWindowEnd"),
|
||||
"description": result.get("description") or fallback_description,
|
||||
"descriptionSource": "deterministic",
|
||||
"descriptionSource": "قطعی",
|
||||
"field_conditions": {
|
||||
"soil_moisture": farm_context.get("recent_sensor_averages", {}).get("soil_moisture"),
|
||||
"soil_temperature": farm_context.get("recent_sensor_averages", {}).get("soil_temperature"),
|
||||
@@ -233,19 +240,19 @@ class YieldHarvestSummaryService:
|
||||
"season_year": season_year,
|
||||
"series": [
|
||||
{
|
||||
"name": "Predicted Yield",
|
||||
"name": "عملکرد پیش بینی شده",
|
||||
"type": "line",
|
||||
"data": yield_series,
|
||||
},
|
||||
{
|
||||
"name": "Biomass",
|
||||
"name": "بیوماس",
|
||||
"type": "area",
|
||||
"data": biomass_series,
|
||||
},
|
||||
],
|
||||
"xAxis": {"type": "datetime"},
|
||||
"xAxis": {"type": "datetime", "label": "تاریخ"},
|
||||
"meta": {
|
||||
"unit": "kg/ha",
|
||||
"unit": "کیلوگرم در هکتار",
|
||||
"simulation_engine": result.get("engine"),
|
||||
"simulation_model": result.get("model_name"),
|
||||
"scenario_id": result.get("scenario_id"),
|
||||
@@ -281,10 +288,10 @@ class YieldHarvestSummaryService:
|
||||
"days_until_harvest": days_until,
|
||||
"current_dvs": round(pcse_dvs_stage, 4),
|
||||
"summary": (
|
||||
f"Operations are prioritized for {farm_context.get('crop_name') or 'the selected crop'} "
|
||||
f"with {days_until} days remaining until the predicted harvest window."
|
||||
f"عملیات برداشت برای {farm_context.get('crop_name') or 'محصول انتخاب شده'} "
|
||||
f"با توجه به {days_until} روز باقی مانده تا بازه پیش بینی شده برداشت اولویت بندی شده است."
|
||||
),
|
||||
"rules_source": "deterministic_dvs_rules",
|
||||
"rules_source": "قواعد_قطعی_DVS",
|
||||
"field_context": {
|
||||
"soil_type": farm_context.get("soil", {}).get("soil_type"),
|
||||
"soil_moisture": farm_context.get("recent_sensor_averages", {}).get("soil_moisture"),
|
||||
@@ -325,7 +332,7 @@ class YieldHarvestSummaryService:
|
||||
"farm_uuid": farm_uuid,
|
||||
"crop_name": crop_name,
|
||||
"season_year": season_year,
|
||||
"title": "Season highlights",
|
||||
"title": "خلاصه فصل",
|
||||
# Left blank for narrative merge unless a non-LLM fallback is needed later.
|
||||
"subtitle": "",
|
||||
"total_predicted_yield": total_predicted_yield,
|
||||
@@ -357,7 +364,7 @@ class YieldHarvestSummaryService:
|
||||
"farm_uuid": farm_uuid,
|
||||
"averageReadiness": None,
|
||||
"zones": [],
|
||||
"source": "ndvi_health_service",
|
||||
"source": "سرویس_سلامت_NDVI",
|
||||
}
|
||||
|
||||
location = sensor.center_location
|
||||
@@ -398,7 +405,7 @@ class YieldHarvestSummaryService:
|
||||
zones.append(
|
||||
{
|
||||
"zoneId": f"zone-{zone_index}",
|
||||
"zoneLabel": f"Zone {zone_index}",
|
||||
"zoneLabel": f"ناحیه {zone_index}",
|
||||
"gridPosition": {"row": row_index, "col": col_index},
|
||||
"meanNdvi": cell_ndvi,
|
||||
"readiness": readiness,
|
||||
@@ -413,7 +420,7 @@ class YieldHarvestSummaryService:
|
||||
zones.append(
|
||||
{
|
||||
"zoneId": "zone-center",
|
||||
"zoneLabel": "Center field zone",
|
||||
"zoneLabel": "ناحیه مرکزی مزرعه",
|
||||
"gridPosition": None,
|
||||
"meanNdvi": latest_ndvi,
|
||||
"readiness": readiness,
|
||||
@@ -441,7 +448,7 @@ class YieldHarvestSummaryService:
|
||||
"ndviTrend": ndvi_trend,
|
||||
"averageReadiness": average_readiness,
|
||||
"zones": zones,
|
||||
"source": "ndvi_health_service",
|
||||
"source": "سرویس_سلامت_NDVI",
|
||||
}
|
||||
|
||||
def _to_unix_timestamp(self, value: Any) -> int | None:
|
||||
@@ -480,12 +487,12 @@ class YieldHarvestSummaryService:
|
||||
|
||||
def _readiness_status(self, readiness: int) -> str:
|
||||
if readiness >= 80:
|
||||
return "ready"
|
||||
return READINESS_STATUS_FA["ready"]
|
||||
if readiness >= 55:
|
||||
return "approaching"
|
||||
return READINESS_STATUS_FA["approaching"]
|
||||
if readiness >= 30:
|
||||
return "monitoring"
|
||||
return "not_ready"
|
||||
return READINESS_STATUS_FA["monitoring"]
|
||||
return READINESS_STATUS_FA["not_ready"]
|
||||
|
||||
def _build_yield_quality_bands(
|
||||
self,
|
||||
@@ -555,7 +562,7 @@ class YieldHarvestSummaryService:
|
||||
"farm_uuid": farm_uuid,
|
||||
"crop_name": crop_name,
|
||||
"season_year": season_year,
|
||||
"source": "deterministic_grading_rules",
|
||||
"source": "قواعد_قطعی_درجه_بندی",
|
||||
"is_estimated": True,
|
||||
"protein_content": {
|
||||
"value": protein_content,
|
||||
@@ -568,7 +575,7 @@ class YieldHarvestSummaryService:
|
||||
"grade_distribution": grade_distribution,
|
||||
"primary_quality_grade": primary_quality_grade,
|
||||
"quality_score": quality_score,
|
||||
"summary": f"Primary quality grade is {primary_quality_grade}.",
|
||||
"summary": f"درجه کیفیت غالب محصول {primary_quality_grade} است.",
|
||||
}
|
||||
|
||||
def _get_estimated_revenue(
|
||||
@@ -682,7 +689,7 @@ class YieldHarvestSummaryService:
|
||||
return {
|
||||
"farm_uuid": farm_uuid,
|
||||
"center_coordinates": None,
|
||||
"soil": {"provider": getattr(settings, "SOIL_DATA_PROVIDER", "unknown")},
|
||||
"soil": {"provider": getattr(settings, "SOIL_DATA_PROVIDER", "نامشخص")},
|
||||
"recent_sensor_averages": {},
|
||||
}
|
||||
|
||||
@@ -712,7 +719,7 @@ class YieldHarvestSummaryService:
|
||||
},
|
||||
"farm_boundary": farm_details.get("center_location", {}).get("farm_boundary"),
|
||||
"soil": {
|
||||
"provider": getattr(settings, "SOIL_DATA_PROVIDER", "unknown"),
|
||||
"provider": getattr(settings, "SOIL_DATA_PROVIDER", "نامشخص"),
|
||||
"soil_type": self._infer_soil_type(soil_details),
|
||||
"resolved_metrics": soil_details,
|
||||
},
|
||||
@@ -735,14 +742,14 @@ class YieldHarvestSummaryService:
|
||||
|
||||
def _map_dvs_to_phase(self, dvs: float) -> tuple[str, str]:
|
||||
if dvs >= 2.0:
|
||||
return "ready", "maturity"
|
||||
return "آماده", "رسیدگی"
|
||||
if dvs >= 1.7:
|
||||
return "final_pre_harvest", "late_reproductive"
|
||||
return "پیش_برداشت_نهایی", "زایشی_پایانی"
|
||||
if dvs >= 1.2:
|
||||
return "mid_pre_harvest", "grain_fill"
|
||||
return "پیش_برداشت_میانی", "پرشدن_دانه"
|
||||
if dvs >= 0.8:
|
||||
return "monitoring", "reproductive_transition"
|
||||
return "early_pre_harvest", "vegetative"
|
||||
return "پایش", "گذار_زایشی"
|
||||
return "پیش_برداشت_ابتدایی", "رشد_رویشی"
|
||||
|
||||
def _build_operations_steps(
|
||||
self,
|
||||
@@ -753,74 +760,74 @@ class YieldHarvestSummaryService:
|
||||
) -> list[dict[str, Any]]:
|
||||
field_ready = soil_moisture is None or soil_moisture <= 35.0
|
||||
|
||||
if phase_name == "maturity":
|
||||
if phase_name == "رسیدگی":
|
||||
return [
|
||||
{
|
||||
"key": "desiccation",
|
||||
"title": "Desiccation check",
|
||||
"status": "ready",
|
||||
"title": "بررسی خشک شدن محصول",
|
||||
"status": "آماده",
|
||||
"is_completed": False,
|
||||
"estimated_days": 0,
|
||||
},
|
||||
{
|
||||
"key": "harvesting",
|
||||
"title": "Harvesting",
|
||||
"status": "ready" if field_ready else "watch_field_conditions",
|
||||
"title": "برداشت",
|
||||
"status": "آماده" if field_ready else "نیازمند بررسی شرایط مزرعه",
|
||||
"is_completed": False,
|
||||
"estimated_days": max(min(days_until, 2), 0),
|
||||
},
|
||||
{
|
||||
"key": "transportation",
|
||||
"title": "Transportation",
|
||||
"status": "ready",
|
||||
"title": "انتقال محصول",
|
||||
"status": "آماده",
|
||||
"is_completed": False,
|
||||
"estimated_days": max(min(days_until + 1, 3), 1),
|
||||
},
|
||||
]
|
||||
if phase_name == "late_reproductive":
|
||||
if phase_name == "زایشی_پایانی":
|
||||
return [
|
||||
{
|
||||
"key": "equipment_check",
|
||||
"title": "Inspect harvest equipment",
|
||||
"status": "priority",
|
||||
"title": "بازبینی تجهیزات برداشت",
|
||||
"status": "اولویت بالا",
|
||||
"is_completed": False,
|
||||
"estimated_days": 1,
|
||||
},
|
||||
{
|
||||
"key": "labor_plan",
|
||||
"title": "Confirm labor and transport plan",
|
||||
"status": "priority",
|
||||
"title": "نهایی کردن برنامه نیروی کار و حمل",
|
||||
"status": "اولویت بالا",
|
||||
"is_completed": False,
|
||||
"estimated_days": 2,
|
||||
},
|
||||
{
|
||||
"key": "field_entry",
|
||||
"title": "Verify field access and dry windows",
|
||||
"status": "ready" if field_ready else "monitor",
|
||||
"title": "بررسی امکان ورود به مزرعه و بازه های خشک",
|
||||
"status": "آماده" if field_ready else "پایش",
|
||||
"is_completed": False,
|
||||
"estimated_days": max(min(days_until, 5), 1),
|
||||
},
|
||||
]
|
||||
if phase_name == "grain_fill":
|
||||
if phase_name == "پرشدن_دانه":
|
||||
return [
|
||||
{
|
||||
"key": "monitor_maturity",
|
||||
"title": "Track maturity and storage organ growth",
|
||||
"status": "active",
|
||||
"title": "پایش رسیدگی و رشد اندام ذخیره ای",
|
||||
"status": "در حال انجام",
|
||||
"is_completed": False,
|
||||
"estimated_days": 7,
|
||||
},
|
||||
{
|
||||
"key": "review_readiness",
|
||||
"title": "Review zone readiness differences",
|
||||
"status": "active",
|
||||
"title": "بررسی اختلاف آمادگی بین ناحیه ها",
|
||||
"status": "در حال انجام",
|
||||
"is_completed": False,
|
||||
"estimated_days": 10,
|
||||
},
|
||||
{
|
||||
"key": "prepare_logistics",
|
||||
"title": "Prepare harvest logistics plan",
|
||||
"status": "upcoming",
|
||||
"title": "آماده سازی برنامه لجستیک برداشت",
|
||||
"status": "پیش رو",
|
||||
"is_completed": False,
|
||||
"estimated_days": 14,
|
||||
},
|
||||
@@ -828,22 +835,22 @@ class YieldHarvestSummaryService:
|
||||
return [
|
||||
{
|
||||
"key": "weekly_monitoring",
|
||||
"title": "Run weekly crop maturity checks",
|
||||
"status": "active",
|
||||
"title": "پایش هفتگی رسیدگی محصول",
|
||||
"status": "در حال انجام",
|
||||
"is_completed": False,
|
||||
"estimated_days": 14,
|
||||
},
|
||||
{
|
||||
"key": "update_forecast",
|
||||
"title": "Refresh harvest timing forecast",
|
||||
"status": "active",
|
||||
"title": "به روزرسانی پیش بینی زمان برداشت",
|
||||
"status": "در حال انجام",
|
||||
"is_completed": False,
|
||||
"estimated_days": 10,
|
||||
},
|
||||
{
|
||||
"key": "draft_operations",
|
||||
"title": "Draft harvest operation checklist",
|
||||
"status": "upcoming",
|
||||
"title": "تهیه چک لیست عملیات برداشت",
|
||||
"status": "پیش رو",
|
||||
"is_completed": False,
|
||||
"estimated_days": 21,
|
||||
},
|
||||
@@ -856,12 +863,12 @@ class YieldHarvestSummaryService:
|
||||
if sand is None or clay is None or silt is None:
|
||||
return None
|
||||
if clay >= 40:
|
||||
return "clay"
|
||||
return "رسی"
|
||||
if sand >= 70 and clay <= 15:
|
||||
return "sandy"
|
||||
return "شنی"
|
||||
if silt >= 50 and clay < 27:
|
||||
return "silty_loam"
|
||||
return "loam"
|
||||
return "سیلتی لوم"
|
||||
return "لوم"
|
||||
|
||||
def _safe_float(self, value: Any, default: float | None = 0.0) -> float | None:
|
||||
try:
|
||||
@@ -932,33 +939,33 @@ class YieldHarvestSummaryService:
|
||||
highlights = payload.get("season_highlights_card") or {}
|
||||
total_yield = highlights.get("total_predicted_yield")
|
||||
unit = highlights.get("yield_unit") or ""
|
||||
harvest_date = highlights.get("target_harvest_date") or "the predicted harvest window"
|
||||
harvest_date = highlights.get("target_harvest_date") or "بازه پیش بینی شده برداشت"
|
||||
if total_yield is None:
|
||||
return f"Harvest is targeted for {harvest_date} based on the deterministic season outlook."
|
||||
return f"Predicted yield is {total_yield} {unit} and harvest is targeted for {harvest_date}.".strip()
|
||||
return f"بر اساس چشم انداز قطعی فصل، برداشت برای {harvest_date} هدف گذاری شده است."
|
||||
return f"عملکرد پیش بینی شده {total_yield} {unit} است و برداشت برای {harvest_date} هدف گذاری شده است.".strip()
|
||||
|
||||
def _default_yield_prediction_explanation(self, payload: dict[str, Any]) -> str:
|
||||
yield_card = payload.get("yield_prediction") or {}
|
||||
predicted = yield_card.get("predicted_yield_tons")
|
||||
unit = yield_card.get("unit") or ""
|
||||
if predicted is None:
|
||||
return "Yield forecast is based on the deterministic crop simulation output."
|
||||
return f"Yield forecast is based on the deterministic crop simulation and currently projects {predicted} {unit}.".strip()
|
||||
return "پیش بینی عملکرد بر پایه خروجی قطعی شبیه سازی محصول محاسبه شده است."
|
||||
return f"پیش بینی عملکرد بر پایه شبیه سازی قطعی محصول انجام شده و در حال حاضر مقدار {predicted} {unit} را نشان می دهد.".strip()
|
||||
|
||||
def _default_harvest_readiness_summary(self, payload: dict[str, Any]) -> str:
|
||||
readiness = payload.get("harvest_readiness_zones") or {}
|
||||
average = readiness.get("averageReadiness")
|
||||
if average is None:
|
||||
return "Harvest readiness is derived from the latest deterministic zone signals."
|
||||
return f"Average harvest readiness is {average} based on the latest deterministic zone signals.".strip()
|
||||
return "آمادگی برداشت از آخرین سیگنال های قطعی ناحیه ای استخراج شده است."
|
||||
return f"میانگین آمادگی برداشت بر اساس آخرین سیگنال های قطعی ناحیه ای، {average} است.".strip()
|
||||
|
||||
def _default_operation_note(self, step: dict[str, Any]) -> str:
|
||||
title = step.get("title") or "This operation"
|
||||
status = step.get("status") or "planned"
|
||||
title = step.get("title") or "این عملیات"
|
||||
status = step.get("status") or "برنامه ریزی شده"
|
||||
estimate = step.get("estimated_days")
|
||||
if estimate is None:
|
||||
return f"{title} is currently marked as {status}."
|
||||
return f"{title} is {status} with an estimated timing of {estimate} days.".strip()
|
||||
return f"وضعیت {title} در حال حاضر «{status}» ثبت شده است."
|
||||
return f"{title} با وضعیت «{status}» و زمان بندی تقریبی {estimate} روز ثبت شده است.".strip()
|
||||
|
||||
def _resolve_service(self, *, getter_names: tuple[str, ...]) -> Any:
|
||||
app_config = apps.get_app_config("crop_simulation")
|
||||
|
||||
Reference in New Issue
Block a user