Files
Ai/dashboard_data/services.py
T
2026-03-22 03:08:27 +03:30

173 lines
6.9 KiB
Python

from django.utils import timezone
from .ai_bundle import request_dashboard_ai_bundle
from .card_utils import is_fresh, ttl_for_card
from .cards.anomaly_detection_card import build_anomaly_detection_card
from .cards.economic_overview import build_economic_overview
from .cards.farm_alerts_timeline import build_farm_alerts_timeline
from .cards.farm_alerts_tracker import build_farm_alerts_tracker
from .cards.farm_overview_kpis import build_farm_overview_kpis
from .cards.farm_weather_card import build_farm_weather_card
from .cards.harvest_prediction_card import build_harvest_prediction_card
from .cards.ndvi_health_card import build_ndvi_health_card
from .cards.recommendations_list import build_recommendations_list
from .cards.sensor_comparison_chart import build_sensor_comparison_chart
from .cards.sensor_radar_chart import build_sensor_radar_chart
from .cards.sensor_values_list import build_sensor_values_list
from .cards.soil_moisture_heatmap import build_soil_moisture_heatmap
from .cards.water_need_prediction import build_water_need_prediction
from .cards.yield_prediction_chart import build_yield_prediction_chart
from .context import load_dashboard_context
from .models import DashboardCardSnapshot
CARD_BUILDERS = {
"farmOverviewKpis": build_farm_overview_kpis,
"farmWeatherCard": build_farm_weather_card,
"farmAlertsTracker": build_farm_alerts_tracker,
"sensorValuesList": build_sensor_values_list,
"sensorRadarChart": build_sensor_radar_chart,
"sensorComparisonChart": build_sensor_comparison_chart,
"anomalyDetectionCard": build_anomaly_detection_card,
"farmAlertsTimeline": build_farm_alerts_timeline,
"waterNeedPrediction": build_water_need_prediction,
"harvestPredictionCard": build_harvest_prediction_card,
"yieldPredictionChart": build_yield_prediction_chart,
"soilMoistureHeatmap": build_soil_moisture_heatmap,
"ndviHealthCard": build_ndvi_health_card,
"recommendationsList": build_recommendations_list,
"economicOverview": build_economic_overview,
}
AI_DRIVEN_CARDS = {
"farmAlertsTimeline",
"recommendationsList",
}
def _farm_profile_from_context(context: dict) -> dict:
sensor = context.get("sensor")
location = context.get("location")
plants = context.get("plants", [])
irrigation_methods = context.get("irrigation_methods", [])
return {
"sensor_id": str(getattr(sensor, "uuid_sensor", "")) if sensor else "",
"crop_type": getattr(plants[0], "name", None) if plants else None,
"region": {
"latitude": float(location.latitude) if location else None,
"longitude": float(location.longitude) if location else None,
},
"season": timezone.now().date().isoformat(),
"farming_method": getattr(irrigation_methods[0], "name", None) if irrigation_methods else None,
}
def _sensor_trends_payload(sensor_id: str, context: dict) -> dict:
chart = build_sensor_comparison_chart(sensor_id=sensor_id, context=context, ai_bundle=None)
return {
"current_value": chart.get("currentValue"),
"current_value_quality": chart.get("currentValueQuality"),
"vs_last_week": chart.get("vsLastWeekValue"),
"series": chart.get("series", []),
}
def _alerts_payload(sensor_id: str, context: dict) -> dict:
tracker = build_farm_alerts_tracker(sensor_id=sensor_id, context=context, ai_bundle=None)
return {
"total_alerts": tracker.get("totalAlerts", 0),
"alerts": tracker.get("alerts", []),
"most_critical_issue": tracker.get("mostCriticalIssue"),
"clusters": tracker.get("alertClusters", []),
}
def _anomalies_payload(sensor_id: str, context: dict) -> dict:
anomaly_card = build_anomaly_detection_card(sensor_id=sensor_id, context=context, ai_bundle=None)
return {
"anomalies": anomaly_card.get("anomalies", []),
"interpretation_seed": anomaly_card.get("interpretation"),
}
def _build_ai_payload_request(sensor_id: str, context: dict) -> dict:
structured_context = {
"farm_profile": _farm_profile_from_context(context),
"detected_alerts": _alerts_payload(sensor_id, context),
"anomaly_events": _anomalies_payload(sensor_id, context),
"sensor_trends": _sensor_trends_payload(sensor_id, context),
"timestamps": {
"generated_at": timezone.now().isoformat(),
},
}
system_prompt = (
"You are an agricultural decision-support assistant. "
"Use only the structured data provided. "
"Do not hallucinate sensor values, timestamps, severities, or agronomic events. "
"Generate concise, actionable outputs.\n\n"
"For the timeline, explain what happened, when it happened, and why it matters.\n"
"For recommendations, prioritize by alert severity, time proximity, and potential crop impact.\n"
"Return recommendation objects with: recommendation_title, explanation, suggested_action, urgency_level, related_alert_id (optional)."
)
return {
"sensor_id": sensor_id,
"cards": sorted(AI_DRIVEN_CARDS),
"system_prompt": system_prompt,
"structured_context": structured_context,
}
def build_dashboard_payload(sensor_id: str) -> dict:
context = load_dashboard_context(sensor_id)
if context is None:
return {}
ai_payload_request = _build_ai_payload_request(sensor_id, context)
ai_bundle = request_dashboard_ai_bundle(sensor_id=sensor_id, payload=ai_payload_request)
return {
card_name: builder(sensor_id=sensor_id, context=context, ai_bundle=ai_bundle)
for card_name, builder in CARD_BUILDERS.items()
}
def get_or_build_card(sensor_id: str, card_name: str, context: dict | None = None, ai_bundle: dict | None = None) -> dict:
latest_snapshot = (
DashboardCardSnapshot.objects.filter(sensor_id=sensor_id, card_name=card_name)
.order_by("-generated_at")
.first()
)
if is_fresh(latest_snapshot):
return latest_snapshot.payload
payload = CARD_BUILDERS[card_name](sensor_id=sensor_id, context=context, ai_bundle=ai_bundle)
DashboardCardSnapshot.objects.create(
sensor_id=sensor_id,
card_name=card_name,
payload=payload,
expires_at=timezone.now() + ttl_for_card(card_name),
source="ai" if card_name in AI_DRIVEN_CARDS else "computed",
)
return payload
def build_dashboard_payload_with_cache(sensor_id: str) -> dict:
context = load_dashboard_context(sensor_id)
if context is None:
return {}
ai_payload_request = _build_ai_payload_request(sensor_id, context)
ai_bundle = request_dashboard_ai_bundle(sensor_id=sensor_id, payload=ai_payload_request)
payload = {}
for card_name in CARD_BUILDERS:
payload[card_name] = get_or_build_card(
sensor_id=sensor_id,
card_name=card_name,
context=context,
ai_bundle=ai_bundle,
)
return payload