fix: polish live regional workbench labels
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
- Replaced technical dataset names with theme/source labels and moved benchmark projects, raw source metadata, provider internals, QA evidence and model diagnostics behind advanced disclosures.
|
||||
- Defaulted Detection Lab to the configured local YOLO asset, active local model profile and measured operating threshold; model quality remains explicitly review-required rather than presented as production-perfect.
|
||||
- Simplified Quality, Downloads, Status and Beheer around end-user tasks without changing API contracts, migrations, persistence or inference behavior.
|
||||
- Followed the live browser audit by moving the legacy Mol-only workspace under advanced management, adding friendly regional-boundary dataset names, translating the active model profiles and replacing the empty `waiting` quality status with end-user language.
|
||||
|
||||
## Sprint 192 Regional map state correctness (2026-07-14)
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ def test_detection_operator_profiles_define_explicit_yolo_candidates_and_promote
|
||||
assert "f1: 0.5824578631584316" in source
|
||||
assert "f1: 0.5432865390636915" in source
|
||||
assert "maxBackgroundDetections: 0" in source
|
||||
assert "pure-empty gate passed" in source
|
||||
assert "1,571 fewer false negatives" in source
|
||||
assert "higher false-positive review load" in source
|
||||
assert "lege-achtergrondtest is geslaagd" in source
|
||||
assert "1.571 minder gemiste gebouwen" in source
|
||||
assert "onterecht gevonden objecten" in source
|
||||
|
||||
|
||||
def test_detection_lab_surfaces_profiles_as_deliberate_operator_actions() -> None:
|
||||
|
||||
@@ -34,12 +34,17 @@ def test_technical_projects_and_metadata_are_progressively_disclosed() -> None:
|
||||
projects = read("frontend/src/components/project/ProjectPanel.tsx")
|
||||
areas = read("frontend/src/components/project/AreaPanel.tsx")
|
||||
datasets = read("frontend/src/components/datasets/DatasetPanel.tsx")
|
||||
dataset_names = read("frontend/src/lib/datasetDisplay.ts")
|
||||
|
||||
assert "TECHNICAL_PROJECT_PATTERN" in projects
|
||||
assert "technische test- en benchmarkruns" in projects
|
||||
assert "project.name === LEGACY_MOL_PROJECT_NAME" in projects
|
||||
assert "alternatieve en technische werkruimtes" in projects
|
||||
assert "Kempen · volledige regionale werkruimte" in projects
|
||||
assert "area-catalog-disclosure" in areas
|
||||
assert "dataset-technical-details" in datasets
|
||||
assert "getDatasetDisplayName" in datasets
|
||||
assert "dataset.source_metadata?.layer_type" in dataset_names
|
||||
assert "regional_boundary: 'Grens vervoerregio Kempen'" in dataset_names
|
||||
|
||||
|
||||
def test_configured_yolo_and_active_asset_are_selected_without_hiding_limitations() -> None:
|
||||
@@ -53,3 +58,13 @@ def test_configured_yolo_and_active_asset_are_selected_without_hiding_limitation
|
||||
assert 'aria-label="Status gebouwdetectie"' in lab
|
||||
assert "resultaten blijven controleplichtig" in lab
|
||||
assert "Modelkalibratie voor beheerders" in lab
|
||||
|
||||
|
||||
def test_visible_ai_and_quality_labels_are_end_user_facing() -> None:
|
||||
profiles = read("frontend/src/components/detection/detectionProfiles.ts")
|
||||
quality = read("frontend/src/components/quality/QualityResultsPanel.tsx")
|
||||
|
||||
assert "Aanbevolen controleprofiel kleine gebouwen" in profiles
|
||||
assert "Controleer wel extra op onterecht gevonden objecten" in profiles
|
||||
assert "qualityStatusLabel" in quality
|
||||
assert "nog niet uitgevoerd" in quality
|
||||
|
||||
@@ -18,6 +18,11 @@ Tested before deployment:
|
||||
- The false-positive review validator returned the expected `review_required` gate with all 48 selected decisions still `unreviewed`; no unreviewed QA mismatch was promoted into training data.
|
||||
- Local Docker validation was unavailable because the Windows workstation has no Docker CLI; live container/PostGIS validation remains part of the Tower deployment pass.
|
||||
|
||||
Live browser follow-up:
|
||||
- Tower rebuilt the AI-enabled all-in-one image successfully; container health, PostGIS 3.6, required schema objects, Alembic head `202607140001`, frontend, API proxy and icon checks passed.
|
||||
- The map opened directly on the complete regional workspace with 29 work Areas, no region selector, the full region selected and 466,078 buildings, 88,332 water objects, 84,504 roads and 415,288 parcels available.
|
||||
- The browser audit found four remaining presentation leaks: the legacy Mol-only project was still prominent, boundary datasets showed filenames, profile names were English and an empty quality status said `waiting`. These were corrected and protected by the now `561`-test suite.
|
||||
|
||||
Next:
|
||||
- Extend official population and modern land-use time series from Mol to all 28 municipalities, then expose a guided raster-import and configured-detection run over a drawn map selection.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface DetectionOperatorProfile {
|
||||
export const DETECTION_OPERATOR_PROFILES: DetectionOperatorProfile[] = [
|
||||
{
|
||||
id: 'small-building-balanced-review',
|
||||
displayName: 'Recommended small-building review',
|
||||
displayName: 'Aanbevolen controleprofiel kleine gebouwen',
|
||||
modelAssetId: 'geointel-building-yolov8s-smallbld-minpx3-img640-ft30-pt',
|
||||
confidenceThreshold: 0.15,
|
||||
defaultApproved: true,
|
||||
@@ -28,13 +28,13 @@ export const DETECTION_OPERATOR_PROFILES: DetectionOperatorProfile[] = [
|
||||
positiveSampleCount: 7,
|
||||
maxBackgroundDetections: 0,
|
||||
description:
|
||||
'Recommended recall-balanced profile for building review, with improved small-building coverage across seven Kempen AOIs.',
|
||||
'Aanbevolen profiel met een evenwicht tussen gevonden en gemiste kleine gebouwen, gemeten over zeven testgebieden in de Kempen.',
|
||||
limitationMessage:
|
||||
'The pure-empty gate passed and persisted QA found 1,571 fewer false negatives than the previous balanced profile; expect a higher false-positive review load.',
|
||||
'De lege-achtergrondtest is geslaagd en de kwaliteitsmeting vond 1.571 minder gemiste gebouwen dan het vorige profiel. Controleer wel extra op onterecht gevonden objecten.',
|
||||
},
|
||||
{
|
||||
id: 'expanded-balanced-review',
|
||||
displayName: 'Legacy expanded balanced review',
|
||||
displayName: 'Voorgaand gebalanceerd controleprofiel',
|
||||
modelAssetId: 'geointel-building-yolov8s-aoi1024expandedminpx4vis035e50-pt',
|
||||
confidenceThreshold: 0.15,
|
||||
defaultApproved: true,
|
||||
@@ -44,13 +44,13 @@ export const DETECTION_OPERATOR_PROFILES: DetectionOperatorProfile[] = [
|
||||
f1: 0.5432865390636915,
|
||||
positiveSampleCount: 7,
|
||||
maxBackgroundDetections: 0,
|
||||
description: 'Legacy expanded-AOI profile for review sessions where precision matters more than the newest recall gain.',
|
||||
description: 'Voorgaand profiel voor controles waarbij minder foutieve vondsten belangrijker zijn dan maximale dekking.',
|
||||
limitationMessage:
|
||||
'The pure-empty gate passed; this profile has fewer false positives but more persistent small-building misses than the recommended profile.',
|
||||
'De lege-achtergrondtest is geslaagd. Dit profiel vindt minder onterechte objecten, maar mist meer kleine gebouwen dan het aanbevolen profiel.',
|
||||
},
|
||||
{
|
||||
id: 'conservative-review',
|
||||
displayName: 'Promoted conservative review',
|
||||
displayName: 'Conservatief controleprofiel',
|
||||
modelAssetId: 'geointel-building-yolov8s-aoi1024bg512r3e50-pt',
|
||||
confidenceThreshold: 0.35,
|
||||
defaultApproved: true,
|
||||
@@ -60,8 +60,8 @@ export const DETECTION_OPERATOR_PROFILES: DetectionOperatorProfile[] = [
|
||||
f1: 0.32086574003576274,
|
||||
positiveSampleCount: 7,
|
||||
maxBackgroundDetections: 0,
|
||||
description: 'Legacy high-precision profile for review sessions where fewer false positives matter more than recall.',
|
||||
description: 'Profiel met hoge precisie voor controles waarbij zo weinig mogelijk foutieve vondsten zwaarder wegen dan volledige dekking.',
|
||||
limitationMessage:
|
||||
'Default-approved after the split-background pure-empty gate passed; sparse-context detections remain review-only evidence.',
|
||||
'Goedgekeurd na de lege-achtergrondtest. Resultaten in dun bebouwde context blijven altijd controlebewijs en geen automatische waarheid.',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import type { FormEvent } from 'react'
|
||||
import type { AreaRead, ProjectRead } from '../../types'
|
||||
|
||||
function projectDisplayName(project: ProjectRead | null): string {
|
||||
if (project?.name === 'Kempen Regional Workbench') {
|
||||
return 'Kempen · volledige regio'
|
||||
}
|
||||
if (project?.name === 'Mol Municipality Workbench') {
|
||||
return 'Mol · lokale historische werkruimte'
|
||||
}
|
||||
return project?.name ?? 'Geen werkruimte actief'
|
||||
}
|
||||
|
||||
interface AreaFormState {
|
||||
name: string
|
||||
geometry: string
|
||||
@@ -41,7 +51,7 @@ export function AreaPanel({
|
||||
</div>
|
||||
<span className="count-pill">{areas.length}</span>
|
||||
</div>
|
||||
<p className="muted">{selectedProject ? selectedProject.name : 'Geen werkruimte actief'}</p>
|
||||
<p className="muted">{projectDisplayName(selectedProject)}</p>
|
||||
|
||||
<div className="data-selection-summary data-selection-summary-area">
|
||||
<span>Getoond op de kaart</span>
|
||||
|
||||
@@ -2,11 +2,27 @@ import type { FormEvent } from 'react'
|
||||
import type { ProjectCreate, ProjectRead } from '../../types'
|
||||
|
||||
const TECHNICAL_PROJECT_PATTERN = /^(GeoIntel Detection Quality Matrix|GeoIntel hard-negative|GeoIntel training|Mol Building QA)/i
|
||||
const REGIONAL_PROJECT_NAME = 'Kempen Regional Workbench'
|
||||
const LEGACY_MOL_PROJECT_NAME = 'Mol Municipality Workbench'
|
||||
|
||||
function isTechnicalProject(project: ProjectRead): boolean {
|
||||
return TECHNICAL_PROJECT_PATTERN.test(project.name)
|
||||
}
|
||||
|
||||
function isAdvancedProject(project: ProjectRead): boolean {
|
||||
return isTechnicalProject(project) || project.name === LEGACY_MOL_PROJECT_NAME
|
||||
}
|
||||
|
||||
function projectDisplayName(project: ProjectRead): string {
|
||||
if (project.name === REGIONAL_PROJECT_NAME) {
|
||||
return 'Kempen · volledige regionale werkruimte'
|
||||
}
|
||||
if (project.name === LEGACY_MOL_PROJECT_NAME) {
|
||||
return 'Mol · lokale historische werkruimte'
|
||||
}
|
||||
return project.name
|
||||
}
|
||||
|
||||
interface ProjectPanelProps {
|
||||
projects: ProjectRead[]
|
||||
selectedProjectId: string | null
|
||||
@@ -33,9 +49,9 @@ export function ProjectPanel({
|
||||
onLoadDemoWorkflow,
|
||||
}: ProjectPanelProps): JSX.Element {
|
||||
const selectedProject = projects.find((project) => project.id === selectedProjectId)
|
||||
const regularProjects = projects.filter((project) => !isTechnicalProject(project))
|
||||
const technicalProjects = projects.filter(isTechnicalProject)
|
||||
const visibleProjects = selectedProject && isTechnicalProject(selectedProject)
|
||||
const regularProjects = projects.filter((project) => !isAdvancedProject(project))
|
||||
const advancedProjects = projects.filter(isAdvancedProject)
|
||||
const visibleProjects = selectedProject && isAdvancedProject(selectedProject)
|
||||
? [selectedProject, ...regularProjects]
|
||||
: regularProjects
|
||||
|
||||
@@ -52,7 +68,7 @@ export function ProjectPanel({
|
||||
|
||||
<div className="data-selection-summary data-selection-summary-project">
|
||||
<span>Actieve werkruimte</span>
|
||||
<strong>{selectedProject?.name ?? 'Geen werkruimte actief'}</strong>
|
||||
<strong>{selectedProject ? projectDisplayName(selectedProject) : 'Geen werkruimte actief'}</strong>
|
||||
<small>{selectedProject?.region ?? 'De regionale werkruimte wordt automatisch gekozen.'}</small>
|
||||
</div>
|
||||
|
||||
@@ -68,7 +84,7 @@ export function ProjectPanel({
|
||||
aria-pressed={project.id === selectedProjectId}
|
||||
data-testid={`project-select-${project.id}`}
|
||||
>
|
||||
<strong>{project.name}</strong>
|
||||
<strong>{projectDisplayName(project)}</strong>
|
||||
<span>{project.region || 'Regio niet opgegeven'}</span>
|
||||
</button>
|
||||
</li>
|
||||
@@ -118,11 +134,11 @@ export function ProjectPanel({
|
||||
</button>
|
||||
{demoWorkflowMessage ? <p>{demoWorkflowMessage}</p> : null}
|
||||
</div>
|
||||
{technicalProjects.length > 0 ? (
|
||||
{advancedProjects.length > 0 ? (
|
||||
<details className="technical-run-list">
|
||||
<summary>{technicalProjects.length} technische test- en benchmarkruns</summary>
|
||||
<summary>{advancedProjects.length} alternatieve en technische werkruimtes</summary>
|
||||
<ul className="entity-list">
|
||||
{technicalProjects.map((project) => (
|
||||
{advancedProjects.map((project) => (
|
||||
<li key={project.id}>
|
||||
<button
|
||||
className={project.id === selectedProjectId ? 'entity-card entity-card-active' : 'entity-card'}
|
||||
@@ -131,7 +147,7 @@ export function ProjectPanel({
|
||||
aria-pressed={project.id === selectedProjectId}
|
||||
data-testid={`project-select-${project.id}`}
|
||||
>
|
||||
<strong>{project.name}</strong>
|
||||
<strong>{projectDisplayName(project)}</strong>
|
||||
<span>{project.region || 'Technische run'}</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -45,6 +45,13 @@ function qualityMetricValue(metric: MetricRead | undefined): string {
|
||||
return Number.isInteger(value) ? String(value) : value.toFixed(3)
|
||||
}
|
||||
|
||||
function qualityStatusLabel(status: string | null | undefined): string {
|
||||
if (!status || status === 'waiting') return 'nog niet uitgevoerd'
|
||||
if (status === 'completed' || status === 'ok') return 'afgerond'
|
||||
if (status === 'failed') return 'mislukt'
|
||||
return status
|
||||
}
|
||||
|
||||
function metricByKey(check: QualityCheckRead | null, metricKey: string): MetricRead | undefined {
|
||||
return check?.metrics.find((metric) => metric.metric_key === metricKey)
|
||||
}
|
||||
@@ -169,7 +176,7 @@ export function QualityResultsPanel({
|
||||
</div>
|
||||
<div>
|
||||
<span>Laatste status</span>
|
||||
<strong>{latestCheck?.status ?? 'waiting'}</strong>
|
||||
<strong>{qualityStatusLabel(latestCheck?.status)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -201,7 +208,7 @@ export function QualityResultsPanel({
|
||||
</div>
|
||||
<div>
|
||||
<span>Laatste vergelijking</span>
|
||||
<strong>{latestCheck?.status ?? 'waiting'}</strong>
|
||||
<strong>{qualityStatusLabel(latestCheck?.status)}</strong>
|
||||
<p className="quality-dataset-name">
|
||||
Te controleren: {latestCandidateName ?? latestCheck?.candidate_dataset_id ?? 'n.v.t.'} / referentie: {latestReferenceName ?? latestCheck?.reference_dataset_id ?? 'n.v.t.'}
|
||||
</p>
|
||||
|
||||
@@ -12,7 +12,7 @@ const DATASET_LABEL_BY_LAYER: Record<string, string> = {
|
||||
}
|
||||
|
||||
export function getDatasetDisplayName(dataset: DatasetCreateResponse): string {
|
||||
const layer = (dataset.reference_layer_name ?? dataset.source_metadata?.layer_name ?? '')
|
||||
const layer = (dataset.reference_layer_name ?? dataset.source_metadata?.layer_name ?? dataset.source_metadata?.layer_type ?? '')
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
const label = DATASET_LABEL_BY_LAYER[layer]
|
||||
|
||||
Reference in New Issue
Block a user