from copy import deepcopy from water.services import ( get_farm_weather_card_data, get_water_need_prediction_data, get_water_stress_index_data, ) from crop_health.services import get_crop_health_summary_data from economic_overview.services import get_economic_overview_data from farm_alerts.services import ( get_alert_timeline_data, get_alert_tracker_data, get_recommendations_list_data, ) from fertilization.services import get_fertilization_dashboard_recommendation from irrigation.services import get_irrigation_dashboard_recommendation from pest_detection.services import get_risk_summary_data from device_hub.services import ( get_sensor_7_in_1_summary_data, ) from yield_harvest.services import get_yield_harvest_summary_data from .templates import get_all_card_templates def _update_kpi(card_lookup, card_data): if not card_data: return card_id = card_data.get("id") if not card_id or card_id not in card_lookup: return details = card_data.get("details") clean_data = {key: value for key, value in card_data.items() if key != "details"} card_lookup[card_id].update(clean_data) if details is not None: card_lookup[card_id]["details"] = details def _build_quality_score_card(yield_summary): quality_card = { "id": "quality_score", "title": "امتیاز کیفیت", "subtitle": "برآورد کیفیت محصول", "stats": "۵۹", "avatarColor": "info", "avatarIcon": "tabler-stars", "chipText": "متوسط", "chipColor": "warning", } chart_summary = yield_summary.get("yield_prediction_chart", {}).get("summary", []) if not isinstance(chart_summary, list): return quality_card for item in chart_summary: if not isinstance(item, dict): continue title = str(item.get("title", "")).strip() if "کیفیت" not in title: continue amount = item.get("amount") subtitle = item.get("subtitle") if amount not in (None, ""): quality_card["stats"] = str(amount) if subtitle: quality_card["chipText"] = str(subtitle) quality_card["chipColor"] = "warning" return quality_card return quality_card def _build_days_until_harvest_card(yield_summary): harvest_card = yield_summary.get("harvest_prediction_card", {}) days_until = harvest_card.get("daysUntil") card = { "id": "days_until_harvest", "title": "روز تا برداشت", "subtitle": "زمان باقیمانده تا پنجره برداشت", "stats": "۱۳۵", "avatarColor": "warning", "avatarIcon": "tabler-calendar-event", "chipText": "برنامه ریزی", "chipColor": "success", } if days_until is not None: card["stats"] = str(days_until) return card def _build_overview_kpis(base_cards, crop_health_summary, water_stress_index, avg_soil_moisture, risk_summary, yield_summary): kpis = [crop_health_summary["farmHealthScore"], water_stress_index, avg_soil_moisture, *deepcopy(base_cards["kpis"])] card_lookup = {item["id"]: item for item in kpis} _update_kpi(card_lookup, water_stress_index) _update_kpi(card_lookup, avg_soil_moisture) _update_kpi(card_lookup, risk_summary.get("disease_risk", {})) _update_kpi(card_lookup, risk_summary.get("pest_risk", {})) _update_kpi(card_lookup, yield_summary.get("yield_prediction_card", {})) _update_kpi(card_lookup, _build_quality_score_card(yield_summary)) _update_kpi(card_lookup, _build_days_until_harvest_card(yield_summary)) return {"kpis": kpis} def _build_recommendations_list(farm, fallback_data, harvest_card): recommendations = [] recommendations.extend(get_recommendations_list_data(farm).get("recommendations", [])) recommendations.append(get_irrigation_dashboard_recommendation(farm)) recommendations.append(get_fertilization_dashboard_recommendation(farm)) if harvest_card: recommendations.append( { "title": f"بازه برداشت: {harvest_card.get('optimalWindowStart', '')} تا {harvest_card.get('optimalWindowEnd', '')}", "subtitle": harvest_card.get("description", ""), "avatarIcon": "tabler-calendar-event", "avatarColor": "info", } ) deduped = [] seen_titles = set() for item in recommendations: title = item.get("title") if not title or title in seen_titles: continue seen_titles.add(title) deduped.append(item) if deduped: return {"recommendations": deduped[:4]} return deepcopy(fallback_data) def get_farm_dashboard_cards(farm): cards = get_all_card_templates() water_cards = { "farmWeatherCard": get_farm_weather_card_data(farm), "waterNeedPrediction": get_water_need_prediction_data(farm), "waterStressIndex": get_water_stress_index_data(farm), } crop_health_summary = get_crop_health_summary_data(farm) risk_summary = get_risk_summary_data(farm) yield_summary = get_yield_harvest_summary_data(farm) sensor_summary = get_sensor_7_in_1_summary_data(farm) alert_cards = { "farmAlertsTracker": get_alert_tracker_data(farm), "farmAlertsTimeline": get_alert_timeline_data(farm), } economic_overview = get_economic_overview_data(farm) avg_soil_moisture = sensor_summary["avgSoilMoisture"] cards["farmWeatherCard"] = water_cards["farmWeatherCard"] cards["farmAlertsTracker"] = alert_cards["farmAlertsTracker"] cards["farmAlertsTimeline"] = alert_cards["farmAlertsTimeline"] cards["sensorValuesList"] = sensor_summary["sensorValuesList"] cards["anomalyDetectionCard"] = sensor_summary["anomalyDetectionCard"] cards["waterNeedPrediction"] = water_cards["waterNeedPrediction"] cards["harvestPredictionCard"] = yield_summary["harvest_prediction_card"] cards["yieldPredictionChart"] = yield_summary["yield_prediction_chart"] cards["sensorRadarChart"] = sensor_summary["sensorRadarChart"] cards["sensorComparisonChart"] = sensor_summary["sensorComparisonChart"] cards["soilMoistureHeatmap"] = sensor_summary["soilMoistureHeatmap"] cards["ndviHealthCard"] = crop_health_summary["ndviHealthCard"] cards["economicOverview"] = economic_overview cards["farmOverviewKpis"] = _build_overview_kpis( cards["farmOverviewKpis"], crop_health_summary, water_cards["waterStressIndex"], avg_soil_moisture, risk_summary, yield_summary, ) cards["recommendationsList"] = _build_recommendations_list( farm, cards["recommendationsList"], yield_summary.get("harvest_prediction_card", {}), ) return cards