Add demo raster fixture workflow
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-23 00:12:20 +02:00
parent 4ef2d82cfa
commit ca730edc1c
13 changed files with 208 additions and 14 deletions
+8
View File
@@ -7,6 +7,14 @@
# Changelog # Changelog
## Sprint 97 demo raster fixture workflow (2026-06-23)
- Added a deterministic local GeoTIFF raster fixture to the offline demo workflow so raster controls and AI Lab dataset prerequisites have usable V1 context.
- Returned `raster_dataset_id` from the canonical demo workflow response and wired the frontend demo loader to select it for Detection and Segmentation Labs.
- Kept the candidate vector dataset as the default Data/Map/Export context after demo load.
- Hardened workbench default/interactions smoke scripts to require the candidate vector, reference vector and raster fixture datasets as `3/3 ready`.
- No external provider fetching, real AI inference, migrations or API behavior outside the demo response contract changed.
## Sprint 96 useful default context (2026-06-22) ## Sprint 96 useful default context (2026-06-22)
- Auto-open the first ready vector dataset after project data loads so Data, Map and Exports start with usable context. - Auto-open the first ready vector dataset after project data loads so Data, Map and Exports start with usable context.
+1
View File
@@ -10,6 +10,7 @@ class DemoWorkflowResponse(BaseModel):
area_id: UUID area_id: UUID
reference_dataset_id: UUID reference_dataset_id: UUID
candidate_dataset_id: UUID candidate_dataset_id: UUID
raster_dataset_id: UUID | None = None
quality_check_id: UUID quality_check_id: UUID
metric_count: int metric_count: int
status: str status: str
+104 -1
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import json import json
import os import os
import importlib
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from uuid import UUID, uuid4 from uuid import UUID, uuid4
@@ -14,6 +15,7 @@ from app.schemas.demo import DemoWorkflowResponse
from app.services.geojson_service import parse_geojson_payload from app.services.geojson_service import parse_geojson_payload
from app.services.qa_service import QaService from app.services.qa_service import QaService
from app.services.quality_service import QualityService from app.services.quality_service import QualityService
from app.services.raster_service import extract_raster_metadata
from app.services.storage_service import StorageService from app.services.storage_service import StorageService
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from app.utils.geometry import area_m2, geometry_bbox_polygon, normalize_to_multipolygon from app.utils.geometry import area_m2, geometry_bbox_polygon, normalize_to_multipolygon
@@ -24,6 +26,7 @@ class DemoWorkflowService:
AREA_NAME = "Demo AOI - Geel buildings" AREA_NAME = "Demo AOI - Geel buildings"
REFERENCE_FILENAME = "demo_reference_buildings.geojson" REFERENCE_FILENAME = "demo_reference_buildings.geojson"
CANDIDATE_FILENAME = "demo_predicted_buildings.geojson" CANDIDATE_FILENAME = "demo_predicted_buildings.geojson"
RASTER_FILENAME = "demo_context_raster.tif"
EXPECTED_METRICS_FILENAME = "expected_qa_metrics.json" EXPECTED_METRICS_FILENAME = "expected_qa_metrics.json"
@staticmethod @staticmethod
@@ -101,15 +104,28 @@ class DemoWorkflowService:
.filter(Dataset.project_id == project_id) .filter(Dataset.project_id == project_id)
.filter(Dataset.dataset_role == "source") .filter(Dataset.dataset_role == "source")
.filter(Dataset.source_name == "fixture") .filter(Dataset.source_name == "fixture")
.filter(Dataset.dataset_type == "vector")
.first() .first()
) )
raster = DemoWorkflowService._find_demo_raster_dataset(db, project_id)
quality_check = ( quality_check = (
db.query(QualityCheck) db.query(QualityCheck)
.filter(QualityCheck.project_id == project_id) .filter(QualityCheck.project_id == project_id)
.filter(QualityCheck.check_type == "demo_candidate_vs_reference") .filter(QualityCheck.check_type == "demo_candidate_vs_reference")
.first() .first()
) )
return bool(area and reference and candidate and quality_check) return bool(area and reference and candidate and raster and quality_check)
@staticmethod
def _find_demo_raster_dataset(db: Session, project_id: UUID) -> Dataset | None:
return (
db.query(Dataset)
.filter(Dataset.project_id == project_id)
.filter(Dataset.dataset_type == "raster")
.filter(Dataset.source_name == "fixture")
.filter(Dataset.name == DemoWorkflowService.RASTER_FILENAME)
.first()
)
@staticmethod @staticmethod
def _create_area(db: Session, project_id: UUID) -> Area: def _create_area(db: Session, project_id: UUID) -> Area:
@@ -207,6 +223,84 @@ class DemoWorkflowService:
) )
return dataset return dataset
@staticmethod
def _create_demo_raster_bytes() -> bytes:
numpy = importlib.import_module("numpy")
rasterio = importlib.import_module("rasterio")
rasterio_io = importlib.import_module("rasterio.io")
rasterio_transform = importlib.import_module("rasterio.transform")
width = 64
height = 48
data = numpy.linspace(20, 220, num=width * height, dtype=numpy.uint8).reshape((height, width))
transform = rasterio_transform.from_bounds(4.9895, 51.1595, 4.9930, 51.1615, width, height)
with rasterio_io.MemoryFile() as memfile:
with memfile.open(
driver="GTiff",
width=width,
height=height,
count=1,
dtype="uint8",
crs="EPSG:4326",
transform=transform,
nodata=0,
) as dataset:
dataset.write(data, 1)
return memfile.read()
@staticmethod
def _create_raster_dataset(db: Session, *, project_id: UUID, area_id: UUID) -> Dataset:
dataset_id = uuid4()
raw = DemoWorkflowService._create_demo_raster_bytes()
storage_info = StorageService.persist_dataset_file(
project_id=str(project_id),
dataset_id=str(dataset_id),
dataset_type="raster",
original_filename=DemoWorkflowService.RASTER_FILENAME,
content=raw,
content_type="image/tiff",
)
metadata = extract_raster_metadata(storage_info["storage_path"])
bounds = metadata.get("bounds")
bounds_json = None
if isinstance(bounds, list) and len(bounds) == 4:
bounds_json = {"minx": bounds[0], "miny": bounds[1], "maxx": bounds[2], "maxy": bounds[3]}
dataset = Dataset(
id=dataset_id,
project_id=project_id,
area_id=area_id,
name=DemoWorkflowService.RASTER_FILENAME,
dataset_type="raster",
source="fixture",
dataset_role="source",
source_name="fixture",
reference_layer_name=None,
source_metadata={
"fixture": True,
"fixture_name": DemoWorkflowService.RASTER_FILENAME,
"usage": "offline demo raster workflow only",
},
provenance_metadata={
"created_by": "demo_workflow",
"source_path": "generated:demo_context_raster",
},
imported_at=datetime.now(timezone.utc),
storage_path=storage_info["storage_path"],
original_filename=storage_info["original_filename"],
stored_filename=storage_info["stored_filename"],
content_type=storage_info["content_type"],
size_bytes=storage_info["size_bytes"],
checksum_sha256=storage_info["checksum_sha256"],
crs=metadata.get("crs"),
bounds_json=bounds_json,
metadata_json=metadata,
status="ready",
)
db.add(dataset)
db.commit()
db.refresh(dataset)
return dataset
@staticmethod @staticmethod
def _persist_qa( def _persist_qa(
db: Session, db: Session,
@@ -306,8 +400,10 @@ class DemoWorkflowService:
.filter(Dataset.project_id == existing.id) .filter(Dataset.project_id == existing.id)
.filter(Dataset.dataset_role == "source") .filter(Dataset.dataset_role == "source")
.filter(Dataset.source_name == "fixture") .filter(Dataset.source_name == "fixture")
.filter(Dataset.dataset_type == "vector")
.first() .first()
) )
raster = DemoWorkflowService._find_demo_raster_dataset(db, existing.id)
quality_check = ( quality_check = (
db.query(QualityCheck) db.query(QualityCheck)
.filter(QualityCheck.project_id == existing.id) .filter(QualityCheck.project_id == existing.id)
@@ -316,6 +412,8 @@ class DemoWorkflowService:
.first() .first()
) )
if area and reference and candidate and quality_check: if area and reference and candidate and quality_check:
if not raster:
raster = DemoWorkflowService._create_raster_dataset(db=db, project_id=existing.id, area_id=area.id)
if not DemoWorkflowService._quality_check_matches_expected(db, quality_check): if not DemoWorkflowService._quality_check_matches_expected(db, quality_check):
area = DemoWorkflowService._sync_demo_area(db, area) area = DemoWorkflowService._sync_demo_area(db, area)
quality_check = DemoWorkflowService._persist_qa( quality_check = DemoWorkflowService._persist_qa(
@@ -330,6 +428,7 @@ class DemoWorkflowService:
area_id=area.id, area_id=area.id,
reference_dataset_id=reference.id, reference_dataset_id=reference.id,
candidate_dataset_id=candidate.id, candidate_dataset_id=candidate.id,
raster_dataset_id=raster.id,
quality_check_id=quality_check.id, quality_check_id=quality_check.id,
metric_count=db.query(Metric).filter(Metric.quality_check_id == quality_check.id).count(), metric_count=db.query(Metric).filter(Metric.quality_check_id == quality_check.id).count(),
status="ready", status="ready",
@@ -352,6 +451,7 @@ class DemoWorkflowService:
area = None area = None
reference = None reference = None
candidate = None candidate = None
raster = None
quality_check = None quality_check = None
created = True created = True
@@ -381,6 +481,8 @@ class DemoWorkflowService:
source_name="fixture", source_name="fixture",
reference_layer_name=None, reference_layer_name=None,
) )
if not raster:
raster = DemoWorkflowService._create_raster_dataset(db=db, project_id=project.id, area_id=area.id)
if not quality_check: if not quality_check:
quality_check = DemoWorkflowService._persist_qa( quality_check = DemoWorkflowService._persist_qa(
db=db, db=db,
@@ -395,6 +497,7 @@ class DemoWorkflowService:
area_id=area.id, area_id=area.id,
reference_dataset_id=reference.id, reference_dataset_id=reference.id,
candidate_dataset_id=candidate.id, candidate_dataset_id=candidate.id,
raster_dataset_id=raster.id,
quality_check_id=quality_check.id, quality_check_id=quality_check.id,
metric_count=6, metric_count=6,
status="ready", status="ready",
+1 -1
View File
@@ -121,7 +121,7 @@ def test_workbench_default_state_script_verifies_populated_demo_start_state() ->
assert "/quality-checks" in content assert "/quality-checks" in content
assert "data.items" in content assert "data.items" in content
assert "Demo AOI - Geel buildings" in content assert "Demo AOI - Geel buildings" in content
assert "2/2 ready" in content assert "3/3 ready" in content
def test_pass_end_check_excludes_vendor_and_build_outputs() -> None: def test_pass_end_check_excludes_vendor_and_build_outputs() -> None:
@@ -0,0 +1,30 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def test_demo_workflow_contract_includes_raster_fixture_dataset() -> None:
schema = (ROOT / "backend" / "app" / "schemas" / "demo.py").read_text(encoding="utf-8")
service = (ROOT / "backend" / "app" / "services" / "demo_workflow_service.py").read_text(encoding="utf-8")
assert "raster_dataset_id: UUID | None = None" in schema
assert 'RASTER_FILENAME = "demo_context_raster.tif"' in service
assert "_create_demo_raster_bytes" in service
assert "_create_raster_dataset" in service
assert "dataset_type=\"raster\"" in service
assert "source_name=\"fixture\"" in service
assert "raster_dataset_id=raster.id" in service
def test_demo_workflow_frontend_uses_raster_fixture_for_ai_labs() -> None:
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
hook = (ROOT / "frontend" / "src" / "hooks" / "useDemoWorkflow.ts").read_text(encoding="utf-8")
assert "raster_dataset_id?: string | null" in types
assert "setSelectedDetectionDatasetId" in app
assert "setSelectedSegmentationDatasetId" in app
assert "setSelectedDetectionDatasetId(result.raster_dataset_id ?? '')" in hook
assert "setSelectedSegmentationDatasetId(result.raster_dataset_id ?? '')" in hook
assert "rasterDataset" in hook
+26
View File
@@ -1,3 +1,29 @@
## Sprint 97 demo raster fixture workflow (2026-06-23)
Changed:
- Added `raster_dataset_id` to the demo workflow response contract.
- Extended `DemoWorkflowService` with a deterministic in-memory `demo_context_raster.tif` GeoTIFF fixture persisted through `StorageService` and the existing `datasets` table as a ready `raster`/`fixture` source dataset.
- Updated the frontend demo workflow hook so Detection and Segmentation Labs receive the seeded raster dataset while the candidate vector remains selected for Data, Map and Export review.
- Updated default-state and interaction smokes to require candidate vector, reference vector and raster fixture datasets as `3/3 ready`.
- Updated `scripts/README.md`, `frontend/README.md` and `CHANGELOG.md`.
Validation:
- RED: `python -m pytest backend\tests\test_sprint97_demo_raster_fixture.py -q` failed before implementation because the demo schema/service and frontend hook did not expose or select a raster fixture.
- `python -m pytest backend\tests\test_sprint97_demo_raster_fixture.py -q` passed: 2 tests.
- `python -m compileall backend/app` passed.
- `python -m pytest backend\tests\test_sprint97_demo_raster_fixture.py backend\tests\test_sprint15_demo_workflow.py backend\tests\test_sprint21_demo_workflow_smoke.py backend\tests\test_readiness_gate.py -q` passed: 21 tests.
- `bash -n scripts/verify_workbench_default_state.sh` passed.
- `bash -n scripts/verify_workbench_interactions.sh` passed.
- `cd frontend && npm run typecheck` passed.
- `python -m pytest backend\tests\test_sprint96_useful_default_context.py backend\tests\test_sprint39_frontend_orchestration_hooks.py -q` passed: 11 tests.
Limitations:
- The raster fixture is a tiny generated local GeoTIFF for V1 workflow validation only. It does not represent external imagery and does not enable real AI inference.
- No migrations, provider fetching, real YOLO/SAM behavior or product scope beyond the existing offline demo workflow changed.
Next recommended pass:
- Run full readiness, rebuild/redeploy, then verify that the live browser-facing workbench exposes three ready demo datasets and preselects the raster fixture for AI Labs.
## Sprint 48 Backend API contract audit (2026-06-17) ## Sprint 48 Backend API contract audit (2026-06-17)
Changed: Changed:
+1
View File
@@ -260,6 +260,7 @@ AI Lab run controls explicitly explain when no raster dataset is available, inst
- The Data workspace surfaces selected project, AOI and dataset context before creation/upload forms, then separates form and catalog/list regions for faster scanning. - The Data workspace surfaces selected project, AOI and dataset context before creation/upload forms, then separates form and catalog/list regions for faster scanning.
- The Export Center includes a handoff readiness summary, grouped artifact actions and provenance-rich export cards so report/GeoJSON handoff stays understandable in long-running demo projects. - The Export Center includes a handoff readiness summary, grouped artifact actions and provenance-rich export cards so report/GeoJSON handoff stays understandable in long-running demo projects.
- The Export Center highlights the latest project report, project metadata, dataset GeoJSON, detection GeoJSON and segmentation GeoJSON artifacts with direct preview/download actions. - The Export Center highlights the latest project report, project metadata, dataset GeoJSON, detection GeoJSON and segmentation GeoJSON artifacts with direct preview/download actions.
- The offline demo workflow now selects the seeded raster fixture for Detection and Segmentation Lab prerequisites while keeping the candidate vector dataset open for Data/Map/Export review.
## Workbench shell refactor ## Workbench shell refactor
+2
View File
@@ -363,7 +363,9 @@ function App(): JSX.Element {
setQaCandidateDatasetId, setQaCandidateDatasetId,
setQaReferenceDatasetId, setQaReferenceDatasetId,
setQaAreaId, setQaAreaId,
setSelectedDetectionDatasetId,
setDetectionReferenceDatasetId, setDetectionReferenceDatasetId,
setSelectedSegmentationDatasetId,
setSegmentationReferenceDatasetId, setSegmentationReferenceDatasetId,
setErrorMessage, setErrorMessage,
}) })
+9
View File
@@ -17,7 +17,9 @@ interface DemoWorkflowOptions {
setQaCandidateDatasetId: (datasetId: string) => void setQaCandidateDatasetId: (datasetId: string) => void
setQaReferenceDatasetId: (datasetId: string) => void setQaReferenceDatasetId: (datasetId: string) => void
setQaAreaId: (areaId: string) => void setQaAreaId: (areaId: string) => void
setSelectedDetectionDatasetId: (datasetId: string) => void
setDetectionReferenceDatasetId: (datasetId: string) => void setDetectionReferenceDatasetId: (datasetId: string) => void
setSelectedSegmentationDatasetId: (datasetId: string) => void
setSegmentationReferenceDatasetId: (datasetId: string) => void setSegmentationReferenceDatasetId: (datasetId: string) => void
setErrorMessage: (message: string | null) => void setErrorMessage: (message: string | null) => void
} }
@@ -36,7 +38,9 @@ export function useDemoWorkflow({
setQaCandidateDatasetId, setQaCandidateDatasetId,
setQaReferenceDatasetId, setQaReferenceDatasetId,
setQaAreaId, setQaAreaId,
setSelectedDetectionDatasetId,
setDetectionReferenceDatasetId, setDetectionReferenceDatasetId,
setSelectedSegmentationDatasetId,
setSegmentationReferenceDatasetId, setSegmentationReferenceDatasetId,
setErrorMessage, setErrorMessage,
}: DemoWorkflowOptions) { }: DemoWorkflowOptions) {
@@ -55,7 +59,9 @@ export function useDemoWorkflow({
setQaCandidateDatasetId(result.candidate_dataset_id) setQaCandidateDatasetId(result.candidate_dataset_id)
setQaReferenceDatasetId(result.reference_dataset_id) setQaReferenceDatasetId(result.reference_dataset_id)
setQaAreaId(result.area_id) setQaAreaId(result.area_id)
setSelectedDetectionDatasetId(result.raster_dataset_id ?? '')
setDetectionReferenceDatasetId(result.reference_dataset_id) setDetectionReferenceDatasetId(result.reference_dataset_id)
setSelectedSegmentationDatasetId(result.raster_dataset_id ?? '')
setSegmentationReferenceDatasetId(result.reference_dataset_id) setSegmentationReferenceDatasetId(result.reference_dataset_id)
setDemoWorkflowMessage(result.message) setDemoWorkflowMessage(result.message)
await loadProjects(result.project_id) await loadProjects(result.project_id)
@@ -67,8 +73,11 @@ export function useDemoWorkflow({
loadExports(result.project_id), loadExports(result.project_id),
]) ])
const candidateDataset = projectData?.datasets.find((dataset) => dataset.id === result.candidate_dataset_id) const candidateDataset = projectData?.datasets.find((dataset) => dataset.id === result.candidate_dataset_id)
const rasterDataset = projectData?.datasets.find((dataset) => dataset.id === result.raster_dataset_id)
if (candidateDataset) { if (candidateDataset) {
await loadDatasetDetails(result.project_id, candidateDataset) await loadDatasetDetails(result.project_id, candidateDataset)
} else if (rasterDataset) {
await loadDatasetDetails(result.project_id, rasterDataset)
} }
} catch (error) { } catch (error) {
setErrorMessage(formatError(error, 'Failed to load demo workflow')) setErrorMessage(formatError(error, 'Failed to load demo workflow'))
+1
View File
@@ -40,6 +40,7 @@ export interface DemoWorkflowResponse {
area_id: string area_id: string
reference_dataset_id: string reference_dataset_id: string
candidate_dataset_id: string candidate_dataset_id: string
raster_dataset_id?: string | null
quality_check_id: string quality_check_id: string
metric_count: number metric_count: number
status: string status: string
+4 -3
View File
@@ -46,9 +46,10 @@ bash scripts/verify_workbench_default_state.sh http://192.168.10.150:1202
This smoke is dependency-light and intentionally idempotent: it seeds the This smoke is dependency-light and intentionally idempotent: it seeds the
offline demo workflow, then verifies that `GeoIntel Demo - Building QA` exposes offline demo workflow, then verifies that `GeoIntel Demo - Building QA` exposes
the `Demo AOI - Geel buildings` map geometry, `2/2 ready` demo datasets and a the `Demo AOI - Geel buildings` map geometry, `3/3 ready` demo datasets
persisted QA/QC result through canonical `data.items` envelopes. Pair it with a (candidate vector, reference vector and raster fixture) and a persisted QA/QC
Codex/browser screenshot pass when checking visual layout or overflow. result through canonical `data.items` envelopes. Pair it with a Codex/browser
screenshot pass when checking visual layout or overflow.
Verify the backing state for the core workbench interactions: Verify the backing state for the core workbench interactions:
+10 -5
View File
@@ -79,6 +79,7 @@ project_id="$(json_field "${TMP_DIR}/demo.json" "data.project_id")"
area_id="$(json_field "${TMP_DIR}/demo.json" "data.area_id")" area_id="$(json_field "${TMP_DIR}/demo.json" "data.area_id")"
candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")" candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")"
reference_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.reference_dataset_id")" reference_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.reference_dataset_id")"
raster_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.raster_dataset_id")"
quality_check_id="$(json_field "${TMP_DIR}/demo.json" "data.quality_check_id")" quality_check_id="$(json_field "${TMP_DIR}/demo.json" "data.quality_check_id")"
curl -fsS "${BASE_URL%/}/api/v1/projects" > "${TMP_DIR}/projects.json" curl -fsS "${BASE_URL%/}/api/v1/projects" > "${TMP_DIR}/projects.json"
@@ -123,11 +124,11 @@ PY
curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets" > "${TMP_DIR}/datasets.json" curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets" > "${TMP_DIR}/datasets.json"
require_json_data "${TMP_DIR}/datasets.json" require_json_data "${TMP_DIR}/datasets.json"
"${PYTHON_BIN}" - "${TMP_DIR}/datasets.json" "${candidate_dataset_id}" "${reference_dataset_id}" <<'PY' "${PYTHON_BIN}" - "${TMP_DIR}/datasets.json" "${candidate_dataset_id}" "${reference_dataset_id}" "${raster_dataset_id}" <<'PY'
import json import json
import sys import sys
path, candidate_id, reference_id = sys.argv[1], sys.argv[2], sys.argv[3] path, candidate_id, reference_id, raster_id = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
with open(path, "r", encoding="utf-8") as handle: with open(path, "r", encoding="utf-8") as handle:
payload = json.load(handle) payload = json.load(handle)
items = payload["data"]["items"] items = payload["data"]["items"]
@@ -136,10 +137,14 @@ if candidate_id not in ids:
raise SystemExit("Candidate fixture dataset is missing from default workbench dataset list") raise SystemExit("Candidate fixture dataset is missing from default workbench dataset list")
if reference_id not in ids: if reference_id not in ids:
raise SystemExit("Reference fixture dataset is missing from default workbench dataset list") raise SystemExit("Reference fixture dataset is missing from default workbench dataset list")
if ids[candidate_id].get("status") != "ready" or ids[reference_id].get("status") != "ready": if raster_id not in ids:
raise SystemExit("Expected 2/2 ready demo datasets") raise SystemExit("Raster fixture dataset is missing from default workbench dataset list")
if ids[candidate_id].get("status") != "ready" or ids[reference_id].get("status") != "ready" or ids[raster_id].get("status") != "ready":
raise SystemExit("Expected 3/3 ready demo datasets")
if ids[reference_id].get("dataset_role") != "reference": if ids[reference_id].get("dataset_role") != "reference":
raise SystemExit("Default workbench reference dataset is not marked as reference") raise SystemExit("Default workbench reference dataset is not marked as reference")
if ids[raster_id].get("dataset_type") != "raster" or ids[raster_id].get("source_name") != "fixture":
raise SystemExit("Default workbench raster fixture lost its raster/source metadata")
PY PY
curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/quality-checks" > "${TMP_DIR}/quality_checks.json" curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/quality-checks" > "${TMP_DIR}/quality_checks.json"
@@ -167,5 +172,5 @@ PY
echo "Workbench default state verification passed" echo "Workbench default state verification passed"
echo "Project: GeoIntel Demo - Building QA" echo "Project: GeoIntel Demo - Building QA"
echo "Area: Demo AOI - Geel buildings" echo "Area: Demo AOI - Geel buildings"
echo "Datasets: 2/2 ready" echo "Datasets: 3/3 ready"
echo "QA/QC: seeded check is available" echo "QA/QC: seeded check is available"
+11 -4
View File
@@ -67,6 +67,7 @@ project_id="$(json_field "${TMP_DIR}/demo.json" "data.project_id")"
area_id="$(json_field "${TMP_DIR}/demo.json" "data.area_id")" area_id="$(json_field "${TMP_DIR}/demo.json" "data.area_id")"
candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")" candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")"
reference_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.reference_dataset_id")" reference_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.reference_dataset_id")"
raster_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.raster_dataset_id")"
quality_check_id="$(json_field "${TMP_DIR}/demo.json" "data.quality_check_id")" quality_check_id="$(json_field "${TMP_DIR}/demo.json" "data.quality_check_id")"
curl -fsS "${BASE_URL%/}/api/v1/projects" > "${TMP_DIR}/projects.json" curl -fsS "${BASE_URL%/}/api/v1/projects" > "${TMP_DIR}/projects.json"
@@ -87,12 +88,13 @@ require_json_data "${TMP_DIR}/quality_checks.json"
"${area_id}" \ "${area_id}" \
"${candidate_dataset_id}" \ "${candidate_dataset_id}" \
"${reference_dataset_id}" \ "${reference_dataset_id}" \
"${raster_dataset_id}" \
"${quality_check_id}" <<'PY' "${quality_check_id}" <<'PY'
import json import json
import sys import sys
projects_path, areas_path, datasets_path, quality_path = sys.argv[1:5] projects_path, areas_path, datasets_path, quality_path = sys.argv[1:5]
project_id, area_id, candidate_id, reference_id, quality_check_id = sys.argv[5:10] project_id, area_id, candidate_id, reference_id, raster_id, quality_check_id = sys.argv[5:11]
def load(path): def load(path):
with open(path, "r", encoding="utf-8") as handle: with open(path, "r", encoding="utf-8") as handle:
@@ -116,14 +118,19 @@ if not area.get("geometry"):
dataset_ids = {item["id"]: item for item in datasets} dataset_ids = {item["id"]: item for item in datasets}
candidate = dataset_ids.get(candidate_id) candidate = dataset_ids.get(candidate_id)
reference = dataset_ids.get(reference_id) reference = dataset_ids.get(reference_id)
raster = dataset_ids.get(raster_id)
if not candidate: if not candidate:
raise SystemExit("candidate dataset backing state is missing") raise SystemExit("candidate dataset backing state is missing")
if not reference: if not reference:
raise SystemExit("reference dataset backing state is missing") raise SystemExit("reference dataset backing state is missing")
if candidate.get("status") != "ready" or reference.get("status") != "ready": if not raster:
raise SystemExit("Dataset selection backing state is not 2/2 ready") raise SystemExit("raster dataset backing state is missing")
if candidate.get("status") != "ready" or reference.get("status") != "ready" or raster.get("status") != "ready":
raise SystemExit("Dataset selection backing state is not 3/3 ready")
if reference.get("dataset_role") != "reference": if reference.get("dataset_role") != "reference":
raise SystemExit("reference dataset backing state lost its reference role") raise SystemExit("reference dataset backing state lost its reference role")
if raster.get("dataset_type") != "raster" or raster.get("source_name") != "fixture":
raise SystemExit("raster dataset backing state lost its fixture raster metadata")
quality_check = next((item for item in checks if item["id"] == quality_check_id), None) quality_check = next((item for item in checks if item["id"] == quality_check_id), None)
if not quality_check or quality_check.get("status") != "ok": if not quality_check or quality_check.get("status") != "ok":
@@ -158,6 +165,6 @@ PY
echo "Workbench interaction backing-state verification passed" echo "Workbench interaction backing-state verification passed"
echo "Project switch: GeoIntel Demo - Building QA" echo "Project switch: GeoIntel Demo - Building QA"
echo "Area selection: Demo AOI - Geel buildings" echo "Area selection: Demo AOI - Geel buildings"
echo "Dataset selection: candidate dataset and reference dataset ready" echo "Dataset selection: candidate, reference and raster fixture datasets ready"
echo "QA refresh: seeded metrics available" echo "QA refresh: seeded metrics available"
echo "Export refresh: metadata export listed" echo "Export refresh: metadata export listed"