Expand scoped demo analysis access
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-08-01 16:00:13 +02:00
parent 96db4c966d
commit dfcc11b0d5
19 changed files with 206 additions and 112 deletions
+6 -8
View File
@@ -67,17 +67,15 @@ export function useDemoWorkflow({
setSegmentationReferenceDatasetId(result.reference_dataset_id)
setDemoWorkflowMessage(result.message)
await loadProjects(result.project_id)
const operatorOnlyLoads = restrictedMode
? Promise.resolve()
: Promise.all([
loadDetectionRuns(result.project_id),
loadSegmentationRuns(result.project_id),
loadExports(result.project_id),
]).then(() => undefined)
const analysisLoads = Promise.all([
loadDetectionRuns(result.project_id),
loadSegmentationRuns(result.project_id),
loadExports(result.project_id),
]).then(() => undefined)
const [projectData] = await Promise.all([
loadProjectData(result.project_id),
loadQualityChecks(result.project_id),
operatorOnlyLoads,
analysisLoads,
])
const candidateDataset = projectData?.datasets.find((dataset) => dataset.id === result.candidate_dataset_id)
const rasterDataset = projectData?.datasets.find((dataset) => dataset.id === result.raster_dataset_id)
+3 -2
View File
@@ -207,6 +207,7 @@ export function useDetectionWorkflow({
setDetectionRunError(null)
try {
const params = {
project_id: selectedProjectId ?? '',
class_name: detectionClassFilter || null,
min_confidence: detectionMinConfidenceFilter > 0 ? detectionMinConfidenceFilter : null,
}
@@ -415,7 +416,7 @@ export function useDetectionWorkflow({
setDetectionQaResult(null)
setRunningDetectionQa(true)
try {
const result = await detectionApi.compareWithReference(analysisRunId, {
const result = await detectionApi.compareWithReference(analysisRunId, selectedProjectId!, {
reference_dataset_id: referenceDatasetId,
iou_threshold: iouThresholdOverride ?? qaIouThreshold,
class_name: useCurrentFilters ? detectionClassFilter || null : null,
@@ -486,7 +487,7 @@ export function useDetectionWorkflow({
parameters_json: { calibration: true, calibration_thresholds: thresholds },
})
setSelectedDetectionRunId(result.analysis_run_id)
const qa = await detectionApi.compareWithReference(result.analysis_run_id, {
const qa = await detectionApi.compareWithReference(result.analysis_run_id, selectedProjectId, {
reference_dataset_id: detectionReferenceDatasetId,
iou_threshold: qaIouThreshold,
class_name: detectionClassFilter || null,
+10 -8
View File
@@ -59,7 +59,7 @@ export function useExportWorkflow({
setExporting(true)
setExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedDataset.project_id, {
dataset_id: selectedDataset.id,
export_kind: 'dataset',
name: selectedDataset.name.replace(/\.(geo)?json$/i, ''),
@@ -74,14 +74,14 @@ export function useExportWorkflow({
}
const exportSelectedDetectionRunGeoJson = async () => {
if (!selectedDetectionRunId) {
if (!selectedDetectionRunId || !selectedProjectId) {
setExportError('Select a detection run before exporting GeoJSON.')
return
}
setExporting(true)
setExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedProjectId, {
analysis_run_id: selectedDetectionRunId,
export_kind: 'detection_run',
})
@@ -95,14 +95,14 @@ export function useExportWorkflow({
}
const exportSelectedSegmentationRunGeoJson = async () => {
if (!selectedSegmentationRunId) {
if (!selectedSegmentationRunId || !selectedProjectId) {
setExportError('Select a segmentation run before exporting GeoJSON.')
return
}
setExporting(true)
setExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedProjectId, {
analysis_run_id: selectedSegmentationRunId,
export_kind: 'segmentation_run',
})
@@ -123,7 +123,7 @@ export function useExportWorkflow({
setSelectionExporting(true)
setSelectionExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedDataset.project_id, {
dataset_id: selectedDataset.id,
area_id: areaId,
export_kind: 'vector_selection',
@@ -199,7 +199,8 @@ export function useExportWorkflow({
const previewExportContent = async (exportId: string) => {
setExportError(null)
try {
const response = await exportsApi.getContent(exportId)
if (!selectedProjectId) return
const response = await exportsApi.getContent(selectedProjectId, exportId)
setExportPreview(response.content)
} catch (error) {
setExportError(formatError(error, 'Failed to load export content'))
@@ -207,7 +208,8 @@ export function useExportWorkflow({
}
const downloadExportArtifact = (exportId: string) => {
window.open(exportsApi.downloadUrl(exportId), '_blank', 'noopener,noreferrer')
if (!selectedProjectId) return
window.open(exportsApi.downloadUrl(selectedProjectId, exportId), '_blank', 'noopener,noreferrer')
}
const resetExportsForProject = () => {
+1 -1
View File
@@ -51,7 +51,7 @@ export function useMapSelectionQa({
iou_threshold: 0.5,
area_id: candidateDataset.area_id ?? null,
}
const job: JobRead = await qaApi.runQa(request)
const job: JobRead = await qaApi.runQa(selectedProjectId, request)
if (job.status === 'failed') {
setMapSelectionQaError(job.error_message || 'Map selection QA/QC failed')
return null
+1 -1
View File
@@ -92,7 +92,7 @@ export function useQualityWorkflow({ selectedProjectId, loadProjectData }: Quali
iou_threshold: qaIouThreshold,
area_id: qaAreaId || null,
}
const job: JobRead = await qaApi.runQa(request)
const job: JobRead = await qaApi.runQa(selectedProjectId, request)
if (job.status === 'failed') {
setQaError(job.error_message || 'QA comparison failed')
return
@@ -104,6 +104,7 @@ export function useSegmentationWorkflow({
setSegmentationRunError(null)
try {
const params = {
project_id: selectedProjectId ?? '',
class_name: segmentationClassFilter || null,
min_confidence: segmentationMinConfidenceFilter > 0 ? segmentationMinConfidenceFilter : null,
}
@@ -175,7 +176,7 @@ export function useSegmentationWorkflow({
setSegmentationQaResult(null)
setRunningSegmentationQa(true)
try {
const result = await segmentationApi.compareWithReference(selectedSegmentationRunId, {
const result = await segmentationApi.compareWithReference(selectedSegmentationRunId, selectedProjectId!, {
reference_dataset_id: segmentationReferenceDatasetId,
iou_threshold: qaIouThreshold,
class_name: segmentationClassFilter || null,
@@ -65,19 +65,19 @@ describe('useWorkbenchBootstrap', () => {
expect(state.loadExports).toHaveBeenCalledWith('project-1')
})
it('keeps the guest bootstrap inside the read-only demo surface', async () => {
it('loads the same analysis catalog and project results for a guest', async () => {
const state = { ...options('project-1'), restrictedMode: true }
renderHook(() => useWorkbenchBootstrap(state))
await waitFor(() => expect(state.loadProjectData).toHaveBeenCalledWith('project-1'))
expect(state.loadCapabilities).toHaveBeenCalledOnce()
expect(state.loadQualityChecks).toHaveBeenCalledWith('project-1')
expect(state.loadDetectionModels).not.toHaveBeenCalled()
expect(state.loadSegmentationModels).not.toHaveBeenCalled()
expect(state.loadDetectionRuns).not.toHaveBeenCalled()
expect(state.loadSegmentationRuns).not.toHaveBeenCalled()
expect(state.loadExports).not.toHaveBeenCalled()
expect(state.loadDetectionResults).not.toHaveBeenCalled()
expect(state.loadSegmentationResults).not.toHaveBeenCalled()
expect(state.loadDetectionModels).toHaveBeenCalledOnce()
expect(state.loadSegmentationModels).toHaveBeenCalledOnce()
expect(state.loadDetectionRuns).toHaveBeenCalledWith('project-1')
expect(state.loadSegmentationRuns).toHaveBeenCalledWith('project-1')
expect(state.loadExports).toHaveBeenCalledWith('project-1')
expect(state.loadDetectionResults).toHaveBeenCalledOnce()
expect(state.loadSegmentationResults).toHaveBeenCalledOnce()
})
})
+5 -11
View File
@@ -59,10 +59,8 @@ export function useWorkbenchBootstrap({
useEffect(() => {
loadProjects().catch(() => null)
loadCapabilities().catch(() => null)
if (!restrictedMode) {
loadDetectionModels().catch(() => null)
loadSegmentationModels().catch(() => null)
}
loadDetectionModels().catch(() => null)
loadSegmentationModels().catch(() => null)
}, [restrictedMode])
useEffect(() => {
@@ -81,20 +79,16 @@ export function useWorkbenchBootstrap({
resetExportsForProject()
loadProjectData(selectedProjectId).catch(() => null)
loadQualityChecks(selectedProjectId).catch(() => null)
if (!restrictedMode) {
loadDetectionRuns(selectedProjectId).catch(() => null)
loadSegmentationRuns(selectedProjectId).catch(() => null)
loadExports(selectedProjectId).catch(() => null)
}
loadDetectionRuns(selectedProjectId).catch(() => null)
loadSegmentationRuns(selectedProjectId).catch(() => null)
loadExports(selectedProjectId).catch(() => null)
}, [restrictedMode, selectedProjectId])
useEffect(() => {
if (restrictedMode) return
loadDetectionResults().catch(() => null)
}, [restrictedMode, selectedDetectionRunId, detectionClassFilter, detectionMinConfidenceFilter])
useEffect(() => {
if (restrictedMode) return
loadSegmentationResults().catch(() => null)
}, [restrictedMode, selectedSegmentationRunId, segmentationClassFilter, segmentationMinConfidenceFilter])
}