UPDATE
This commit is contained in:
@@ -34,20 +34,87 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
farm_uuid=self.farm_uuid,
|
||||
center_location=self.location,
|
||||
irrigation_method=self.irrigation_method,
|
||||
sensor_payload={"sensor-7-1": {"soil_moisture": 30.0}},
|
||||
sensor_payload={
|
||||
"sensor-7-1": {
|
||||
"soil_moisture": 30.0,
|
||||
"nitrogen": 18.0,
|
||||
"phosphorus": 12.0,
|
||||
"potassium": 14.0,
|
||||
"soil_ph": 6.9,
|
||||
}
|
||||
},
|
||||
)
|
||||
self.farm.plants.set([self.plant])
|
||||
|
||||
def build_irrigation_optimizer_result(self):
|
||||
return {
|
||||
"engine": "crop_simulation_heuristic",
|
||||
"context_text": "optimizer irrigation context",
|
||||
"recommended_strategy": {
|
||||
"code": "balanced",
|
||||
"label": "آبیاری متعادل",
|
||||
"score": 88.0,
|
||||
"expected_yield_index": 91.0,
|
||||
"total_irrigation_mm": 24.0,
|
||||
"amount_per_event_mm": 8.0,
|
||||
"events": 3,
|
||||
"frequency_per_week": 3,
|
||||
"event_dates": ["2026-04-10"],
|
||||
"timing": "اوایل صبح",
|
||||
"moisture_target_percent": 70,
|
||||
"validity_period": "معتبر برای 3 روز آینده",
|
||||
"reasoning": ["شبیه ساز این سناریو را برتر ارزیابی کرد."],
|
||||
},
|
||||
"alternatives": [
|
||||
{
|
||||
"code": "protective",
|
||||
"label": "آبیاری حمایتی",
|
||||
"score": 80.0,
|
||||
"expected_yield_index": 85.0,
|
||||
"total_irrigation_mm": 28.0,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
def build_fertilization_optimizer_result(self):
|
||||
return {
|
||||
"engine": "crop_simulation_heuristic",
|
||||
"context_text": "optimizer fertilization context",
|
||||
"recommended_strategy": {
|
||||
"code": "balanced",
|
||||
"label": "تغذیه متعادل",
|
||||
"score": 84.0,
|
||||
"expected_yield_index": 88.0,
|
||||
"fertilizer_type": "20-20-20",
|
||||
"amount_kg_per_ha": 65.0,
|
||||
"application_method": "کودآبیاری",
|
||||
"timing": "صبح زود",
|
||||
"validity_period": "معتبر برای 7 روز آینده",
|
||||
"reasoning": ["کسری عناصر با این سناریو بهتر پوشش داده می شود."],
|
||||
},
|
||||
"alternatives": [
|
||||
{
|
||||
"code": "maintenance",
|
||||
"label": "تغذیه نگهدارنده",
|
||||
"score": 72.0,
|
||||
"expected_yield_index": 78.0,
|
||||
"amount_kg_per_ha": 45.0,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
@patch("rag.services.irrigation.calculate_forecast_water_needs", return_value=[])
|
||||
@patch("rag.services.irrigation.resolve_kc", return_value=0.9)
|
||||
@patch("rag.services.irrigation.resolve_crop_profile", return_value={})
|
||||
@patch("rag.services.irrigation.build_irrigation_method_text", return_value="method text")
|
||||
@patch("rag.services.irrigation.build_plant_text", return_value="plant text")
|
||||
@patch("rag.services.irrigation.build_rag_context", return_value="")
|
||||
@patch("rag.services.irrigation._get_optimizer")
|
||||
@patch("rag.services.irrigation.get_chat_client")
|
||||
def test_irrigation_recommendation_uses_farm_relations_when_request_omits_names(
|
||||
self,
|
||||
mock_get_chat_client,
|
||||
mock_get_optimizer,
|
||||
mock_build_rag_context,
|
||||
mock_build_plant_text,
|
||||
mock_build_irrigation_method_text,
|
||||
@@ -55,8 +122,11 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
_mock_resolve_kc,
|
||||
_mock_calculate_forecast_water_needs,
|
||||
):
|
||||
mock_get_optimizer.return_value.optimize_irrigation.return_value = (
|
||||
self.build_irrigation_optimizer_result()
|
||||
)
|
||||
mock_response = Mock()
|
||||
mock_response.choices = [Mock(message=Mock(content='{"plan": {"frequencyPerWeek": 2}}'))]
|
||||
mock_response.choices = [Mock(message=Mock(content='{"sections": [{"type": "list", "title": "custom", "icon": "list", "items": ["مورد سفارشی"]}]}'))]
|
||||
mock_get_chat_client.return_value.chat.completions.create.return_value = mock_response
|
||||
|
||||
result = get_irrigation_recommendation(
|
||||
@@ -64,7 +134,8 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
growth_stage="میوهدهی",
|
||||
)
|
||||
|
||||
self.assertEqual(result["plan"]["frequencyPerWeek"], 2)
|
||||
self.assertEqual(result["sections"][0]["type"], "recommendation")
|
||||
self.assertEqual(result["sections"][0]["amount"], "8.0 میلی متر در هر نوبت (جمع کل 24.0 میلی متر)")
|
||||
mock_build_rag_context.assert_called_once()
|
||||
mock_build_plant_text.assert_called_once_with("گوجهفرنگی", "میوهدهی")
|
||||
mock_build_irrigation_method_text.assert_called_once_with("آبیاری قطرهای")
|
||||
@@ -72,6 +143,7 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
result["selected_irrigation_method"]["name"],
|
||||
"آبیاری قطرهای",
|
||||
)
|
||||
self.assertEqual(result["simulation_optimizer"]["engine"], "crop_simulation_heuristic")
|
||||
|
||||
@patch("rag.services.irrigation.calculate_forecast_water_needs", return_value=[])
|
||||
@patch("rag.services.irrigation.resolve_kc", return_value=0.9)
|
||||
@@ -79,10 +151,12 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
@patch("rag.services.irrigation.build_irrigation_method_text", return_value="method text")
|
||||
@patch("rag.services.irrigation.build_plant_text", return_value="plant text")
|
||||
@patch("rag.services.irrigation.build_rag_context", return_value="")
|
||||
@patch("rag.services.irrigation._get_optimizer")
|
||||
@patch("rag.services.irrigation.get_chat_client")
|
||||
def test_irrigation_recommendation_persists_selected_method_on_farm(
|
||||
self,
|
||||
mock_get_chat_client,
|
||||
mock_get_optimizer,
|
||||
_mock_build_rag_context,
|
||||
_mock_build_plant_text,
|
||||
mock_build_irrigation_method_text,
|
||||
@@ -94,8 +168,11 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
self.farm.irrigation_method = None
|
||||
self.farm.save(update_fields=["irrigation_method", "updated_at"])
|
||||
|
||||
mock_get_optimizer.return_value.optimize_irrigation.return_value = (
|
||||
self.build_irrigation_optimizer_result()
|
||||
)
|
||||
mock_response = Mock()
|
||||
mock_response.choices = [Mock(message=Mock(content='{"plan": {"frequencyPerWeek": 4}}'))]
|
||||
mock_response.choices = [Mock(message=Mock(content='{"sections": [{"type": "recommendation", "title": "test", "icon": "droplet", "content": "custom"}]}'))]
|
||||
mock_get_chat_client.return_value.chat.completions.create.return_value = mock_response
|
||||
|
||||
result = get_irrigation_recommendation(
|
||||
@@ -111,15 +188,20 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
|
||||
@patch("rag.services.fertilization.build_plant_text", return_value="plant text")
|
||||
@patch("rag.services.fertilization.build_rag_context", return_value="")
|
||||
@patch("rag.services.fertilization._get_optimizer")
|
||||
@patch("rag.services.fertilization.get_chat_client")
|
||||
def test_fertilization_recommendation_uses_farm_plant_when_request_omits_name(
|
||||
self,
|
||||
mock_get_chat_client,
|
||||
mock_get_optimizer,
|
||||
_mock_build_rag_context,
|
||||
mock_build_plant_text,
|
||||
):
|
||||
mock_get_optimizer.return_value.optimize_fertilization.return_value = (
|
||||
self.build_fertilization_optimizer_result()
|
||||
)
|
||||
mock_response = Mock()
|
||||
mock_response.choices = [Mock(message=Mock(content='{"plan": {"npkRatio": "20-20-20"}}'))]
|
||||
mock_response.choices = [Mock(message=Mock(content='{"sections": [{"type": "warning", "title": "هشدار", "icon": "alert-triangle", "content": "از اختلاط نامناسب خودداری شود."}]}'))]
|
||||
mock_get_chat_client.return_value.chat.completions.create.return_value = mock_response
|
||||
|
||||
result = get_fertilization_recommendation(
|
||||
@@ -127,5 +209,32 @@ class RecommendationServiceDefaultsTests(TestCase):
|
||||
growth_stage="رویشی",
|
||||
)
|
||||
|
||||
self.assertEqual(result["plan"]["npkRatio"], "20-20-20")
|
||||
self.assertEqual(result["sections"][0]["fertilizerType"], "20-20-20")
|
||||
mock_build_plant_text.assert_called_once_with("گوجهفرنگی", "رویشی")
|
||||
self.assertEqual(result["simulation_optimizer"]["engine"], "crop_simulation_heuristic")
|
||||
|
||||
@patch("rag.services.fertilization.build_plant_text", return_value="plant text")
|
||||
@patch("rag.services.fertilization.build_rag_context", return_value="")
|
||||
@patch("rag.services.fertilization._get_optimizer")
|
||||
@patch("rag.services.fertilization.get_chat_client")
|
||||
def test_fertilization_recommendation_falls_back_to_optimizer_json_when_llm_returns_invalid_payload(
|
||||
self,
|
||||
mock_get_chat_client,
|
||||
mock_get_optimizer,
|
||||
_mock_build_rag_context,
|
||||
_mock_build_plant_text,
|
||||
):
|
||||
mock_get_optimizer.return_value.optimize_fertilization.return_value = (
|
||||
self.build_fertilization_optimizer_result()
|
||||
)
|
||||
mock_response = Mock()
|
||||
mock_response.choices = [Mock(message=Mock(content="not-json"))]
|
||||
mock_get_chat_client.return_value.chat.completions.create.return_value = mock_response
|
||||
|
||||
result = get_fertilization_recommendation(
|
||||
sensor_uuid=str(self.farm_uuid),
|
||||
growth_stage="رویشی",
|
||||
)
|
||||
|
||||
self.assertEqual(result["sections"][0]["applicationMethod"], "کودآبیاری")
|
||||
self.assertEqual(result["sections"][2]["type"], "warning")
|
||||
|
||||
Reference in New Issue
Block a user