Initial public release
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from app.models import Dataset, SourceRegistry, SourceSnapshot
|
||||
from app.services.qa_service import QaService
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self, datasets=None, areas=None):
|
||||
self.datasets = {item.id: item for item in (datasets or [])}
|
||||
self.areas = {item.id: item for item in (areas or [])}
|
||||
|
||||
def get(self, model, item_id):
|
||||
if model.__name__ == "Dataset":
|
||||
return self.datasets.get(item_id)
|
||||
if model.__name__ == "Area":
|
||||
return self.areas.get(item_id)
|
||||
return None
|
||||
|
||||
|
||||
def _feature(feature_id: str, coordinates: list[list[list[float]]]) -> dict:
|
||||
return {
|
||||
"type": "Feature",
|
||||
"id": feature_id,
|
||||
"properties": {"source_feature_id": feature_id},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": coordinates,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _write_dataset(path: Path, coordinates: list[list[list[float]]]) -> None:
|
||||
payload = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [_feature("feature-1", coordinates)],
|
||||
}
|
||||
path.write_text(json.dumps(payload), encoding="utf-8")
|
||||
|
||||
|
||||
def _write_features(path: Path, features: list[dict]) -> None:
|
||||
path.write_text(json.dumps({"type": "FeatureCollection", "features": features}), encoding="utf-8")
|
||||
|
||||
|
||||
def _authoritative_reference(dataset: Dataset) -> Dataset:
|
||||
"""Give QA reference fixtures the same durable authority proof as GRB."""
|
||||
|
||||
source_id = uuid4()
|
||||
snapshot_id = uuid4()
|
||||
checksum = sha256(Path(str(dataset.storage_path)).read_bytes()).hexdigest()
|
||||
source = SourceRegistry(
|
||||
id=source_id,
|
||||
source_key="grb",
|
||||
display_name="GRB test reference",
|
||||
classification="authoritative",
|
||||
authority_name="Digitaal Vlaanderen",
|
||||
authority_scope_json={"zone": "Flanders"},
|
||||
usage_policy_json={"ground_truth_allowed": True, "validation_authority": {"building_validation": "primary"}},
|
||||
)
|
||||
snapshot = SourceSnapshot(
|
||||
id=snapshot_id,
|
||||
source_registry_id=source_id,
|
||||
snapshot_key=f"qa-grb-{dataset.id}",
|
||||
checksum_sha256=checksum,
|
||||
ingest_status="ingested",
|
||||
freshness_status="current",
|
||||
)
|
||||
dataset.source = "grb"
|
||||
dataset.source_name = "grb"
|
||||
dataset.dataset_role = "reference"
|
||||
dataset.status = "ready"
|
||||
dataset.checksum_sha256 = checksum
|
||||
dataset.source_registry_id = source_id
|
||||
dataset.source_snapshot_id = snapshot_id
|
||||
dataset.data_contract_key = "geointel.vector.geojson"
|
||||
dataset.data_contract_version = "1.0.0"
|
||||
dataset.validation_status = "passed"
|
||||
dataset.provenance_status = "complete"
|
||||
dataset.lineage_status = "complete"
|
||||
dataset.quarantine_status = "not_quarantined"
|
||||
dataset.source_registry = source
|
||||
dataset.source_snapshot = snapshot
|
||||
return dataset
|
||||
|
||||
|
||||
def test_qa_compare_candidate_with_reference_returns_metrics(tmp_path) -> None:
|
||||
project_id = uuid4()
|
||||
candidate_id = uuid4()
|
||||
reference_id = uuid4()
|
||||
candidate_path = tmp_path / "candidate.geojson"
|
||||
reference_path = tmp_path / "reference.geojson"
|
||||
polygon = [[[4.0, 51.0], [4.1, 51.0], [4.1, 51.1], [4.0, 51.1], [4.0, 51.0]]]
|
||||
_write_dataset(candidate_path, polygon)
|
||||
_write_dataset(reference_path, polygon)
|
||||
|
||||
candidate = Dataset(
|
||||
id=candidate_id,
|
||||
project_id=project_id,
|
||||
name="candidate.geojson",
|
||||
dataset_type="vector",
|
||||
source="test",
|
||||
storage_path=str(candidate_path),
|
||||
crs="EPSG:4326",
|
||||
metadata_json={"crs_assumed": False},
|
||||
)
|
||||
reference = _authoritative_reference(Dataset(
|
||||
id=reference_id,
|
||||
project_id=project_id,
|
||||
name="reference.geojson",
|
||||
dataset_type="vector",
|
||||
source="test",
|
||||
storage_path=str(reference_path),
|
||||
crs="EPSG:4326",
|
||||
metadata_json={"crs_assumed": False},
|
||||
))
|
||||
|
||||
result = QaService.compare_candidate_with_reference(
|
||||
db=FakeSession([candidate, reference]),
|
||||
project_id=project_id,
|
||||
candidate_dataset_id=candidate_id,
|
||||
reference_dataset_id=reference_id,
|
||||
iou_threshold=0.5,
|
||||
)
|
||||
|
||||
assert result.status == "ok"
|
||||
assert result.matches == 1
|
||||
assert result.false_positives == 0
|
||||
assert result.false_negatives == 0
|
||||
assert result.precision == 1.0
|
||||
assert result.recall == 1.0
|
||||
assert result.f1_score == 1.0
|
||||
|
||||
|
||||
def test_qa_compare_candidate_with_reference_returns_feature_level_evidence(tmp_path) -> None:
|
||||
project_id = uuid4()
|
||||
candidate_id = uuid4()
|
||||
reference_id = uuid4()
|
||||
candidate_path = tmp_path / "candidate.geojson"
|
||||
reference_path = tmp_path / "reference.geojson"
|
||||
matched_candidate = [[[4.0, 51.0], [4.1, 51.0], [4.1, 51.1], [4.0, 51.1], [4.0, 51.0]]]
|
||||
matched_reference = [[[4.0, 51.0], [4.1, 51.0], [4.1, 51.1], [4.0, 51.1], [4.0, 51.0]]]
|
||||
false_positive = [[[4.5, 51.5], [4.6, 51.5], [4.6, 51.6], [4.5, 51.6], [4.5, 51.5]]]
|
||||
false_negative = [[[4.8, 51.8], [4.9, 51.8], [4.9, 51.9], [4.8, 51.9], [4.8, 51.8]]]
|
||||
_write_features(candidate_path, [_feature("candidate-match", matched_candidate), _feature("candidate-extra", false_positive)])
|
||||
_write_features(reference_path, [_feature("reference-match", matched_reference), _feature("reference-missing", false_negative)])
|
||||
|
||||
candidate = Dataset(
|
||||
id=candidate_id,
|
||||
project_id=project_id,
|
||||
name="candidate.geojson",
|
||||
dataset_type="vector",
|
||||
source="test",
|
||||
storage_path=str(candidate_path),
|
||||
crs="EPSG:4326",
|
||||
metadata_json={"crs_assumed": False},
|
||||
)
|
||||
reference = _authoritative_reference(Dataset(
|
||||
id=reference_id,
|
||||
project_id=project_id,
|
||||
name="reference.geojson",
|
||||
dataset_type="vector",
|
||||
source="test",
|
||||
storage_path=str(reference_path),
|
||||
crs="EPSG:4326",
|
||||
metadata_json={"crs_assumed": False},
|
||||
))
|
||||
|
||||
result = QaService.compare_candidate_with_reference(
|
||||
db=FakeSession([candidate, reference]),
|
||||
project_id=project_id,
|
||||
candidate_dataset_id=candidate_id,
|
||||
reference_dataset_id=reference_id,
|
||||
iou_threshold=0.5,
|
||||
)
|
||||
|
||||
assert result.matches == 1
|
||||
assert result.false_positives == 1
|
||||
assert result.false_negatives == 1
|
||||
assert result.match_evidence == [
|
||||
{
|
||||
"candidate_feature_id": "candidate-match",
|
||||
"reference_feature_id": "reference-match",
|
||||
"iou": 1.0,
|
||||
}
|
||||
]
|
||||
assert result.false_positive_evidence == [{"candidate_feature_id": "candidate-extra"}]
|
||||
assert result.false_negative_evidence == [{"reference_feature_id": "reference-missing"}]
|
||||
|
||||
|
||||
def test_dataset_reference_metadata_migration_declares_required_columns() -> None:
|
||||
migration_path = Path(__file__).parents[1] / "alembic" / "versions" / "202606120001_add_dataset_reference_metadata.py"
|
||||
migration_text = migration_path.read_text(encoding="utf-8")
|
||||
for column_name in (
|
||||
"dataset_role",
|
||||
"source_name",
|
||||
"reference_layer_name",
|
||||
"source_metadata",
|
||||
"provenance_metadata",
|
||||
"imported_at",
|
||||
):
|
||||
assert column_name in migration_text
|
||||
Reference in New Issue
Block a user