This commit is contained in:
2026-05-11 15:31:10 +03:30
parent c2b6052e5c
commit 948c062b93
5 changed files with 1668 additions and 19 deletions
+176 -1
View File
@@ -5,7 +5,16 @@ import uuid
from django.test import TestCase
from rest_framework.test import APIClient
from location_data.models import BlockSubdivision, SoilLocation
from location_data.models import (
AnalysisGridCell,
AnalysisGridObservation,
BlockSubdivision,
RemoteSensingClusterAssignment,
RemoteSensingClusterBlock,
RemoteSensingRun,
RemoteSensingSubdivisionResult,
SoilLocation,
)
from farm_data.models import PlantCatalogSnapshot, SensorData, SensorParameter
from farm_data.services import (
assign_farm_plants_from_backend_ids,
@@ -181,6 +190,172 @@ class FarmDetailApiTests(TestCase):
SensorParameter.objects.filter(sensor_key="leaf-sensor", code="leaf_wetness").exists()
)
def test_detail_aggregates_satellite_and_sensor_metrics_from_kmeans_sub_blocks_to_main_block(self):
subdivision = BlockSubdivision.objects.create(
soil_location=self.location,
block_code="block-1",
source_boundary=square_boundary_for_center(35.7, 51.4, delta=0.002),
chunk_size_sqm=900,
status="subdivided",
)
run = RemoteSensingRun.objects.create(
soil_location=self.location,
block_subdivision=subdivision,
block_code="block-1",
chunk_size_sqm=900,
temporal_start=date(2026, 4, 1),
temporal_end=date(2026, 4, 30),
status=RemoteSensingRun.STATUS_SUCCESS,
)
result = RemoteSensingSubdivisionResult.objects.create(
soil_location=self.location,
run=run,
block_subdivision=subdivision,
block_code="block-1",
chunk_size_sqm=900,
temporal_start=run.temporal_start,
temporal_end=run.temporal_end,
cluster_count=2,
selected_features=["ndvi", "ndwi", "soil_vv_db"],
metadata={"used_cell_count": 3, "cluster_summaries": []},
)
cell_payloads = [
("cell-1", 0, 0.2, 10.0),
("cell-2", 0, 0.4, 12.0),
("cell-3", 1, 0.9, 20.0),
]
created_cells = []
for index, (cell_code, cluster_label, ndvi, ndwi) in enumerate(cell_payloads):
cell = AnalysisGridCell.objects.create(
soil_location=self.location,
block_subdivision=subdivision,
block_code="block-1",
cell_code=cell_code,
chunk_size_sqm=900,
geometry=square_boundary_for_center(35.7 + (index * 0.0001), 51.4 + (index * 0.0001), delta=0.00005),
centroid_lat=f"{35.7000 + (index * 0.0001):.6f}",
centroid_lon=f"{51.4000 + (index * 0.0001):.6f}",
)
created_cells.append((cell, cluster_label))
AnalysisGridObservation.objects.create(
cell=cell,
run=run,
temporal_start=run.temporal_start,
temporal_end=run.temporal_end,
ndvi=ndvi,
ndwi=ndwi,
soil_vv_db=-8.0 - index,
)
RemoteSensingClusterAssignment.objects.create(
result=result,
cell=cell,
cluster_label=cluster_label,
raw_feature_values={},
scaled_feature_values={},
)
cluster_0 = RemoteSensingClusterBlock.objects.create(
result=result,
soil_location=self.location,
block_subdivision=subdivision,
block_code="block-1",
sub_block_code="cluster-0",
cluster_label=0,
chunk_size_sqm=900,
centroid_lat="35.700050",
centroid_lon="51.400050",
cell_count=2,
cell_codes=["cell-1", "cell-2"],
geometry=square_boundary_for_center(35.70005, 51.40005, delta=0.00008),
metadata={},
)
cluster_1 = RemoteSensingClusterBlock.objects.create(
result=result,
soil_location=self.location,
block_subdivision=subdivision,
block_code="block-1",
sub_block_code="cluster-1",
cluster_label=1,
chunk_size_sqm=900,
centroid_lat="35.700200",
centroid_lon="51.400200",
cell_count=1,
cell_codes=["cell-3"],
geometry=square_boundary_for_center(35.7002, 51.4002, delta=0.00008),
metadata={},
)
self.location.block_layout = {
"input_block_count": 1,
"default_full_farm": True,
"algorithm_status": "completed",
"blocks": [
{
"block_code": "block-1",
"order": 1,
"source": "input",
"boundary": square_boundary_for_center(35.7, 51.4, delta=0.002),
"needs_subdivision": True,
"sub_blocks": [
{
"cluster_uuid": str(cluster_0.uuid),
"sub_block_code": "cluster-0",
"cluster_label": 0,
},
{
"cluster_uuid": str(cluster_1.uuid),
"sub_block_code": "cluster-1",
"cluster_label": 1,
},
],
}
],
}
self.location.save(update_fields=["block_layout", "updated_at"])
self.farm.sensor_payload = {
"sensor-a": {
"cluster_uuid": str(cluster_0.uuid),
"soil_moisture": 10.0,
"nitrogen": 100.0,
},
"sensor-b": {
"cluster_uuid": str(cluster_0.uuid),
"soil_moisture": 20.0,
"nitrogen": 80.0,
},
"sensor-c": {
"cluster_uuid": str(cluster_1.uuid),
"soil_moisture": 30.0,
"nitrogen": 60.0,
},
}
self.farm.save(update_fields=["sensor_payload", "updated_at"])
response = self.client.get(f"/api/farm-data/{self.farm_uuid}/detail/")
self.assertEqual(response.status_code, 200)
payload = response.json()["data"]
block_snapshot = payload["soil"]["satellite_snapshots"][0]
self.assertEqual(block_snapshot["block_code"], "block-1")
self.assertEqual(block_snapshot["sub_block_count"], 2)
self.assertEqual(block_snapshot["satellite_metrics"]["ndvi"], 0.6)
self.assertEqual(block_snapshot["satellite_metrics"]["ndwi"], 15.5)
self.assertEqual(block_snapshot["sensor_metrics"]["soil_moisture"], 22.5)
self.assertEqual(block_snapshot["sensor_metrics"]["nitrogen"], 75.0)
self.assertEqual(block_snapshot["resolved_metrics"]["soil_moisture"], 22.5)
self.assertEqual(block_snapshot["metric_sources"]["ndvi"]["strategy"], "sub_block_mean_average")
self.assertEqual(block_snapshot["metric_sources"]["soil_moisture"]["strategy"], "sub_block_mean_average")
self.assertEqual(len(block_snapshot["satellite_sub_blocks"]), 2)
self.assertEqual(len(block_snapshot["sensor_sub_blocks"]), 2)
block_layout = payload["center_location"]["block_layout"]
self.assertEqual(
block_layout["blocks"][0]["aggregated_metrics"]["resolved_metrics"]["soil_moisture"],
22.5,
)
self.assertEqual(
block_layout["blocks"][0]["aggregated_metrics"]["satellite_metrics"]["ndvi"],
0.6,
)
class FarmDataUpsertApiTests(TestCase):
def setUp(self):