UPDATE
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user