KSvend Claude Happy commited on
Commit ·
b7f7fb5
1
Parent(s): e9494fc
feat: implement NdviIndicator.submit_batch() for openEO batch jobs
Browse filesGenerated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- app/indicators/ndvi.py +44 -1
- tests/test_indicator_ndvi.py +26 -0
app/indicators/ndvi.py
CHANGED
|
@@ -25,7 +25,7 @@ from app.models import (
|
|
| 25 |
TrendDirection,
|
| 26 |
ConfidenceLevel,
|
| 27 |
)
|
| 28 |
-
from app.openeo_client import get_connection, build_ndvi_graph, build_true_color_graph, _bbox_dict
|
| 29 |
|
| 30 |
logger = logging.getLogger(__name__)
|
| 31 |
|
|
@@ -40,6 +40,49 @@ class NdviIndicator(BaseIndicator):
|
|
| 40 |
estimated_minutes = 8
|
| 41 |
|
| 42 |
_true_color_path: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
async def process(
|
| 45 |
self, aoi: AOI, time_range: TimeRange, season_months: list[int] | None = None
|
|
|
|
| 25 |
TrendDirection,
|
| 26 |
ConfidenceLevel,
|
| 27 |
)
|
| 28 |
+
from app.openeo_client import get_connection, build_ndvi_graph, build_true_color_graph, _bbox_dict, submit_as_batch
|
| 29 |
|
| 30 |
logger = logging.getLogger(__name__)
|
| 31 |
|
|
|
|
| 40 |
estimated_minutes = 8
|
| 41 |
|
| 42 |
_true_color_path: str | None = None
|
| 43 |
+
uses_batch = True
|
| 44 |
+
|
| 45 |
+
async def submit_batch(
|
| 46 |
+
self, aoi: AOI, time_range: TimeRange, season_months: list[int] | None = None
|
| 47 |
+
) -> list:
|
| 48 |
+
conn = get_connection()
|
| 49 |
+
bbox = _bbox_dict(aoi.bbox)
|
| 50 |
+
|
| 51 |
+
current_start = time_range.start.isoformat()
|
| 52 |
+
current_end = time_range.end.isoformat()
|
| 53 |
+
|
| 54 |
+
baseline_start = date(
|
| 55 |
+
time_range.start.year - BASELINE_YEARS,
|
| 56 |
+
time_range.start.month,
|
| 57 |
+
time_range.start.day,
|
| 58 |
+
).isoformat()
|
| 59 |
+
baseline_end = date(
|
| 60 |
+
time_range.start.year,
|
| 61 |
+
time_range.start.month,
|
| 62 |
+
time_range.start.day,
|
| 63 |
+
).isoformat()
|
| 64 |
+
|
| 65 |
+
current_cube = build_ndvi_graph(
|
| 66 |
+
conn=conn, bbox=bbox,
|
| 67 |
+
temporal_extent=[current_start, current_end],
|
| 68 |
+
resolution_m=RESOLUTION_M,
|
| 69 |
+
)
|
| 70 |
+
baseline_cube = build_ndvi_graph(
|
| 71 |
+
conn=conn, bbox=bbox,
|
| 72 |
+
temporal_extent=[baseline_start, baseline_end],
|
| 73 |
+
resolution_m=RESOLUTION_M,
|
| 74 |
+
)
|
| 75 |
+
true_color_cube = build_true_color_graph(
|
| 76 |
+
conn=conn, bbox=bbox,
|
| 77 |
+
temporal_extent=[current_start, current_end],
|
| 78 |
+
resolution_m=RESOLUTION_M,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
return [
|
| 82 |
+
submit_as_batch(conn, current_cube, f"ndvi-current-{aoi.name}"),
|
| 83 |
+
submit_as_batch(conn, baseline_cube, f"ndvi-baseline-{aoi.name}"),
|
| 84 |
+
submit_as_batch(conn, true_color_cube, f"ndvi-truecolor-{aoi.name}"),
|
| 85 |
+
]
|
| 86 |
|
| 87 |
async def process(
|
| 88 |
self, aoi: AOI, time_range: TimeRange, season_months: list[int] | None = None
|
tests/test_indicator_ndvi.py
CHANGED
|
@@ -125,3 +125,29 @@ def test_ndvi_compute_stats():
|
|
| 125 |
assert 0 < stats["overall_mean"] < 1
|
| 126 |
assert "valid_months" in stats
|
| 127 |
assert stats["valid_months"] == 12
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
assert 0 < stats["overall_mean"] < 1
|
| 126 |
assert "valid_months" in stats
|
| 127 |
assert stats["valid_months"] == 12
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
@pytest.mark.asyncio
|
| 131 |
+
async def test_ndvi_submit_batch_creates_three_jobs(test_aoi, test_time_range):
|
| 132 |
+
"""submit_batch() creates current, baseline, and true-color batch jobs."""
|
| 133 |
+
from app.indicators.ndvi import NdviIndicator
|
| 134 |
+
|
| 135 |
+
indicator = NdviIndicator()
|
| 136 |
+
|
| 137 |
+
mock_conn = MagicMock()
|
| 138 |
+
mock_job = MagicMock()
|
| 139 |
+
mock_job.job_id = "j-test"
|
| 140 |
+
mock_conn.create_job.return_value = mock_job
|
| 141 |
+
|
| 142 |
+
with patch("app.indicators.ndvi.get_connection", return_value=mock_conn), \
|
| 143 |
+
patch("app.indicators.ndvi.build_ndvi_graph") as mock_ndvi_graph, \
|
| 144 |
+
patch("app.indicators.ndvi.build_true_color_graph") as mock_tc_graph:
|
| 145 |
+
|
| 146 |
+
mock_ndvi_graph.return_value = MagicMock()
|
| 147 |
+
mock_tc_graph.return_value = MagicMock()
|
| 148 |
+
|
| 149 |
+
jobs = await indicator.submit_batch(test_aoi, test_time_range)
|
| 150 |
+
|
| 151 |
+
assert len(jobs) == 3
|
| 152 |
+
assert mock_ndvi_graph.call_count == 2 # current + baseline
|
| 153 |
+
assert mock_tc_graph.call_count == 1 # true-color
|