UPDATE
This commit is contained in:
@@ -53,7 +53,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
قواعد تکميلي:
|
قواعد تکميلي:
|
||||||
- اگر هشدار مهمي وجود ندارد، آرايه هاي `notifications` يا `timeline` را خالي برگردان.
|
- اگر در کانتکست، notificationهاي قبلي براي بازه اي بين 1 روز تا 7 روز گذشته ارسال شده بود، آن ها را بررسي کن و notification تکراري يا هم معنا دوباره نساز.
|
||||||
|
- فقط وقتي notification جديد بساز که وضعيت واقعا جديد باشد، شدت هشدار تغيير معنادار کرده باشد، يا اقدام پيشنهادي جديد و مهم لازم باشد.
|
||||||
|
- اگر هشدار مهمي وجود ندارد، آرايه هاي `notifications` يا `timeline` را خالي برگردان و در `headline` و `overview` شفاف بگو که فعلا مورد مهمي براي اعلام وجود ندارد.
|
||||||
- `headline` و `overview` هميشه الزامي هستند.
|
- `headline` و `overview` هميشه الزامي هستند.
|
||||||
- عنوان ها کوتاه و عملياتي باشند.
|
- عنوان ها کوتاه و عملياتي باشند.
|
||||||
- `suggested_action` بايد يک اقدام مشخص مزرعه اي باشد، نه توصيه کلي.
|
- `suggested_action` بايد يک اقدام مشخص مزرعه اي باشد، نه توصيه کلي.
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
# راهنمای استفاده از API هشدارهای مزرعه
|
||||||
|
|
||||||
|
این سند نحوه کار با API فعال هشدارهای مزرعه را توضیح میدهد.
|
||||||
|
|
||||||
|
## Endpoint فعال
|
||||||
|
|
||||||
|
- `POST /api/farm-alerts/tracker/`
|
||||||
|
|
||||||
|
نکته:
|
||||||
|
- endpoint `POST /api/farm-alerts/timeline/` حذف شده و دیگر قابل استفاده نیست.
|
||||||
|
|
||||||
|
## کاربرد API
|
||||||
|
|
||||||
|
این API با دریافت `farm_uuid` و یک لیست از `alerts`:
|
||||||
|
|
||||||
|
- وضعیت فعلی هشدارهای مزرعه را تحلیل میکند
|
||||||
|
- context مزرعه را همراه با alertهای ارسالی به RAG میفرستد
|
||||||
|
- فقط notificationهای مهم را تولید میکند
|
||||||
|
- notificationهای تولیدشده را در دیتابیس ذخیره میکند
|
||||||
|
|
||||||
|
## ساختار درخواست
|
||||||
|
|
||||||
|
فیلدهای ورودی:
|
||||||
|
|
||||||
|
- `farm_uuid`: شناسه مزرعه
|
||||||
|
- `alerts`: لیست alertهای ورودی برای تحلیل
|
||||||
|
|
||||||
|
فیلد `farm_uuid` الزامی است.
|
||||||
|
|
||||||
|
## ساختار هر alert
|
||||||
|
|
||||||
|
هر آیتم داخل `alerts` میتواند این فیلدها را داشته باشد:
|
||||||
|
|
||||||
|
- `alert_id`: شناسه هشدار
|
||||||
|
- `level`: سطح هشدار مثل `info` یا `warning` یا `danger`
|
||||||
|
- `title`: عنوان هشدار
|
||||||
|
- `message`: توضیح هشدار
|
||||||
|
- `suggested_action`: اقدام پیشنهادی
|
||||||
|
- `source_metric_type`: نوع شاخص مثل `moisture`
|
||||||
|
- `timestamp`: زمان هشدار با فرمت datetime
|
||||||
|
- `payload`: داده تکمیلی به صورت JSON object
|
||||||
|
|
||||||
|
همه فیلدهای داخل هر alert اختیاری هستند، ولی بهتر است برای تحلیل دقیقتر حداقل `title` یا `message` و در صورت امکان `level` و `source_metric_type` ارسال شوند.
|
||||||
|
|
||||||
|
## نمونه درخواست
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"alerts": [
|
||||||
|
{
|
||||||
|
"alert_id": "soil-moisture-001",
|
||||||
|
"level": "warning",
|
||||||
|
"title": "افت رطوبت خاک",
|
||||||
|
"message": "رطوبت خاک کمتر از حد مطلوب گزارش شده است.",
|
||||||
|
"suggested_action": "آبیاری اصلاحی بررسی شود.",
|
||||||
|
"source_metric_type": "moisture",
|
||||||
|
"timestamp": "2025-02-14T09:30:00Z",
|
||||||
|
"payload": {
|
||||||
|
"window": "3d",
|
||||||
|
"current_value": 38.5,
|
||||||
|
"threshold": 45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alert_id": "fungal-risk-002",
|
||||||
|
"level": "danger",
|
||||||
|
"title": "ریسک قارچی بالا",
|
||||||
|
"message": "شرایط محیطی برای بیماری قارچی شدید شده است.",
|
||||||
|
"suggested_action": "بازدید و اقدام پیشگیرانه فوری انجام شود.",
|
||||||
|
"source_metric_type": "fungal_risk",
|
||||||
|
"timestamp": "2025-02-14T10:00:00Z",
|
||||||
|
"payload": {
|
||||||
|
"humidity": 89,
|
||||||
|
"duration_hours": 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## نمونه درخواست با curl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/farm-alerts/tracker/ \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"alerts": [
|
||||||
|
{
|
||||||
|
"alert_id": "soil-moisture-001",
|
||||||
|
"level": "warning",
|
||||||
|
"title": "افت رطوبت خاک",
|
||||||
|
"message": "رطوبت خاک کمتر از حد مطلوب گزارش شده است.",
|
||||||
|
"suggested_action": "آبیاری اصلاحی بررسی شود.",
|
||||||
|
"source_metric_type": "moisture"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## ساختار پاسخ موفق
|
||||||
|
|
||||||
|
پاسخ HTTP با envelope زیر برمیگردد:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"service_id": "farm_alerts",
|
||||||
|
"tracker": {},
|
||||||
|
"headline": "جمع بندی کوتاه وضعیت هشدارها",
|
||||||
|
"overview": "توضیح کوتاه و اجرایی از مهم ترین وضعیت مزرعه",
|
||||||
|
"status_level": "warning",
|
||||||
|
"notifications": [
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"endpoint": "tracker",
|
||||||
|
"level": "warning",
|
||||||
|
"title": "افت رطوبت خاک",
|
||||||
|
"message": "تنش رطوبتی در مزرعه ادامه دارد.",
|
||||||
|
"suggested_action": "آبیاری جبرانی کوتاه مدت اجرا شود.",
|
||||||
|
"source_alert_id": "soil-moisture-001",
|
||||||
|
"source_metric_type": "moisture",
|
||||||
|
"payload": {},
|
||||||
|
"created_at": "2025-02-14T10:15:00+00:00",
|
||||||
|
"updated_at": "2025-02-14T10:15:00+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"raw_llm_response": "{\"headline\":\"...\"}",
|
||||||
|
"structured_context": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## وضعیتهای خطا
|
||||||
|
|
||||||
|
### خطای ورودی نامعتبر
|
||||||
|
|
||||||
|
اگر `farm_uuid` ارسال نشود:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"farm_uuid": [
|
||||||
|
"farm_uuid الزامی است."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### خطای داخلی
|
||||||
|
|
||||||
|
اگر در مرحله تحلیل RAG یا تولید پاسخ خطایی رخ دهد:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 500,
|
||||||
|
"msg": "خطا در تولید tracker هشدارها: ...",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## نکات رفتاری API
|
||||||
|
|
||||||
|
- اگر `alerts` ارسال نشود، API آن را به صورت آرایه خالی در نظر میگیرد.
|
||||||
|
- notificationهای ساختهشده برای endpoint `tracker` در دیتابیس ذخیره میشوند.
|
||||||
|
- مدل باید notification تکراری نسازد و اگر مورد مهمی وجود نداشته باشد، خروجی notification میتواند خالی باشد.
|
||||||
|
- تحلیل فقط روی endpoint `tracker` فعال است.
|
||||||
|
|
||||||
|
## پیشنهاد برای مصرفکننده API
|
||||||
|
|
||||||
|
- برای هر alert یک `alert_id` پایدار بفرستید تا ردیابی و جلوگیری از تکرار بهتر انجام شود.
|
||||||
|
- برای alertهای حساس، `timestamp` و `source_metric_type` را حتما ارسال کنید.
|
||||||
|
- اگر داده تکمیلی دارید، آن را داخل `payload` بفرستید تا RAG context کاملتر شود.
|
||||||
@@ -3,16 +3,31 @@ from rest_framework import serializers
|
|||||||
from .models import FarmAlertNotification
|
from .models import FarmAlertNotification
|
||||||
|
|
||||||
|
|
||||||
|
class IncomingAlertSerializer(serializers.Serializer):
|
||||||
|
alert_id = serializers.CharField(required=False, allow_blank=True, help_text="شناسه هشدار")
|
||||||
|
level = serializers.CharField(required=False, allow_blank=True, help_text="سطح هشدار")
|
||||||
|
title = serializers.CharField(required=False, allow_blank=True, help_text="عنوان هشدار")
|
||||||
|
message = serializers.CharField(required=False, allow_blank=True, help_text="متن هشدار")
|
||||||
|
suggested_action = serializers.CharField(required=False, allow_blank=True, help_text="اقدام پیشنهادی")
|
||||||
|
source_metric_type = serializers.CharField(required=False, allow_blank=True, help_text="نوع شاخص")
|
||||||
|
timestamp = serializers.DateTimeField(required=False, allow_null=True, help_text="زمان هشدار")
|
||||||
|
payload = serializers.JSONField(required=False, help_text="داده تکمیلی هشدار")
|
||||||
|
|
||||||
|
|
||||||
class FarmAlertsRequestSerializer(serializers.Serializer):
|
class FarmAlertsRequestSerializer(serializers.Serializer):
|
||||||
farm_uuid = serializers.CharField(required=False, help_text="شناسه مزرعه")
|
farm_uuid = serializers.CharField(required=False, help_text="شناسه مزرعه")
|
||||||
sensor_uuid = serializers.CharField(required=False, help_text="نام قدیمی برای farm_uuid")
|
alerts = IncomingAlertSerializer(
|
||||||
query = serializers.CharField(required=False, allow_blank=True, help_text="سوال اختیاری")
|
many=True,
|
||||||
|
required=False,
|
||||||
|
help_text="لیست هشدارهای ورودی که باید در تحلیل RAG در نظر گرفته شوند",
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
farm_uuid = attrs.get("farm_uuid") or attrs.get("sensor_uuid")
|
farm_uuid = attrs.get("farm_uuid")
|
||||||
if not farm_uuid:
|
if not farm_uuid:
|
||||||
raise serializers.ValidationError({"farm_uuid": "farm_uuid الزامی است."})
|
raise serializers.ValidationError({"farm_uuid": "farm_uuid الزامی است."})
|
||||||
attrs["farm_uuid"] = farm_uuid
|
attrs["farm_uuid"] = farm_uuid
|
||||||
|
attrs["alerts"] = attrs.get("alerts") or []
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+41
-7
@@ -29,7 +29,7 @@ KB_NAME = "farm_alerts"
|
|||||||
SERVICE_ID = "farm_alerts"
|
SERVICE_ID = "farm_alerts"
|
||||||
|
|
||||||
TRACKER_PROMPT = (
|
TRACKER_PROMPT = (
|
||||||
"وضعیت هشدارهای مزرعه را فقط بر اساس داده های ساختاریافته، اطلاعات مزرعه، و متون بازیابی شده از پایگاه دانش تحلیل کن. "
|
"وضعیت هشدارهای مزرعه را فقط بر اساس داده های ساختاریافته، اطلاعات مزرعه، alertهاي ورودي، و متون بازیابی شده از پایگاه دانش تحلیل کن. "
|
||||||
"پاسخ فقط JSON معتبر باشد و این کلیدها را داشته باشد: headline, overview, status_level, notifications. "
|
"پاسخ فقط JSON معتبر باشد و این کلیدها را داشته باشد: headline, overview, status_level, notifications. "
|
||||||
"status_level فقط یکی از danger, warning, info باشد. "
|
"status_level فقط یکی از danger, warning, info باشد. "
|
||||||
"notifications باید آرایه ای از آبجکت ها با کلیدهای level, title, message, suggested_action, source_alert_id, source_metric_type باشد. "
|
"notifications باید آرایه ای از آبجکت ها با کلیدهای level, title, message, suggested_action, source_alert_id, source_metric_type باشد. "
|
||||||
@@ -38,7 +38,7 @@ TRACKER_PROMPT = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
TIMELINE_PROMPT = (
|
TIMELINE_PROMPT = (
|
||||||
"بر اساس داده های هشدار مزرعه، یک timeline عملیاتی بساز. "
|
"بر اساس داده های هشدار مزرعه و alertهاي ورودي، یک timeline عملیاتی بساز. "
|
||||||
"پاسخ فقط JSON معتبر باشد و این کلیدها را داشته باشد: headline, overview, timeline, notifications. "
|
"پاسخ فقط JSON معتبر باشد و این کلیدها را داشته باشد: headline, overview, timeline, notifications. "
|
||||||
"timeline باید آرایه ای از آبجکت ها با کلیدهای timestamp, level, title, description, source_alert_id, source_metric_type باشد. "
|
"timeline باید آرایه ای از آبجکت ها با کلیدهای timestamp, level, title, description, source_alert_id, source_metric_type باشد. "
|
||||||
"level فقط danger, warning, info باشد. "
|
"level فقط danger, warning, info باشد. "
|
||||||
@@ -128,7 +128,30 @@ def _farm_profile(context: dict[str, Any], farm_uuid: str) -> dict[str, Any]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _build_structured_context(farm_uuid: str) -> tuple[dict[str, Any], dict[str, Any]]:
|
def _normalize_incoming_alerts(alerts: list[dict[str, Any]] | None) -> list[dict[str, Any]]:
|
||||||
|
normalized: list[dict[str, Any]] = []
|
||||||
|
for item in alerts or []:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
continue
|
||||||
|
normalized.append(
|
||||||
|
{
|
||||||
|
"alert_id": item.get("alert_id") or None,
|
||||||
|
"level": item.get("level") or None,
|
||||||
|
"title": item.get("title") or None,
|
||||||
|
"message": item.get("message") or None,
|
||||||
|
"suggested_action": item.get("suggested_action") or None,
|
||||||
|
"source_metric_type": item.get("source_metric_type") or None,
|
||||||
|
"timestamp": item.get("timestamp"),
|
||||||
|
"payload": item.get("payload") if isinstance(item.get("payload"), dict) else {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
|
def _build_structured_context(
|
||||||
|
farm_uuid: str,
|
||||||
|
incoming_alerts: list[dict[str, Any]] | None = None,
|
||||||
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||||
context = load_farm_context(farm_uuid)
|
context = load_farm_context(farm_uuid)
|
||||||
if context is None:
|
if context is None:
|
||||||
raise ValueError("farm_uuid نامعتبر است یا اطلاعات هشدار مزرعه پیدا نشد.")
|
raise ValueError("farm_uuid نامعتبر است یا اطلاعات هشدار مزرعه پیدا نشد.")
|
||||||
@@ -138,6 +161,7 @@ def _build_structured_context(farm_uuid: str) -> tuple[dict[str, Any], dict[str,
|
|||||||
"farm_profile": _farm_profile(context, farm_uuid),
|
"farm_profile": _farm_profile(context, farm_uuid),
|
||||||
"tracker": tracker,
|
"tracker": tracker,
|
||||||
"forecasts": _forecast_summary(context),
|
"forecasts": _forecast_summary(context),
|
||||||
|
"incoming_alerts": _normalize_incoming_alerts(incoming_alerts),
|
||||||
}
|
}
|
||||||
return context, structured
|
return context, structured
|
||||||
|
|
||||||
@@ -336,8 +360,13 @@ def _llm_response(
|
|||||||
raise RuntimeError(f"Farm alerts generation failed for farm {farm_uuid}.") from exc
|
raise RuntimeError(f"Farm alerts generation failed for farm {farm_uuid}.") from exc
|
||||||
|
|
||||||
|
|
||||||
def get_farm_alerts_tracker(*, farm_uuid: str, query: str | None = None) -> dict[str, Any]:
|
def get_farm_alerts_tracker(
|
||||||
_, structured_context = _build_structured_context(farm_uuid)
|
*,
|
||||||
|
farm_uuid: str,
|
||||||
|
query: str | None = None,
|
||||||
|
alerts: list[dict[str, Any]] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
_, structured_context = _build_structured_context(farm_uuid, incoming_alerts=alerts)
|
||||||
tracker = structured_context["tracker"]
|
tracker = structured_context["tracker"]
|
||||||
user_query = query or "وضعیت فعلی هشدارهای مزرعه را ارزیابی کن و اگر لازم است notification بساز."
|
user_query = query or "وضعیت فعلی هشدارهای مزرعه را ارزیابی کن و اگر لازم است notification بساز."
|
||||||
llm_result, raw_response, tone_file = _llm_response(
|
llm_result, raw_response, tone_file = _llm_response(
|
||||||
@@ -368,8 +397,13 @@ def get_farm_alerts_tracker(*, farm_uuid: str, query: str | None = None) -> dict
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_farm_alerts_timeline(*, farm_uuid: str, query: str | None = None) -> dict[str, Any]:
|
def get_farm_alerts_timeline(
|
||||||
_, structured_context = _build_structured_context(farm_uuid)
|
*,
|
||||||
|
farm_uuid: str,
|
||||||
|
query: str | None = None,
|
||||||
|
alerts: list[dict[str, Any]] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
_, structured_context = _build_structured_context(farm_uuid, incoming_alerts=alerts)
|
||||||
tracker = structured_context["tracker"]
|
tracker = structured_context["tracker"]
|
||||||
user_query = query or "برای هشدارهای مزرعه یک timeline عملیاتی بساز و اگر لازم است notification ثبت کن."
|
user_query = query or "برای هشدارهای مزرعه یک timeline عملیاتی بساز و اگر لازم است notification ثبت کن."
|
||||||
llm_result, raw_response, tone_file = _llm_response(
|
llm_result, raw_response, tone_file = _llm_response(
|
||||||
|
|||||||
+1
-2
@@ -1,9 +1,8 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import FarmAlertsTimelineView, FarmAlertsTrackerView
|
from .views import FarmAlertsTrackerView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("tracker/", FarmAlertsTrackerView.as_view(), name="farm-alerts-tracker"),
|
path("tracker/", FarmAlertsTrackerView.as_view(), name="farm-alerts-tracker"),
|
||||||
path("timeline/", FarmAlertsTimelineView.as_view(), name="farm-alerts-timeline"),
|
|
||||||
]
|
]
|
||||||
|
|||||||
+14
-51
@@ -6,7 +6,7 @@ from rest_framework.views import APIView
|
|||||||
from config.openapi import build_envelope_serializer, build_response
|
from config.openapi import build_envelope_serializer, build_response
|
||||||
|
|
||||||
from .serializers import FarmAlertsRequestSerializer
|
from .serializers import FarmAlertsRequestSerializer
|
||||||
from .services import get_farm_alerts_timeline, get_farm_alerts_tracker
|
from .services import get_farm_alerts_tracker
|
||||||
|
|
||||||
|
|
||||||
FarmAlertsValidationErrorSerializer = build_envelope_serializer(
|
FarmAlertsValidationErrorSerializer = build_envelope_serializer(
|
||||||
@@ -18,10 +18,6 @@ FarmAlertsTrackerResponseSerializer = build_envelope_serializer(
|
|||||||
"FarmAlertsTrackerResponseSerializer",
|
"FarmAlertsTrackerResponseSerializer",
|
||||||
data_schema=None,
|
data_schema=None,
|
||||||
)
|
)
|
||||||
FarmAlertsTimelineResponseSerializer = build_envelope_serializer(
|
|
||||||
"FarmAlertsTimelineResponseSerializer",
|
|
||||||
data_schema=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FarmAlertsTrackerView(APIView):
|
class FarmAlertsTrackerView(APIView):
|
||||||
@@ -30,7 +26,8 @@ class FarmAlertsTrackerView(APIView):
|
|||||||
summary="ارزیابی tracker هشدارهای مزرعه",
|
summary="ارزیابی tracker هشدارهای مزرعه",
|
||||||
description=(
|
description=(
|
||||||
"با دریافت farm_uuid، هشدارهای مزرعه را تحلیل می کند، "
|
"با دریافت farm_uuid، هشدارهای مزرعه را تحلیل می کند، "
|
||||||
"کانتکست مزرعه را به RAG می فرستد، و notificationهای سطح خطر/هشدار/اطلاع رسانی را در دیتابیس ذخیره می کند."
|
"کانتکست مزرعه و لیست alertهای ورودی را به RAG می فرستد، "
|
||||||
|
"و notificationهای سطح خطر/هشدار/اطلاع رسانی را در دیتابیس ذخیره می کند."
|
||||||
),
|
),
|
||||||
request=FarmAlertsRequestSerializer,
|
request=FarmAlertsRequestSerializer,
|
||||||
responses={
|
responses={
|
||||||
@@ -43,6 +40,16 @@ class FarmAlertsTrackerView(APIView):
|
|||||||
"نمونه درخواست tracker",
|
"نمونه درخواست tracker",
|
||||||
value={
|
value={
|
||||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"alerts": [
|
||||||
|
{
|
||||||
|
"alert_id": "soil-moisture-001",
|
||||||
|
"level": "warning",
|
||||||
|
"title": "افت رطوبت خاک",
|
||||||
|
"message": "رطوبت خاک کمتر از حد مطلوب گزارش شده است.",
|
||||||
|
"suggested_action": "آبیاری اصلاحی بررسی شود.",
|
||||||
|
"source_metric_type": "moisture",
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
request_only=True,
|
request_only=True,
|
||||||
),
|
),
|
||||||
@@ -60,6 +67,7 @@ class FarmAlertsTrackerView(APIView):
|
|||||||
result = get_farm_alerts_tracker(
|
result = get_farm_alerts_tracker(
|
||||||
farm_uuid=validated["farm_uuid"],
|
farm_uuid=validated["farm_uuid"],
|
||||||
query=validated.get("query"),
|
query=validated.get("query"),
|
||||||
|
alerts=validated.get("alerts"),
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return Response(
|
return Response(
|
||||||
@@ -67,48 +75,3 @@ class FarmAlertsTrackerView(APIView):
|
|||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
)
|
)
|
||||||
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)
|
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class FarmAlertsTimelineView(APIView):
|
|
||||||
@extend_schema(
|
|
||||||
tags=["Farm Alerts"],
|
|
||||||
summary="دریافت timeline هشدارهای مزرعه",
|
|
||||||
description=(
|
|
||||||
"با دریافت farm_uuid، timeline هشدارهای مزرعه را با کمک RAG می سازد "
|
|
||||||
"و notificationهای استخراج شده را در دیتابیس ذخیره می کند."
|
|
||||||
),
|
|
||||||
request=FarmAlertsRequestSerializer,
|
|
||||||
responses={
|
|
||||||
200: build_response(FarmAlertsTimelineResponseSerializer, "خروجی timeline هشدارهای مزرعه."),
|
|
||||||
400: build_response(FarmAlertsValidationErrorSerializer, "پارامتر ورودی نامعتبر است."),
|
|
||||||
500: build_response(FarmAlertsValidationErrorSerializer, "خطا در تولید timeline هشدارها."),
|
|
||||||
},
|
|
||||||
examples=[
|
|
||||||
OpenApiExample(
|
|
||||||
"نمونه درخواست timeline",
|
|
||||||
value={
|
|
||||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
|
||||||
},
|
|
||||||
request_only=True,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def post(self, request):
|
|
||||||
serializer = FarmAlertsRequestSerializer(data=request.data)
|
|
||||||
if not serializer.is_valid():
|
|
||||||
return Response(
|
|
||||||
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
|
||||||
)
|
|
||||||
validated = serializer.validated_data
|
|
||||||
try:
|
|
||||||
result = get_farm_alerts_timeline(
|
|
||||||
farm_uuid=validated["farm_uuid"],
|
|
||||||
query=validated.get("query"),
|
|
||||||
)
|
|
||||||
except Exception as exc:
|
|
||||||
return Response(
|
|
||||||
{"code": 500, "msg": f"خطا در تولید timeline هشدارها: {exc}", "data": None},
|
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
)
|
|
||||||
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user