From 29517d68cad6ea69c13ebc6c8daeac64ab432bf1 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 20 Sep 2024 13:25:33 +0200 Subject: [PATCH 01/39] WIP --- docker-compose.yml | 8 +++++ src/api/Controllers/BoreholeFileController.cs | 35 +++++++++++++++---- src/client/src/api/file/file.ts | 16 +++++++-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b293b7e2b..3533d6800 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,14 @@ services: ports: - 9000:9000 - 9002:9002 + dataextraction: + image: borehole-api + restart: unless-stopped + ports: + - 8000:8000 + environment: + AWS_ENDPOINT: http://minio:9000 + AWS_S3_BUCKET: "cannonflea" db: image: postgis/postgis:15-3.4-alpine restart: unless-stopped diff --git a/src/api/Controllers/BoreholeFileController.cs b/src/api/Controllers/BoreholeFileController.cs index edc4ab241..8df06fa39 100644 --- a/src/api/Controllers/BoreholeFileController.cs +++ b/src/api/Controllers/BoreholeFileController.cs @@ -113,15 +113,36 @@ public async Task GetDataExtractionFileInfo([Required, Range(1, i var fileUuid = boreholeFile.File.NameUuid.Replace(".pdf", "", StringComparison.OrdinalIgnoreCase); var fileCount = await boreholeFileUploadService.CountDataExtractionObjects(fileUuid).ConfigureAwait(false); - var result = await boreholeFileUploadService.GetDataExtractionImageInfo(fileUuid, index).ConfigureAwait(false); - return Ok(new + try { - fileName = result.FileName, - width = result.Width, - height = result.Height, - count = fileCount, - }); + var result = await boreholeFileUploadService.GetDataExtractionImageInfo(fileUuid, index).ConfigureAwait(false); + + return Ok(new + { + fileName = result.FileName, + width = result.Width, + height = result.Height, + count = fileCount, + }); + } + catch (Exception ex) + { + if (ex.Message.Contains("The specified key does not exist.")) + { + return Ok(new + { + fileName = fileUuid, + width = 0, + height = 0, + count = 0, + }); + } else + { + return Problem(ex.Message); + + } + } } catch (Exception ex) { diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index 254ea0486..5c3bc5f6f 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -52,12 +52,24 @@ export const updateFile = async ( }; export const getDataExtractionFileInfo = async (boreholeFileId: number, index: number) => { - const response = await fetchApiV2( + let response = await fetchApiV2( `boreholefile/getDataExtractionFileInfo?boreholeFileId=${boreholeFileId}&index=${index}`, "GET", ); if (response) { - return response as DataExtractionResponse; + response = response as DataExtractionResponse; + if (response.count === 0) { + const createResponse = await fetch("http://localhost:8000/api/V1/create_png", { + method: "POST", + cache: "no-cache", + credentials: "same-origin", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ filename: response.fileName + ".pdf" }), + }); + console.log("createResponse", createResponse); + // TODO: Handle createResponse + } + return response; } else { throw new ApiError("errorDataExtractionFileLoading", 500); } From dd257f51a2753f746256c34d848d0f6455d5c3aa Mon Sep 17 00:00:00 2001 From: tschumpr Date: Fri, 20 Sep 2024 17:16:48 +0200 Subject: [PATCH 02/39] Integrate extraction api --- docker-compose.yml | 3 + src/client/src/api/file/file.ts | 53 +++++++++++---- .../form/location/coordinatesSegment.tsx | 16 ++--- .../detail/labeling/labelingInterfaces.tsx | 7 +- .../pages/detail/labeling/labelingPanel.tsx | 65 +++++++++---------- 5 files changed, 89 insertions(+), 55 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3533d6800..3d524e4ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,10 @@ services: - 8000:8000 environment: AWS_ENDPOINT: http://minio:9000 + AWS_ACCESS_KEY_ID: REDSQUIRREL + AWS_SECRET_ACCESS_KEY: YELLOWMONKEY AWS_S3_BUCKET: "cannonflea" + db: image: postgis/postgis:15-3.4-alpine restart: unless-stopped diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index 5c3bc5f6f..12f1d9ccf 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -1,6 +1,7 @@ import { DataExtractionResponse, maxFileSizeKB } from "./fileInterfaces.ts"; import { download, fetchApiV2, fetchApiV2Base, upload } from "../fetchApiV2"; import { ApiError } from "../apiInterfaces.ts"; +import { ExtractionRequest, ExtractionResponse } from "../../pages/detail/labeling/labelingInterfaces.tsx"; export async function uploadFile(boreholeId: number, file: File) { if (file && file.size <= maxFileSizeKB) { @@ -51,7 +52,10 @@ export const updateFile = async ( }); }; -export const getDataExtractionFileInfo = async (boreholeFileId: number, index: number) => { +export async function getDataExtractionFileInfo( + boreholeFileId: number, + index: number, +): Promise { let response = await fetchApiV2( `boreholefile/getDataExtractionFileInfo?boreholeFileId=${boreholeFileId}&index=${index}`, "GET", @@ -59,21 +63,14 @@ export const getDataExtractionFileInfo = async (boreholeFileId: number, index: n if (response) { response = response as DataExtractionResponse; if (response.count === 0) { - const createResponse = await fetch("http://localhost:8000/api/V1/create_png", { - method: "POST", - cache: "no-cache", - credentials: "same-origin", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ filename: response.fileName + ".pdf" }), - }); - console.log("createResponse", createResponse); - // TODO: Handle createResponse + await createExtractionPngs(response.fileName); + return await getDataExtractionFileInfo(boreholeFileId, index); } return response; } else { throw new ApiError("errorDataExtractionFileLoading", 500); } -}; +} export async function loadImage(fileName: string) { const response = await fetchApiV2Base("boreholefile/dataextraction/" + fileName, "GET"); @@ -83,3 +80,37 @@ export async function loadImage(fileName: string) { throw new ApiError(response.statusText, response.status); } } + +export async function createExtractionPngs(fileName: string) { + // TODO: Maybe update URL after proper integration + const response = await fetch("http://localhost:8000/api/V1/create_pngs", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ filename: fileName + ".pdf" }), + }); + if (!response.ok) { + throw new ApiError("errorDataExtractionFileLoading", 500); + } +} + +export async function extractData(request: ExtractionRequest): Promise { + // TODO: Maybe update URL after proper integration + const response = await fetch("http://localhost:8000/api/V1/extract_data", { + method: "POST", + headers: { "Content-Type": "application/json", Accept: "application/json" }, + body: JSON.stringify(request), + }); + + if (response.ok) { + const extractionResponse = (await response.json()) as ExtractionResponse; + console.log("extractionResponse", extractionResponse); + if (extractionResponse.detail) { + throw new ApiError(extractionResponse.detail, 500); + } + return extractionResponse; + } else { + const text = await response.text(); + console.log(text); + throw new ApiError("errorDataExtraction", response.status); + } +} diff --git a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx index d06ac4024..476436845 100644 --- a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx +++ b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx @@ -282,7 +282,7 @@ const CoordinatesSegment: React.FC = ({ ]); useEffect(() => { - if (extractionObject?.type === "coordinate" && extractionObject?.state === "success") { + if (extractionObject?.type === "coordinates" && extractionObject?.state === "success") { const coordinate = extractionObject?.result?.value as Coordinate; if (coordinate) { setCurrentReferenceSystem(referenceSystems[coordinate.projection].code); @@ -380,7 +380,7 @@ const CoordinatesSegment: React.FC = ({ const referenceSystemKey = currentReferenceSystem === referenceSystems.LV95.code ? ReferenceSystemKey.LV95 : ReferenceSystemKey.LV03; setExtractionObject({ - type: "coordinate", + type: "coordinates", state: "start", previousValue: { east: formMethods.getValues(referenceSystems[referenceSystemKey].fieldName.X), @@ -403,7 +403,7 @@ const CoordinatesSegment: React.FC = ({ showLabeling && editingEnabled && ( startLabeling()} /> ) @@ -418,7 +418,7 @@ const CoordinatesSegment: React.FC = ({ selected={[currentReferenceSystem ?? referenceSystems.LV95.code]} canReset={false} readonly={!editingEnabled} - className={extractionObject?.type === "coordinate" ? "ai" : ""} + className={extractionObject?.type === "coordinates" ? "ai" : ""} onUpdate={e => onReferenceSystemChange(e)} values={Object.entries(referenceSystems).map(([, value]) => ({ key: value.code, @@ -435,7 +435,7 @@ const CoordinatesSegment: React.FC = ({ onUpdate={onCoordinateChange} disabled={currentReferenceSystem !== referenceSystems.LV95.code} readonly={!editingEnabled} - className={extractionObject?.type === "coordinate" ? "ai" : ""} + className={extractionObject?.type === "coordinates" ? "ai" : ""} value={formMethods.getValues(referenceSystems.LV95.fieldName.X)} /> = ({ onUpdate={onCoordinateChange} disabled={currentReferenceSystem !== referenceSystems.LV95.code} readonly={!editingEnabled} - className={extractionObject?.type === "coordinate" ? "ai" : ""} + className={extractionObject?.type === "coordinates" ? "ai" : ""} value={formMethods.getValues(referenceSystems.LV95.fieldName.Y)} /> @@ -459,7 +459,7 @@ const CoordinatesSegment: React.FC = ({ onUpdate={onCoordinateChange} disabled={currentReferenceSystem !== referenceSystems.LV03.code} readonly={!editingEnabled} - className={extractionObject?.type === "coordinate" ? "ai" : ""} + className={extractionObject?.type === "coordinates" ? "ai" : ""} value={formMethods.getValues(referenceSystems.LV03.fieldName.X)} /> = ({ onUpdate={onCoordinateChange} disabled={currentReferenceSystem !== referenceSystems.LV03.code} readonly={!editingEnabled} - className={extractionObject?.type === "coordinate" ? "ai" : ""} + className={extractionObject?.type === "coordinates" ? "ai" : ""} value={formMethods.getValues(referenceSystems.LV03.fieldName.Y)} /> diff --git a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx index 1f75c29cc..164653bef 100644 --- a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx +++ b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx @@ -2,8 +2,7 @@ import { LabelingContext } from "./labelingContext.tsx"; import { useContext } from "react"; import { ReferenceSystemKey } from "../form/location/coordinateSegmentInterfaces.ts"; -// TODO: Extend with other types -export type ExtractionType = "coordinate"; +export type ExtractionType = "text" | "number" | "coordinates"; export type ExtractionState = "start" | "drawing" | "loading" | "success" | "error"; export interface ExtractionObject { @@ -23,7 +22,8 @@ export interface ExtractionBoundingBox { export interface ExtractionRequest { filename: string; page_number: number; - bounding_box: ExtractionBoundingBox; + bbox: ExtractionBoundingBox; + format: ExtractionType; } export interface Coordinate { @@ -34,6 +34,7 @@ export interface Coordinate { export interface ExtractionResponse { value: string | number | Coordinate | null; + detail?: string; bbox: ExtractionBoundingBox; } diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index 7081feac7..6edbffb3a 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -1,17 +1,11 @@ import { Box, Button, ButtonGroup, Stack, ToggleButton, ToggleButtonGroup, Typography } from "@mui/material"; -import { - ExtractionRequest, - ExtractionResponse, - labelingFileFormat, - PanelPosition, - useLabelingContext, -} from "./labelingInterfaces.tsx"; +import { ExtractionRequest, labelingFileFormat, PanelPosition, useLabelingContext } from "./labelingInterfaces.tsx"; import { ChevronLeft, ChevronRight, FileIcon, PanelBottom, PanelRight, Plus } from "lucide-react"; import { FC, MouseEvent, useCallback, useContext, useEffect, useRef, useState } from "react"; import { theme } from "../../../AppTheme.ts"; import { File as FileInterface, FileResponse, maxFileSizeKB } from "../../../api/file/fileInterfaces.ts"; import LabelingFileSelector from "./labelingFileSelector.tsx"; -import { getDataExtractionFileInfo, getFiles, loadImage, uploadFile } from "../../../api/file/file.ts"; +import { extractData, getDataExtractionFileInfo, getFiles, loadImage, uploadFile } from "../../../api/file/file.ts"; import { AlertContext } from "../../../components/alert/alertContext.tsx"; import { useTranslation } from "react-i18next"; import Map from "ol/Map.js"; @@ -30,7 +24,6 @@ import Draw, { createBox } from "ol/interaction/Draw"; import { Geometry } from "ol/geom"; import Feature from "ol/Feature"; import { DragRotate, PinchRotate } from "ol/interaction"; -import { ReferenceSystemKey } from "../form/location/coordinateSegmentInterfaces.ts"; interface LabelingPanelProps { boreholeId: number; @@ -139,32 +132,38 @@ const LabelingPanel: FC = ({ boreholeId }) => { }; }; - const extractData = useCallback( + const triggerDataExtraction = useCallback( (fileName: string, extent: number[]) => { - setExtractionObject({ - ...extractionObject, - state: "loading", - }); - - const bbox = convert2bbox(extent); - const request: ExtractionRequest = { - filename: fileName.substring(0, fileName.lastIndexOf("-")), - page_number: activePage, - bounding_box: bbox, - }; - // TODO: Send coordinates to labeling api to extract data - console.log("Request", request); - setTimeout(() => { - const response: ExtractionResponse = { - value: { east: 2600000 + extent[0], north: 1200000 + extent[1], projection: ReferenceSystemKey.LV95 }, - bbox: bbox, - }; + if (extractionObject && extractionObject.type) { setExtractionObject({ ...extractionObject, - state: "success", - result: response, + state: "loading", }); - }, 400); + + const bbox = convert2bbox(extent); + const request: ExtractionRequest = { + filename: fileName.substring(0, fileName.lastIndexOf("-")) + ".pdf", + page_number: activePage, + bbox: bbox, + format: extractionObject.type, + }; + + extractData(request) + .then(response => { + setExtractionObject({ + ...extractionObject, + state: "success", + result: response, + }); + }) + .catch(error => { + console.log("error", error); + setExtractionObject({ + ...extractionObject, + state: "error", + }); + }); + } }, [activePage, extractionObject, setExtractionObject], ); @@ -276,7 +275,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { drawingSource.on("addfeature", e => { const extent = e.feature?.getGeometry()?.getExtent(); if (extent) { - extractData(response.fileName, extent); + triggerDataExtraction(response.fileName, extent); } }); const drawingLayer = new VectorLayer({ @@ -312,7 +311,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { setMap(map); }); } - }, [activePage, extractData, pageCount, selectedFile]); + }, [activePage, triggerDataExtraction, pageCount, selectedFile]); return ( Date: Mon, 23 Sep 2024 10:49:12 +0200 Subject: [PATCH 03/39] Add cancellation option --- src/client/src/api/file/file.ts | 3 +- .../pages/detail/labeling/labelingPanel.tsx | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index 12f1d9ccf..d0292f1ea 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -93,12 +93,13 @@ export async function createExtractionPngs(fileName: string) { } } -export async function extractData(request: ExtractionRequest): Promise { +export async function extractData(request: ExtractionRequest, abortSignal: AbortSignal): Promise { // TODO: Maybe update URL after proper integration const response = await fetch("http://localhost:8000/api/V1/extract_data", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify(request), + signal: abortSignal, }); if (response.ok) { diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index b6b214f99..ea48b73ba 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -9,13 +9,7 @@ import { ToggleButtonGroup, Typography, } from "@mui/material"; -import { - ExtractionRequest, - ExtractionResponse, - labelingFileFormat, - PanelPosition, - useLabelingContext, -} from "./labelingInterfaces.tsx"; +import { ExtractionRequest, labelingFileFormat, PanelPosition, useLabelingContext } from "./labelingInterfaces.tsx"; import { ChevronLeft, ChevronRight, FileIcon, PanelBottom, PanelRight, Plus, X } from "lucide-react"; import { FC, MouseEvent, useCallback, useEffect, useRef, useState } from "react"; import { theme } from "../../../AppTheme.ts"; @@ -26,10 +20,9 @@ import { maxFileSizeKB, } from "../../../api/file/fileInterfaces.ts"; import LabelingFileSelector from "./labelingFileSelector.tsx"; -import { getDataExtractionFileInfo, getFiles, uploadFile } from "../../../api/file/file.ts"; +import { extractData, getDataExtractionFileInfo, getFiles, uploadFile } from "../../../api/file/file.ts"; import { useTranslation } from "react-i18next"; import { ButtonSelect } from "../../../components/buttons/buttonSelect.tsx"; -import { ReferenceSystemKey } from "../form/location/coordinateSegmentInterfaces.ts"; import { LabelingDrawContainer } from "./labelingDrawContainer.tsx"; import { useAlertManager } from "../../../components/alert/alertManager.tsx"; import { styled } from "@mui/system"; @@ -68,7 +61,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { const [fileInfo, setFileInfo] = useState(); const [activePage, setActivePage] = useState(1); const [drawTooltipLabel, setDrawTooltipLabel] = useState(); - const [requestTimeout, setRequestTimeout] = useState(); + const [abortController, setAbortController] = useState(); const fileInputRef = useRef(null); const { alertIsOpen, text, severity, autoHideDuration, showAlert, closeAlert } = useAlertManager(); @@ -115,9 +108,9 @@ const LabelingPanel: FC = ({ boreholeId }) => { fileInputRef.current?.click(); }; - const extractData = useCallback( + const triggerDataExtraction = useCallback( (extent: number[]) => { - if (fileInfo && extractionObject) { + if (fileInfo && extractionObject && extractionObject.type) { const bbox = { x0: extent[0], y0: extent[1], @@ -135,8 +128,9 @@ const LabelingPanel: FC = ({ boreholeId }) => { state: "loading", }); setDrawTooltipLabel(undefined); - // TODO: Add cancel option - extractData(request) + const abortController = new AbortController(); + setAbortController(abortController); + extractData(request, abortController.signal) .then(response => { setExtractionObject({ ...extractionObject, @@ -145,11 +139,16 @@ const LabelingPanel: FC = ({ boreholeId }) => { }); }) .catch(error => { - console.log("error", error); - setExtractionObject({ - ...extractionObject, - state: "error", - }); + if (!error?.message?.includes("AbortError")) { + setExtractionObject({ + ...extractionObject, + state: "error", + }); + showAlert(t(error.message), "error"); + } + }) + .finally(() => { + setAbortController(undefined); }); } }, @@ -157,7 +156,10 @@ const LabelingPanel: FC = ({ boreholeId }) => { ); const cancelRequest = () => { - clearTimeout(requestTimeout); + if (abortController) { + abortController.abort(); + setAbortController(undefined); + } setExtractionObject({ type: "coordinates", state: "start" }); }; @@ -341,7 +343,11 @@ const LabelingPanel: FC = ({ boreholeId }) => { }} /> - + ) : ( Date: Mon, 23 Sep 2024 11:48:25 +0200 Subject: [PATCH 04/39] Fix bbox --- .../src/pages/detail/labeling/labelingPanel.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index ea48b73ba..f952e97ac 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -112,10 +112,10 @@ const LabelingPanel: FC = ({ boreholeId }) => { (extent: number[]) => { if (fileInfo && extractionObject && extractionObject.type) { const bbox = { - x0: extent[0], - y0: extent[1], - x1: extent[2], - y1: extent[3], + x0: Math.min(...[extent[0], extent[2]]), + y0: Math.min(...[extent[1], extent[3]]), + x1: Math.max(...[extent[0], extent[2]]), + y1: Math.max(...[extent[1], extent[3]]), }; const request: ExtractionRequest = { filename: fileInfo.fileName.substring(0, fileInfo.fileName.lastIndexOf("-")) + ".pdf", @@ -144,7 +144,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { ...extractionObject, state: "error", }); - showAlert(t(error.message), "error"); + showAlert(error.message, "error"); } }) .finally(() => { @@ -152,7 +152,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { }); } }, - [activePage, extractionObject, fileInfo, setExtractionObject], + [activePage, extractionObject, fileInfo, setExtractionObject, showAlert], ); const cancelRequest = () => { From a5f1fab31551d458c13eefa2c16661a6a0497ab6 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 23 Sep 2024 12:06:27 +0200 Subject: [PATCH 05/39] Fix setting response values --- .../src/pages/detail/form/location/coordinatesSegment.tsx | 6 +++--- src/client/src/pages/detail/labeling/labelingInterfaces.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx index 756072b7f..c4eda842b 100644 --- a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx +++ b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx @@ -19,7 +19,7 @@ import { } from "./coordinateSegmentInterfaces.js"; import { boundingBox, referenceSystems, webApilv03tolv95, webApilv95tolv03 } from "./coordinateSegmentConstants.js"; import { LabelingButton } from "../../../../components/buttons/labelingButton.tsx"; -import { Coordinate, useLabelingContext } from "../../labeling/labelingInterfaces.js"; +import { useLabelingContext } from "../../labeling/labelingInterfaces.js"; import { FormSegmentBox } from "../../../../components/styledComponents.ts"; import { FormContainer, FormCoordinate, FormSelect } from "../../../../components/form/form"; import { Codelist } from "../../../../components/legacyComponents/domain/domainInterface.ts"; @@ -282,8 +282,8 @@ const CoordinatesSegment: React.FC = ({ ]); useEffect(() => { - if (extractionObject?.type === "coordinates" && extractionObject?.state === "success") { - const coordinate = extractionObject?.result?.value as Coordinate; + if (extractionObject?.type === "coordinates" && extractionObject.state === "success") { + const coordinate = extractionObject.result["coordinates"]; if (coordinate) { setCurrentReferenceSystem(referenceSystems[coordinate.projection].code); setValuesForReferenceSystem(coordinate.projection, coordinate.east.toString(), coordinate.north.toString()); diff --git a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx index 164653bef..063d2fd0c 100644 --- a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx +++ b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx @@ -33,9 +33,9 @@ export interface Coordinate { } export interface ExtractionResponse { - value: string | number | Coordinate | null; - detail?: string; bbox: ExtractionBoundingBox; + detail?: string; + [key in ExtractionType]?: string | number | Coordinate; } export type PanelPosition = "right" | "bottom"; From 93c47541e18f7a592de2b13e5e88019d0cca67c6 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 23 Sep 2024 13:30:57 +0200 Subject: [PATCH 06/39] Show correct error messages --- src/client/public/locale/de/common.json | 2 ++ src/client/public/locale/en/common.json | 2 ++ src/client/public/locale/fr/common.json | 2 ++ src/client/public/locale/it/common.json | 2 ++ src/client/src/api/file/file.ts | 11 ++++------- .../src/pages/detail/labeling/labelingPanel.tsx | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/client/public/locale/de/common.json b/src/client/public/locale/de/common.json index 8800ba73d..104670c9f 100644 --- a/src/client/public/locale/de/common.json +++ b/src/client/public/locale/de/common.json @@ -156,6 +156,8 @@ "era": "Ära", "errorAttention": "Achtung", "errorBoreholeFileLoading": "Beim Laden der Bohrungsdateien ist ein Fehler aufgetreten.", + "errorDataExtraction": "Die Datenextraktion ist fehlgeschlagen: {{message}}", + "errorDataExtractionFileLoading": "Beim Laden der Datenextraktionsdatei ist ein Fehler aufgetreten.", "errorDuplicatedUploadPerBorehole": "Diese Datei wurde bereits für diese Bohrung hochgeladen und kann nicht ein zweites Mal hochgeladen werden.", "errorDuringBoreholeFileUpload": "Beim Hochladen ist ein Fehler aufgetreten.", "errorEndPoint": "Sie sollten den Endpunkt hinzufügen", diff --git a/src/client/public/locale/en/common.json b/src/client/public/locale/en/common.json index c14d89cfd..35c2946a1 100644 --- a/src/client/public/locale/en/common.json +++ b/src/client/public/locale/en/common.json @@ -156,6 +156,8 @@ "era": "Era", "errorAttention": "Attention", "errorBoreholeFileLoading": "An error occurred while loading the borehole files.", + "errorDataExtraction": "The data extraction has failed: {{message}}", + "errorDataExtractionFileLoading": "An error occurred while loading the data extraction file.", "errorDuplicatedUploadPerBorehole": "This file has already been uploaded for this borehole and cannot be uploaded a second time.", "errorDuringBoreholeFileUpload": "An error occurred during the upload.", "errorEndPoint": "You should add an end point", diff --git a/src/client/public/locale/fr/common.json b/src/client/public/locale/fr/common.json index e60c61843..1cb5ffdea 100644 --- a/src/client/public/locale/fr/common.json +++ b/src/client/public/locale/fr/common.json @@ -156,6 +156,8 @@ "era": "Ere", "errorAttention": "Attention", "errorBoreholeFileLoading": "Une erreur s'est produite lors du chargement des fichiers de forage.", + "errorDataExtraction": "L'extraction des données a échoué: {{message}}", + "errorDataExtractionFileLoading": "Une erreur s'est produite lors du chargement du fichier d'extraction des données.", "errorDuplicatedUploadPerBorehole": "Ce fichier a déjà été téléchargé pour ce forage et ne peut pas être téléchargé une deuxième fois.", "errorDuringBoreholeFileUpload": "Une erreur s'est produite lors du téléchargement.", "errorEndPoint": "Veuillez ajouter la terminaison du forage", diff --git a/src/client/public/locale/it/common.json b/src/client/public/locale/it/common.json index 4b8e5f620..83e4dafc0 100644 --- a/src/client/public/locale/it/common.json +++ b/src/client/public/locale/it/common.json @@ -156,6 +156,8 @@ "era": "Era", "errorAttention": "Attenzione", "errorBoreholeFileLoading": "Si è verificato un errore durante il caricamento dei file di perforazione.", + "errorDataExtraction": "L'estrazione dei dati è fallita: {{message}}", + "errorDataExtractionFileLoading": "Si è verificato un errore durante il caricamento del file di estrazione dei dati.", "errorDuplicatedUploadPerBorehole": "Questo file è già stato caricato, per questa perforazione, e quindi non può essere caricato una seconda volta.", "errorDuringBoreholeFileUpload": "Si è verificato un errore durante il caricamento.", "errorEndPoint": "Per favore aggiungere il punto finale", diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index d0292f1ea..423a45243 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -103,15 +103,12 @@ export async function extractData(request: ExtractionRequest, abortSignal: Abort }); if (response.ok) { - const extractionResponse = (await response.json()) as ExtractionResponse; - console.log("extractionResponse", extractionResponse); - if (extractionResponse.detail) { - throw new ApiError(extractionResponse.detail, 500); + const responseObject = await response.json(); + if (responseObject.detail) { + throw new ApiError(responseObject.detail, 500); } - return extractionResponse; + return responseObject as ExtractionResponse; } else { - const text = await response.text(); - console.log(text); throw new ApiError("errorDataExtraction", response.status); } } diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index f952e97ac..b8a36dde3 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -144,7 +144,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { ...extractionObject, state: "error", }); - showAlert(error.message, "error"); + showAlert(t(error.message), "error"); } }) .finally(() => { From efb482c147800801b8509ed78ec9f4831d46c84e Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 23 Sep 2024 13:31:47 +0200 Subject: [PATCH 07/39] Fix setting result value --- .../detail/form/location/coordinatesSegment.tsx | 4 ++-- .../pages/detail/labeling/labelingInterfaces.tsx | 10 ++++------ .../src/pages/detail/labeling/labelingPanel.tsx | 14 ++++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx index c4eda842b..60993da69 100644 --- a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx +++ b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx @@ -19,7 +19,7 @@ import { } from "./coordinateSegmentInterfaces.js"; import { boundingBox, referenceSystems, webApilv03tolv95, webApilv95tolv03 } from "./coordinateSegmentConstants.js"; import { LabelingButton } from "../../../../components/buttons/labelingButton.tsx"; -import { useLabelingContext } from "../../labeling/labelingInterfaces.js"; +import { Coordinate, useLabelingContext } from "../../labeling/labelingInterfaces.js"; import { FormSegmentBox } from "../../../../components/styledComponents.ts"; import { FormContainer, FormCoordinate, FormSelect } from "../../../../components/form/form"; import { Codelist } from "../../../../components/legacyComponents/domain/domainInterface.ts"; @@ -283,7 +283,7 @@ const CoordinatesSegment: React.FC = ({ useEffect(() => { if (extractionObject?.type === "coordinates" && extractionObject.state === "success") { - const coordinate = extractionObject.result["coordinates"]; + const coordinate = extractionObject.value as Coordinate; if (coordinate) { setCurrentReferenceSystem(referenceSystems[coordinate.projection].code); setValuesForReferenceSystem(coordinate.projection, coordinate.east.toString(), coordinate.north.toString()); diff --git a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx index 063d2fd0c..3c0710cec 100644 --- a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx +++ b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx @@ -8,7 +8,7 @@ export type ExtractionState = "start" | "drawing" | "loading" | "success" | "err export interface ExtractionObject { type?: ExtractionType; state: ExtractionState; - result?: ExtractionResponse; + value?: string | number | Coordinate; previousValue?: string | number | Coordinate | null; } @@ -32,11 +32,9 @@ export interface Coordinate { projection: ReferenceSystemKey; } -export interface ExtractionResponse { - bbox: ExtractionBoundingBox; - detail?: string; - [key in ExtractionType]?: string | number | Coordinate; -} +export type ExtractionResponse = { + [key in ExtractionType]: string | number | Coordinate; +}; export type PanelPosition = "right" | "bottom"; diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index b8a36dde3..597638721 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -132,11 +132,13 @@ const LabelingPanel: FC = ({ boreholeId }) => { setAbortController(abortController); extractData(request, abortController.signal) .then(response => { - setExtractionObject({ - ...extractionObject, - state: "success", - result: response, - }); + if (extractionObject.type) { + setExtractionObject({ + ...extractionObject, + state: "success", + value: response[extractionObject.type], + }); + } }) .catch(error => { if (!error?.message?.includes("AbortError")) { @@ -152,7 +154,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { }); } }, - [activePage, extractionObject, fileInfo, setExtractionObject, showAlert], + [activePage, extractionObject, fileInfo, setExtractionObject, showAlert, t], ); const cancelRequest = () => { From a16406fad5beb5e7d1d23ff320a8cfa2dadfb882 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 23 Sep 2024 13:51:11 +0200 Subject: [PATCH 08/39] Fix warnings --- src/api/Controllers/BoreholeFileController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/Controllers/BoreholeFileController.cs b/src/api/Controllers/BoreholeFileController.cs index 8df06fa39..60c8f275c 100644 --- a/src/api/Controllers/BoreholeFileController.cs +++ b/src/api/Controllers/BoreholeFileController.cs @@ -128,7 +128,7 @@ public async Task GetDataExtractionFileInfo([Required, Range(1, i } catch (Exception ex) { - if (ex.Message.Contains("The specified key does not exist.")) + if (ex.Message.Contains("The specified key does not exist.", StringComparison.CurrentCulture)) { return Ok(new { @@ -137,10 +137,10 @@ public async Task GetDataExtractionFileInfo([Required, Range(1, i height = 0, count = 0, }); - } else + } + else { return Problem(ex.Message); - } } } From 2ab83c7bf3a3eb98781c065a164f787e965670fc Mon Sep 17 00:00:00 2001 From: tschumpr Date: Mon, 23 Sep 2024 16:59:54 +0200 Subject: [PATCH 09/39] Fix merge conflicts --- src/client/src/pages/detail/labeling/labelingPanel.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index 23718e03a..c9d5b02b6 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -11,15 +11,14 @@ import { } from "@mui/material"; import { ExtractionRequest, - ExtractionResponse, ExtractionState, labelingFileFormat, PanelPosition, useLabelingContext, } from "./labelingInterfaces.tsx"; -import { ExtractionRequest, labelingFileFormat, PanelPosition, useLabelingContext } from "./labelingInterfaces.tsx"; import { ChevronLeft, ChevronRight, FileIcon, PanelBottom, PanelRight, Plus, X } from "lucide-react"; import { FC, MouseEvent, useCallback, useEffect, useRef, useState } from "react"; + import { theme } from "../../../AppTheme.ts"; import { DataExtractionResponse, From 9bfe01576f00a4810307353a2ceef9cc641d6e63 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Tue, 24 Sep 2024 17:26:13 +0200 Subject: [PATCH 10/39] Fix merge conflict --- src/client/src/api/file/file.ts | 2 +- .../pages/detail/labeling/labelingPanel.tsx | 29 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index 24166fc8a..7614e0a3d 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -1,5 +1,5 @@ -import { ApiError } from "../apiInterfaces.ts"; import { ExtractionRequest, ExtractionResponse } from "../../pages/detail/labeling/labelingInterfaces.tsx"; +import { ApiError } from "../apiInterfaces.ts"; import { download, fetchApiV2, fetchApiV2Base, upload } from "../fetchApiV2"; import { DataExtractionResponse, maxFileSizeKB } from "./fileInterfaces.ts"; diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index c9d5b02b6..fbea5ec7e 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -1,3 +1,5 @@ +import { FC, MouseEvent, useCallback, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Alert, Box, @@ -9,30 +11,27 @@ import { ToggleButtonGroup, Typography, } from "@mui/material"; -import { - ExtractionRequest, - ExtractionState, - labelingFileFormat, - PanelPosition, - useLabelingContext, -} from "./labelingInterfaces.tsx"; +import { styled } from "@mui/system"; import { ChevronLeft, ChevronRight, FileIcon, PanelBottom, PanelRight, Plus, X } from "lucide-react"; -import { FC, MouseEvent, useCallback, useEffect, useRef, useState } from "react"; - -import { theme } from "../../../AppTheme.ts"; +import { extractData, getDataExtractionFileInfo, getFiles, uploadFile } from "../../../api/file/file.ts"; import { DataExtractionResponse, File as FileInterface, FileResponse, maxFileSizeKB, } from "../../../api/file/fileInterfaces.ts"; -import LabelingFileSelector from "./labelingFileSelector.tsx"; -import { extractData, getDataExtractionFileInfo, getFiles, uploadFile } from "../../../api/file/file.ts"; -import { useTranslation } from "react-i18next"; +import { theme } from "../../../AppTheme.ts"; +import { useAlertManager } from "../../../components/alert/alertManager.tsx"; import { ButtonSelect } from "../../../components/buttons/buttonSelect.tsx"; import { LabelingDrawContainer } from "./labelingDrawContainer.tsx"; -import { useAlertManager } from "../../../components/alert/alertManager.tsx"; -import { styled } from "@mui/system"; +import LabelingFileSelector from "./labelingFileSelector.tsx"; +import { + ExtractionRequest, + ExtractionState, + labelingFileFormat, + PanelPosition, + useLabelingContext, +} from "./labelingInterfaces.tsx"; import "./labelingPanel.css"; export const LabelingAlert = styled(Alert)({ From 842d914f4ab30df7f5d7f3ead83d8c26a5fb91ec Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 25 Sep 2024 12:34:10 +0200 Subject: [PATCH 11/39] Add test --- src/client/cypress/e2e/editor/labeling.cy.js | 222 ++++++++++++++++++ src/client/cypress/e2e/helpers/formHelpers.js | 88 ++++++- src/client/cypress/e2e/helpers/testHelpers.js | 26 ++ .../cypress/fixtures/labeling_attachment.pdf | Bin 0 -> 24066 bytes .../src/components/buttons/labelingButton.tsx | 3 +- .../src/components/buttons/mapControls.jsx | 1 + .../form/location/coordinatesSegment.tsx | 3 +- .../detail/labeling/labelingDrawContainer.tsx | 6 + .../pages/detail/labeling/labelingPanel.tsx | 21 +- 9 files changed, 359 insertions(+), 11 deletions(-) create mode 100644 src/client/cypress/fixtures/labeling_attachment.pdf diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index d39ac49a9..1a8d6bd3c 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -1,3 +1,5 @@ +import Draw from "ol/interaction/Draw"; +import { evaluateCoordinate, evaluateSelect, hasAiStyle, hasError, isDisabled } from "../helpers/formHelpers.js"; import { interceptShowLabelingCall, newEditableBorehole, @@ -91,5 +93,225 @@ describe("Test labeling tool", () => { cy.get('[data-cy="labeling-file-button-select"]').contains("WOLFHEART.pdf"); cy.get('[data-cy="button-select-popover"] .MuiListItem-root').eq(1).click(); cy.get('[data-cy="labeling-file-button-select"]').contains("borehole_attachment_3.pdf"); + + // Cannot draw if the panel was opened with the panel toggle button + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; + }); + }); + + it.skip("can extract data from image", () => { + newEditableBorehole().as("borehole_id"); + cy.get('[data-cy="labeling-toggle-button"]').click(); + cy.get('[data-cy="labeling-file-dropzone"]').should("exist"); + cy.get('[data-cy="labeling-file-selector"]').contains("No documents have been uploaded yet."); + + cy.get('[data-cy="labeling-file-dropzone"]').selectFile("cypress/fixtures/labeling_attachment.pdf", { + force: true, + mimeType: "application/pdf", + fileName: "labeling_attachment.pdf", + }); + + cy.wait("@get-borehole-files"); + cy.wait("@extraction-file-info"); + cy.wait("@dataextraction"); + cy.get('[data-cy="labeling-file-button-select"]').contains("labeling_attachment.pdf"); + cy.get('[data-cy="labeling-page-count"]').contains("1 / 3"); + cy.get('[data-cy="labeling-page-previous"]').should("be.disabled"); + cy.get('[data-cy="labeling-page-next"]').should("not.be.disabled"); + + cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); + cy.get('[data-cy="labeling-draw-tooltip"]').should("to.be.visible"); + cy.get('[data-cy="labeling-draw-tooltip"]').contains("Draw box around north & east coordinates"); + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; + }); + + evaluateSelect("spatial_reference_system", "20104001"); + hasAiStyle("spatial_reference_system"); + hasError("spatial_reference_system", false); + evaluateCoordinate("location_x", ""); + hasAiStyle("location_x"); + hasError("location_x", false); + evaluateCoordinate("location_y", ""); + hasAiStyle("location_y"); + hasError("location_y", false); + evaluateCoordinate("location_x_lv03", ""); + hasAiStyle("location_x_lv03"); + hasError("location_x_lv03", false); + isDisabled("location_x_lv03"); + evaluateCoordinate("location_y_lv03", ""); + hasAiStyle("location_y_lv03"); + hasError("location_y_lv03", false); + isDisabled("location_y_lv03"); + + // Can draw box around coordinates and extract correct coordinates + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 400, y: 60 }) + .trigger("pointerup", { x: 400, y: 60 }); + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 600, y: 170 }) + .trigger("pointerup", { x: 600, y: 170 }); + + cy.wait(1000); + cy.wait("@extract_data"); + cy.get('[data-cy="labeling-draw-tooltip"]').should("not.to.be.visible"); + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; + }); + + evaluateSelect("spatial_reference_system", "20104001"); + evaluateCoordinate("location_x", "2'646'359.70"); + hasError("location_x", false); + isDisabled("location_x", false); + evaluateCoordinate("location_y", "1'249'017.82"); + hasError("location_y", false); + isDisabled("location_y", false); + evaluateCoordinate("location_x_lv03", "646'359.70"); + hasError("location_x_lv03", false); + isDisabled("location_x_lv03", true); + evaluateCoordinate("location_y_lv03", "249'017.82"); + hasError("location_y_lv03", false); + isDisabled("location_y_lv03", true); + + // Can draw after navigating to the next page and extract correct bbox from rotated, zoomed image + cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); + cy.get('[data-cy="labeling-page-next"]').click(); + cy.wait("@extraction-file-info"); + cy.wait("@dataextraction"); + cy.wait(1000); + cy.get('[data-cy="labeling-page-count"]').contains("2 / 3"); + cy.get('[data-cy="labeling-page-previous"]').should("not.be.disabled"); + cy.get('[data-cy="labeling-page-next"]').should("not.be.disabled"); + + cy.window().then(win => { + const view = win.labelingImage.getView(); + expect(view.getRotation()).to.equal(0); + }); + cy.get('[data-cy="rotate-button"]').click(); + cy.window().then(win => { + const view = win.labelingImage.getView(); + expect(view.getRotation()).to.equal(Math.PI / 2); + }); + cy.wait(1000); + + cy.get('[data-cy="labeling-panel"] [data-cy="zoom-in-button"]').click(); + cy.wait(1000); + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 600, y: 60 }) + .trigger("pointerup", { x: 600, y: 200 }); + cy.wait(1000); + + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; + }); + + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 600, y: 60 }) + .trigger("pointerup", { x: 600, y: 60 }); + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 800, y: 170 }) + .trigger("pointerup", { x: 800, y: 170 }); + + cy.wait(1000); + cy.wait("@extract_data"); + + evaluateSelect("spatial_reference_system", "20104002"); + evaluateCoordinate("location_x", "2'646'465.97"); + hasError("location_x", false); + isDisabled("location_x", true); + evaluateCoordinate("location_y", "1'249'931.66"); + hasError("location_y", false); + isDisabled("location_y", true); + evaluateCoordinate("location_x_lv03", "646'465.97"); + hasError("location_x_lv03", false); + isDisabled("location_x_lv03", false); + evaluateCoordinate("location_y_lv03", "249'931.66"); + hasError("location_y_lv03", false); + isDisabled("location_y_lv03", false); + + // Shows alert if no coordinates are extracted + cy.get('[data-cy="labeling-page-next"]').click(); + cy.wait("@extraction-file-info"); + cy.wait("@dataextraction"); + cy.get('[data-cy="labeling-page-count"]').contains("3 / 3"); + cy.get('[data-cy="labeling-page-previous"]').should("not.be.disabled"); + cy.get('[data-cy="labeling-page-next"]').should("be.disabled"); + cy.wait(1000); + + cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; + }); + cy.wait(1000); + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 50, y: 60 }) + .trigger("pointerup", { x: 50, y: 60 }); + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 400, y: 110 }) + .trigger("pointerup", { x: 400, y: 110 }); + cy.wait(1000); + cy.wait("@extract_data"); + + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; + }); + + evaluateSelect("spatial_reference_system", "20104002"); + evaluateCoordinate("location_x", "2'646'465.97"); + evaluateCoordinate("location_y", "1'249'931.66"); + evaluateCoordinate("location_x_lv03", "646'465.97"); + evaluateCoordinate("location_y_lv03", "249'931.66"); + + cy.get('[data-cy="labeling-alert"]').contains("Coordinates not found."); + + // Drawing is active immediately when opening the panel with the labeling-button + cy.get('[data-cy="labeling-toggle-button"]').click(); + cy.get('[data-cy="labeling-panel"]').should("not.exist"); + + cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); + cy.get('[data-cy="labeling-file-dropzone"]').should("exist"); + cy.contains("labeling_attachment.pdf").click(); + cy.wait("@extraction-file-info"); + cy.wait("@dataextraction"); + cy.wait(1000); + + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; + }); + + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 400, y: 60 }) + .trigger("pointerup", { x: 400, y: 60 }); + cy.get('[data-cy="labeling-panel"]') + .trigger("pointerdown", { x: 600, y: 170 }) + .trigger("pointerup", { x: 600, y: 170 }); + cy.wait(1000); + cy.wait("@extract_data"); + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; + }); + + evaluateSelect("spatial_reference_system", "20104001"); + evaluateCoordinate("location_x", "2'646'359.70"); + hasError("location_x", false); + isDisabled("location_x", false); + evaluateCoordinate("location_y", "1'249'017.82"); + hasError("location_y", false); + isDisabled("location_y", false); + evaluateCoordinate("location_x_lv03", "646'359.70"); + hasError("location_x_lv03", false); + isDisabled("location_x_lv03", true); + evaluateCoordinate("location_y_lv03", "249'017.82"); + hasError("location_y_lv03", false); + isDisabled("location_y_lv03", true); }); }); diff --git a/src/client/cypress/e2e/helpers/formHelpers.js b/src/client/cypress/e2e/helpers/formHelpers.js index 2fa76f1a6..5b0c5fa6b 100644 --- a/src/client/cypress/e2e/helpers/formHelpers.js +++ b/src/client/cypress/e2e/helpers/formHelpers.js @@ -1,9 +1,54 @@ import { createBaseSelector } from "./testHelpers"; +/** + * Checks if a form element has an error. + * @param {string} fieldName The name of the form element. + * @param {boolean} hasError The expected error state. + * @param {string} parent (optional) The parent of the form element. + */ +export const hasError = (fieldName, hasError = true, parent) => { + const selector = createBaseSelector(parent) + `[data-cy^="${fieldName}-form"] .Mui-error`; + if (hasError) { + cy.get(selector).should("exist"); + } else { + cy.get(selector).should("not.exist"); + } +}; + +/** + * Checks if a form element is disabled. + * @param {string} fieldName The name of the form element. + * @param {boolean} isDisabled The expected disabled state. + * @param {string} parent (optional) The parent of the form element. + */ +export const isDisabled = (fieldName, isDisabled = true, parent) => { + const selector = createBaseSelector(parent) + `[data-cy^="${fieldName}-form"] .Mui-disabled`; + if (isDisabled) { + cy.get(selector).should("exist"); + } else { + cy.get(selector).should("not.exist"); + } +}; + +/** + * Checks if a form element has the AI styling. + * @param {string} fieldName The name of the form element. + * @param {boolean} hasAiStyle The expected ai state. + * @param {string} parent (optional) The parent of the form element. + */ +export const hasAiStyle = (fieldName, hasAiStyle = true, parent) => { + const selector = createBaseSelector(parent) + `[data-cy^="${fieldName}-form"].ai`; + if (hasAiStyle) { + cy.get(selector).should("exist"); + } else { + cy.get(selector).should("not.exist"); + } +}; + /** * Sets the value for an input form element. * @param {string} fieldName The name of the input field. - * @param {string} text The text to type into the input field. + * @param {string} value The text to type into the input field. * @param {string} parent (optional) The parent of the form element. */ export const setInput = (fieldName, value, parent) => { @@ -20,8 +65,8 @@ export const setInput = (fieldName, value, parent) => { /** * Evaluates the state of an input form element - * @param {string} fieldName The name of the select field. - * @param {number} expectedValue The expected number of options in the dropdown. + * @param {string} fieldName The name of the input field. + * @param {number} expectedValue The expected value. * @param {string} parent (optional) The parent of the form element. */ export const evaluateInput = (fieldName, expectedValue, parent) => { @@ -35,8 +80,8 @@ export const evaluateInput = (fieldName, expectedValue, parent) => { /** * Evaluates the state of a textarea form element - * @param {string} fieldName The name of the select field. - * @param {number} expectedValue The expected number of options in the dropdown. + * @param {string} fieldName The name of the input field. + * @param {number} expectedValue The expected value. * @param {string} parent (optional) The parent of the form element. */ export const evaluateTextarea = (fieldName, expectedValue, parent) => { @@ -188,3 +233,36 @@ export const evaluateDisplayValue = (fieldName, expectedValue, parent) => { cy.get(selector).contains(expectedValue); } }; + +/** + * Sets the value for an coordinate form element. + * @param {string} fieldName The name of the coordinate field. + * @param {string} value The text to type into the coordinate field. + * @param {string} parent (optional) The parent of the form element. + */ +export const setCoordinate = (fieldName, value, parent) => { + var selector = createBaseSelector(parent) + `[data-cy="${fieldName}-formCoordinate"]`; + cy.get(selector) + .click() + .then(() => { + cy.focused().clear(); + cy.get(selector).type(value, { + delay: 10, + }); + }); +}; + +/** + * Evaluates the state of an coordinate form element + * @param {string} fieldName The name of the coordinate field. + * @param {number} expectedValue The expected value. + * @param {string} parent (optional) The parent of the form element. + */ +export const evaluateCoordinate = (fieldName, expectedValue, parent) => { + var selector = createBaseSelector(parent) + `[data-cy="${fieldName}-formCoordinate"] input`; + cy.get(selector) + .filter((k, input) => { + return input.value === expectedValue; + }) + .should("have.length", 1); +}; diff --git a/src/client/cypress/e2e/helpers/testHelpers.js b/src/client/cypress/e2e/helpers/testHelpers.js index 364f4195e..b67c0117d 100644 --- a/src/client/cypress/e2e/helpers/testHelpers.js +++ b/src/client/cypress/e2e/helpers/testHelpers.js @@ -98,6 +98,16 @@ export const interceptApiCalls = () => { }); cy.intercept("/api/v2/boreholefile/getAllForBorehole?boreholeId=**").as("get-borehole-files"); + cy.intercept("/api/v2/boreholefile/getDataExtractionFileInfo*").as("extraction-file-info"); + cy.intercept({ + method: "GET", + url: "/api/v2/boreholefile/dataextraction/*", + }).as("dataextraction"); + cy.intercept({ + method: "GET", + url: "/api/v2/boreholefile/dataextraction/*", + }).as("dataextraction"); + cy.intercept("http://localhost:8000/api/V1/extract_data").as("extract_data"); }; /** @@ -202,6 +212,22 @@ const waitForCreation = () => { }); }; +const waitForFileUpload = () => { + return cy.wait(["@get-borehole-files"]).then(interception => { + cy.task("log", "Uploaded new labeling file: " + interception.response.body); + const body = interception.response.body; + return cy.wrap(body[0].fileId); + }); +}; + +export const newLabelingAttachment = filePath => { + cy.get('[data-cy="labeling-file-dropzone"]').attachFile(filePath, { + subjectType: "drag-n-drop", + }); + const fileId = waitForFileUpload(); + return fileId; +}; + export const createBorehole = values => { return cy.get("@id_token").then(token => cy diff --git a/src/client/cypress/fixtures/labeling_attachment.pdf b/src/client/cypress/fixtures/labeling_attachment.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a82f1d97d8420a1d18bf1977d7c8e21391c4a063 GIT binary patch literal 24066 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 b$vUB>)Sy>TmHkub|21O^3(%48AV&iLn|d(L literal 0 HcmV?d00001 diff --git a/src/client/src/components/buttons/labelingButton.tsx b/src/client/src/components/buttons/labelingButton.tsx index e6a29581c..8043e4440 100644 --- a/src/client/src/components/buttons/labelingButton.tsx +++ b/src/client/src/components/buttons/labelingButton.tsx @@ -13,7 +13,8 @@ export const LabelingButton = forwardRef((props, color="ai" sx={{ borderRadius: "4px", - }}> + }} + data-cy="labeling-button"> diff --git a/src/client/src/components/buttons/mapControls.jsx b/src/client/src/components/buttons/mapControls.jsx index a821b6f87..1bab81ff5 100644 --- a/src/client/src/components/buttons/mapControls.jsx +++ b/src/client/src/components/buttons/mapControls.jsx @@ -26,6 +26,7 @@ const MapControls = ({ onZoomIn, onZoomOut, onFitToExtent, onRotate }) => { {!!onRotate && ( @@ -279,7 +287,12 @@ const LabelingPanel: FC = ({ boreholeId }) => { )} {alertIsOpen ? ( - + {text} ) : ( From f5c3584f1d7478491f747412037d81a7b1011014 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Wed, 25 Sep 2024 16:22:04 +0200 Subject: [PATCH 12/39] Update test --- src/client/cypress/e2e/editor/labeling.cy.js | 173 +++++++----------- src/client/cypress/e2e/helpers/testHelpers.js | 20 +- 2 files changed, 66 insertions(+), 127 deletions(-) diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index 1a8d6bd3c..e277859a4 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -16,6 +16,45 @@ const isFileActive = (fileName, isActive) => { .should(isActive ? "exist" : "not.exist"); }; +const drawBox = (x1, y1, x2, y2) => { + cy.get('[data-cy="labeling-draw-tooltip"]').should("to.be.visible"); + cy.get('[data-cy="labeling-draw-tooltip"]').contains("Draw box around north & east coordinates"); + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect( + interactions.some(interaction => { + return interaction.constructor.name === "Draw"; + }), + ).to.be.true; + }); + cy.get('[data-cy="labeling-panel"]').trigger("pointerdown", { x: x1, y: y1 }).trigger("pointerup", { x: x1, y: y1 }); + cy.get('[data-cy="labeling-panel"]').trigger("pointerdown", { x: x2, y: y2 }).trigger("pointerup", { x: x2, y: y2 }); + + cy.wait("@extract-data"); + cy.get('[data-cy="labeling-draw-tooltip"]').should("not.to.be.visible"); + cy.window().then(win => { + const interactions = win.labelingImage.getInteractions().getArray(); + expect( + interactions.some(interaction => { + return interaction.constructor.name === "Draw"; + }), + ).to.be.false; + }); +}; + +const waitForLabelingImageLoaded = () => { + cy.wait("@extraction-file-info"); + cy.wait("@load-extraction-file"); + cy.window().then(win => { + const layers = win.labelingImage.getLayers().getArray(); + expect( + layers.some(layer => { + return layer.constructor.name === "ImageLayer"; + }), + ).to.be.true; + }); +}; + describe("Test labeling tool", () => { beforeEach(() => { interceptShowLabelingCall(); @@ -114,21 +153,13 @@ describe("Test labeling tool", () => { }); cy.wait("@get-borehole-files"); - cy.wait("@extraction-file-info"); - cy.wait("@dataextraction"); + waitForLabelingImageLoaded(); cy.get('[data-cy="labeling-file-button-select"]').contains("labeling_attachment.pdf"); cy.get('[data-cy="labeling-page-count"]').contains("1 / 3"); cy.get('[data-cy="labeling-page-previous"]').should("be.disabled"); cy.get('[data-cy="labeling-page-next"]').should("not.be.disabled"); cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); - cy.get('[data-cy="labeling-draw-tooltip"]').should("to.be.visible"); - cy.get('[data-cy="labeling-draw-tooltip"]').contains("Draw box around north & east coordinates"); - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; - }); - evaluateSelect("spatial_reference_system", "20104001"); hasAiStyle("spatial_reference_system"); hasError("spatial_reference_system", false); @@ -148,41 +179,26 @@ describe("Test labeling tool", () => { isDisabled("location_y_lv03"); // Can draw box around coordinates and extract correct coordinates - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 400, y: 60 }) - .trigger("pointerup", { x: 400, y: 60 }); - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 600, y: 170 }) - .trigger("pointerup", { x: 600, y: 170 }); - - cy.wait(1000); - cy.wait("@extract_data"); - cy.get('[data-cy="labeling-draw-tooltip"]').should("not.to.be.visible"); - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; - }); - + drawBox(400, 60, 600, 170); + // TODO: Update coordinates once api returns coordinates as floats evaluateSelect("spatial_reference_system", "20104001"); - evaluateCoordinate("location_x", "2'646'359.70"); + evaluateCoordinate("location_x", "2'646'359"); hasError("location_x", false); isDisabled("location_x", false); - evaluateCoordinate("location_y", "1'249'017.82"); + evaluateCoordinate("location_y", "1'249'017"); hasError("location_y", false); isDisabled("location_y", false); - evaluateCoordinate("location_x_lv03", "646'359.70"); + evaluateCoordinate("location_x_lv03", "646'358"); hasError("location_x_lv03", false); isDisabled("location_x_lv03", true); - evaluateCoordinate("location_y_lv03", "249'017.82"); + evaluateCoordinate("location_y_lv03", "249'017"); hasError("location_y_lv03", false); isDisabled("location_y_lv03", true); // Can draw after navigating to the next page and extract correct bbox from rotated, zoomed image cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); cy.get('[data-cy="labeling-page-next"]').click(); - cy.wait("@extraction-file-info"); - cy.wait("@dataextraction"); - cy.wait(1000); + waitForLabelingImageLoaded(); cy.get('[data-cy="labeling-page-count"]').contains("2 / 3"); cy.get('[data-cy="labeling-page-previous"]').should("not.be.disabled"); cy.get('[data-cy="labeling-page-next"]').should("not.be.disabled"); @@ -205,69 +221,36 @@ describe("Test labeling tool", () => { .trigger("pointerup", { x: 600, y: 200 }); cy.wait(1000); - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; - }); - - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 600, y: 60 }) - .trigger("pointerup", { x: 600, y: 60 }); - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 800, y: 170 }) - .trigger("pointerup", { x: 800, y: 170 }); - - cy.wait(1000); - cy.wait("@extract_data"); - + drawBox(600, 60, 800, 170); evaluateSelect("spatial_reference_system", "20104002"); - evaluateCoordinate("location_x", "2'646'465.97"); + evaluateCoordinate("location_x", "2'646'466"); hasError("location_x", false); isDisabled("location_x", true); - evaluateCoordinate("location_y", "1'249'931.66"); + evaluateCoordinate("location_y", "1'249'931"); hasError("location_y", false); isDisabled("location_y", true); - evaluateCoordinate("location_x_lv03", "646'465.97"); + evaluateCoordinate("location_x_lv03", "646'465"); hasError("location_x_lv03", false); isDisabled("location_x_lv03", false); - evaluateCoordinate("location_y_lv03", "249'931.66"); + evaluateCoordinate("location_y_lv03", "249'931"); hasError("location_y_lv03", false); isDisabled("location_y_lv03", false); // Shows alert if no coordinates are extracted cy.get('[data-cy="labeling-page-next"]').click(); - cy.wait("@extraction-file-info"); - cy.wait("@dataextraction"); + waitForLabelingImageLoaded(); cy.get('[data-cy="labeling-page-count"]').contains("3 / 3"); cy.get('[data-cy="labeling-page-previous"]').should("not.be.disabled"); cy.get('[data-cy="labeling-page-next"]').should("be.disabled"); cy.wait(1000); cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; - }); - cy.wait(1000); - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 50, y: 60 }) - .trigger("pointerup", { x: 50, y: 60 }); - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 400, y: 110 }) - .trigger("pointerup", { x: 400, y: 110 }); - cy.wait(1000); - cy.wait("@extract_data"); - - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; - }); - + drawBox(50, 60, 400, 110); evaluateSelect("spatial_reference_system", "20104002"); - evaluateCoordinate("location_x", "2'646'465.97"); - evaluateCoordinate("location_y", "1'249'931.66"); - evaluateCoordinate("location_x_lv03", "646'465.97"); - evaluateCoordinate("location_y_lv03", "249'931.66"); + evaluateCoordinate("location_x", "2'646'466"); + evaluateCoordinate("location_y", "1'249'931"); + evaluateCoordinate("location_x_lv03", "646'465"); + evaluateCoordinate("location_y_lv03", "249'931"); cy.get('[data-cy="labeling-alert"]').contains("Coordinates not found."); @@ -278,40 +261,12 @@ describe("Test labeling tool", () => { cy.get('[data-cy="coordinate-segment"] [data-cy="labeling-button"]').click(); cy.get('[data-cy="labeling-file-dropzone"]').should("exist"); cy.contains("labeling_attachment.pdf").click(); - cy.wait("@extraction-file-info"); - cy.wait("@dataextraction"); - cy.wait(1000); - - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.true; - }); - - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 400, y: 60 }) - .trigger("pointerup", { x: 400, y: 60 }); - cy.get('[data-cy="labeling-panel"]') - .trigger("pointerdown", { x: 600, y: 170 }) - .trigger("pointerup", { x: 600, y: 170 }); - cy.wait(1000); - cy.wait("@extract_data"); - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; - }); - + waitForLabelingImageLoaded(); + drawBox(400, 60, 600, 170); evaluateSelect("spatial_reference_system", "20104001"); - evaluateCoordinate("location_x", "2'646'359.70"); - hasError("location_x", false); - isDisabled("location_x", false); - evaluateCoordinate("location_y", "1'249'017.82"); - hasError("location_y", false); - isDisabled("location_y", false); - evaluateCoordinate("location_x_lv03", "646'359.70"); - hasError("location_x_lv03", false); - isDisabled("location_x_lv03", true); - evaluateCoordinate("location_y_lv03", "249'017.82"); - hasError("location_y_lv03", false); - isDisabled("location_y_lv03", true); + evaluateCoordinate("location_x", "2'646'359"); + evaluateCoordinate("location_y", "1'249'017"); + evaluateCoordinate("location_x_lv03", "646'358"); + evaluateCoordinate("location_y_lv03", "249'017"); }); }); diff --git a/src/client/cypress/e2e/helpers/testHelpers.js b/src/client/cypress/e2e/helpers/testHelpers.js index b67c0117d..3ad2fbce6 100644 --- a/src/client/cypress/e2e/helpers/testHelpers.js +++ b/src/client/cypress/e2e/helpers/testHelpers.js @@ -106,8 +106,8 @@ export const interceptApiCalls = () => { cy.intercept({ method: "GET", url: "/api/v2/boreholefile/dataextraction/*", - }).as("dataextraction"); - cy.intercept("http://localhost:8000/api/V1/extract_data").as("extract_data"); + }).as("load-extraction-file"); + cy.intercept("http://localhost:8000/api/V1/extract_data").as("extract-data"); }; /** @@ -212,22 +212,6 @@ const waitForCreation = () => { }); }; -const waitForFileUpload = () => { - return cy.wait(["@get-borehole-files"]).then(interception => { - cy.task("log", "Uploaded new labeling file: " + interception.response.body); - const body = interception.response.body; - return cy.wrap(body[0].fileId); - }); -}; - -export const newLabelingAttachment = filePath => { - cy.get('[data-cy="labeling-file-dropzone"]').attachFile(filePath, { - subjectType: "drag-n-drop", - }); - const fileId = waitForFileUpload(); - return fileId; -}; - export const createBorehole = values => { return cy.get("@id_token").then(token => cy From f6d3ac44191fb9831e52a1ee6824bbd050cee2ac Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 09:03:42 +0200 Subject: [PATCH 13/39] Add infos --- src/client/cypress/e2e/editor/labeling.cy.js | 1 + .../cypress/fixtures/labeling_attachment.pdf | Bin 24066 -> 24070 bytes src/client/src/api/file/file.ts | 6 +++--- .../pages/detail/labeling/labelingPanel.tsx | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index e277859a4..9cd2e425f 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -140,6 +140,7 @@ describe("Test labeling tool", () => { }); }); + // We have to wait for the docker integration before this test can be enabled it.skip("can extract data from image", () => { newEditableBorehole().as("borehole_id"); cy.get('[data-cy="labeling-toggle-button"]').click(); diff --git a/src/client/cypress/fixtures/labeling_attachment.pdf b/src/client/cypress/fixtures/labeling_attachment.pdf index a82f1d97d8420a1d18bf1977d7c8e21391c4a063..fc79f6dffa707d94ce53e0c5e5195a877440016b 100644 GIT binary patch delta 1359 zcmZqL!`QZmal-?ydJ8){UasPjqSVA(UapEcTd$waYIYD|d+_(bp2Lx9tjBj&vo&?c zWM+!1PSbM|U$`VPw5qcHMUSV~>?6nc`V^E-d|0^udH9Lx`LCti7XQgBvSpojf8A?c z9<$PK8)T-b_AD2!Sl(hRXLiJif&DR0uuuT^GnJYgjmkYuvnCa~de+-bS5OJjpVH!O z$)Tb0ILJA1LL~3i)%!CxipXr6Uzd2}o!`-aC5|Vv7!Iqi$kUklll8r3tn`gVTXrzt=DVQsR^fud@~8!Kn{K+>ZehFZFJ5l*xvxp{+rcA!2j48Vh-wa~ zJh%JT$2m{!X6#|!(r=W>njoOiOWM^5#c}H!JsO;wtx=}Fu-@#u@p6!2LOb&1r-Mo*-h*{Ijz{1ed)L7fV zNZr6dT~k5dH$TNCu_RT)#mdOQ$i%?X2&!VUq(HqTmsyB`g_WVDm8tRMMV0AV;2`EM?(t}3y^6@hPl|;5L6MXF!{Wg^yKfsLiLv{!+^>X`NtCAkA zt;wAGn=T8*6d#=1YW9ait}Bi?X9lNTRh_UM>tknw{Vfl?IC5FC*Ob3{YiZh`c*CH{ z&f;PHgTQ|Gd*53IR0>y8F(4M$0>0?yJEPQ2?LvgIvg{9eG$f1yR}%L3l>7aG^ug*LsP z7Q?L{{P3%+*aux7Ew8_f;!hRpz){Qjk zr6a*uCX@~)>&1#QnoM?!b*`k?EEl_21^s}c)G`GlU@(~IM+Bv&1()Okc|hkv3d+Qy Xl8U0#G+r(vV+%_|UM^KtSARDEGCSG= delta 1353 zcmZqM!`QTkal-?ydJ{W4UasPjqSVA(UapEcTd(_OHycQ>f7m_Y{Rf%bjfVdBd=^x^ z(3ZZPcx9vW&4oSdmYDd$F*c($cb!=)6+&^qSGSmV%7(us+=t7`yMAA0JsHL_WQXu4Y*sWi?R#$k58gJ=w>}=7q@oKW*#^x;aHlzFu2!@u4Y?<9^1tp4IlTyuFlP%x&W@iFXo7J2N6n zk2KxU)x9fI9oIV};MChiwq8@Nrsm5R|38xbcMpF>^wJe6-D``U_Qx)$4LQguGBNl! zdx_QhqKB<%?AK%$c-~^%xip4-!Q7^s?zSw=FPC-O&Z~4kc%;_A_jtm$K$(LH^FFWh zsk2Gn$FKI9;q79drh`0=`yrgL#8E5(}-iN!`z(tl3@F zTPgon$w}wwyRbXXKA(Oc2>gBf3;#9ool1XBY+lRb&dhFVU}UTKJYQb9bOkeZUY?aBL zf=f$sw)4$Z_WJvgRr+8i_qv%6&N;A$HMK9h-oRMHWtUbbypJ_mYlgj3fo4SZ!Mf|_ zU%#$-by%6_0cX{NmjA8kd*$Tb*K4y^9AM4+&?wIH!I{B_!%;woqnTH~fI+b1km3+2yCbb{0Z++-dG5dgw*dcevm9k&*6oYuVQu9(^nGwj$Puk2K+Q6u3q+kF7 z3VFO-V1|LQsfh)Kn5Ch~<}G1mto0U_=yIkOM&@W@hUVsGrs!f82BwCXV&>>}8Cn<` zpzAd|zxlWmRHPNkvg=8ZVa-&<_Tay<(j! osbac*Kv8O$0@zUfh@jN8;F4S*59k4OYmF@|%z3#~RbBnv00GzAP5=M^ diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index 7614e0a3d..482385f06 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -104,9 +104,9 @@ export async function extractData(request: ExtractionRequest, abortSignal: Abort if (response.ok) { const responseObject = await response.json(); - if (responseObject.detail) { - throw new ApiError(responseObject.detail, 500); - } + // if (responseObject.detail) { + // throw new ApiError(responseObject.detail, 500); + // } return responseObject as ExtractionResponse; } else { throw new ApiError("errorDataExtraction", response.status); diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index 9b3d100da..f4c76f876 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -150,6 +150,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { ...extractionObject, state: ExtractionState.error, }); + // TODO: Check if error message is correct, resp. handle all error cases with different messages showAlert(t(error.message), "error"); } }) From 775bc4e2ddab077fe3fcebb73e493a6b1e4f0a1f Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 09:20:10 +0200 Subject: [PATCH 14/39] Remove extraction image --- docker-compose.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3d524e4ee..b293b7e2b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,17 +12,6 @@ services: ports: - 9000:9000 - 9002:9002 - dataextraction: - image: borehole-api - restart: unless-stopped - ports: - - 8000:8000 - environment: - AWS_ENDPOINT: http://minio:9000 - AWS_ACCESS_KEY_ID: REDSQUIRREL - AWS_SECRET_ACCESS_KEY: YELLOWMONKEY - AWS_S3_BUCKET: "cannonflea" - db: image: postgis/postgis:15-3.4-alpine restart: unless-stopped From 15dee2ccc19ed97d6517c4bf6b4c32915d33a83f Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 09:29:25 +0200 Subject: [PATCH 15/39] Clarify TODOs --- src/client/cypress/e2e/editor/labeling.cy.js | 6 ++++-- src/client/cypress/e2e/helpers/testHelpers.js | 2 ++ src/client/src/api/file/file.ts | 14 +++++++++----- .../detail/form/location/coordinatesSegment.tsx | 1 - .../src/pages/detail/labeling/labelingPanel.tsx | 3 ++- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index 9cd2e425f..32d28d5e8 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -140,7 +140,8 @@ describe("Test labeling tool", () => { }); }); - // We have to wait for the docker integration before this test can be enabled + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 & https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1545 + // We have to wait for the docker integration before this test can be enabled it.skip("can extract data from image", () => { newEditableBorehole().as("borehole_id"); cy.get('[data-cy="labeling-toggle-button"]').click(); @@ -181,7 +182,8 @@ describe("Test labeling tool", () => { // Can draw box around coordinates and extract correct coordinates drawBox(400, 60, 600, 170); - // TODO: Update coordinates once api returns coordinates as floats + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // Update all coordinates once api returns coordinates as floats evaluateSelect("spatial_reference_system", "20104001"); evaluateCoordinate("location_x", "2'646'359"); hasError("location_x", false); diff --git a/src/client/cypress/e2e/helpers/testHelpers.js b/src/client/cypress/e2e/helpers/testHelpers.js index 3ad2fbce6..a8afa73c7 100644 --- a/src/client/cypress/e2e/helpers/testHelpers.js +++ b/src/client/cypress/e2e/helpers/testHelpers.js @@ -107,6 +107,8 @@ export const interceptApiCalls = () => { method: "GET", url: "/api/v2/boreholefile/dataextraction/*", }).as("load-extraction-file"); + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // Check if path is correct cy.intercept("http://localhost:8000/api/V1/extract_data").as("extract-data"); }; diff --git a/src/client/src/api/file/file.ts b/src/client/src/api/file/file.ts index 482385f06..3e5dabe0d 100644 --- a/src/client/src/api/file/file.ts +++ b/src/client/src/api/file/file.ts @@ -82,7 +82,8 @@ export async function loadImage(fileName: string) { } export async function createExtractionPngs(fileName: string) { - // TODO: Maybe update URL after proper integration + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // Maybe update URL after proper integration const response = await fetch("http://localhost:8000/api/V1/create_pngs", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -94,7 +95,8 @@ export async function createExtractionPngs(fileName: string) { } export async function extractData(request: ExtractionRequest, abortSignal: AbortSignal): Promise { - // TODO: Maybe update URL after proper integration + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // Maybe update URL after proper integration const response = await fetch("http://localhost:8000/api/V1/extract_data", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, @@ -104,9 +106,11 @@ export async function extractData(request: ExtractionRequest, abortSignal: Abort if (response.ok) { const responseObject = await response.json(); - // if (responseObject.detail) { - // throw new ApiError(responseObject.detail, 500); - // } + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // "Coordinate not found" Errors should be returned as 404 and handled in the frontend + if (responseObject.detail) { + throw new ApiError(responseObject.detail, 500); + } return responseObject as ExtractionResponse; } else { throw new ApiError("errorDataExtraction", response.status); diff --git a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx index de785945d..f6a94ea5b 100644 --- a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx +++ b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx @@ -283,7 +283,6 @@ const CoordinatesSegment: React.FC = ({ useEffect(() => { if (extractionObject?.type === "coordinates" && extractionObject.state === ExtractionState.success) { - console.log("updateCoordinates: ", extractionObject); const coordinate = extractionObject.value as Coordinate; if (coordinate) { setCurrentReferenceSystem(referenceSystems[coordinate.projection].code); diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index f4c76f876..0a034e996 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -150,7 +150,8 @@ const LabelingPanel: FC = ({ boreholeId }) => { ...extractionObject, state: ExtractionState.error, }); - // TODO: Check if error message is correct, resp. handle all error cases with different messages + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // Check if error message is correct, resp. handle all error cases with different messages showAlert(t(error.message), "error"); } }) From f9c59319ada7fd7a0271ec8753a3615057a71d42 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 09:53:26 +0200 Subject: [PATCH 16/39] Fix check --- src/client/cypress/e2e/editor/labeling.cy.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index 32d28d5e8..d886671c9 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -1,4 +1,3 @@ -import Draw from "ol/interaction/Draw"; import { evaluateCoordinate, evaluateSelect, hasAiStyle, hasError, isDisabled } from "../helpers/formHelpers.js"; import { interceptShowLabelingCall, @@ -136,7 +135,7 @@ describe("Test labeling tool", () => { // Cannot draw if the panel was opened with the panel toggle button cy.window().then(win => { const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction instanceof Draw)).to.be.false; + expect(interactions.some(interaction => interaction.constructor.name === "Draw")).to.be.false; }); }); From 4e63762900134fd7393ef5c259b39d60cb83b543 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 10:10:36 +0200 Subject: [PATCH 17/39] Add wait --- src/client/cypress/e2e/editor/labeling.cy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index d886671c9..b8c9e45b3 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -131,6 +131,7 @@ describe("Test labeling tool", () => { cy.get('[data-cy="labeling-file-button-select"]').contains("WOLFHEART.pdf"); cy.get('[data-cy="button-select-popover"] .MuiListItem-root').eq(1).click(); cy.get('[data-cy="labeling-file-button-select"]').contains("borehole_attachment_3.pdf"); + waitForLabelingImageLoaded(); // Cannot draw if the panel was opened with the panel toggle button cy.window().then(win => { From a7bff963915268b8cfd7e90df3f61d17a650d63e Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 10:48:31 +0200 Subject: [PATCH 18/39] Remove not yet working test segment --- src/client/cypress/e2e/editor/labeling.cy.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/client/cypress/e2e/editor/labeling.cy.js b/src/client/cypress/e2e/editor/labeling.cy.js index b8c9e45b3..e0ee48283 100644 --- a/src/client/cypress/e2e/editor/labeling.cy.js +++ b/src/client/cypress/e2e/editor/labeling.cy.js @@ -131,13 +131,15 @@ describe("Test labeling tool", () => { cy.get('[data-cy="labeling-file-button-select"]').contains("WOLFHEART.pdf"); cy.get('[data-cy="button-select-popover"] .MuiListItem-root').eq(1).click(); cy.get('[data-cy="labeling-file-button-select"]').contains("borehole_attachment_3.pdf"); - waitForLabelingImageLoaded(); - // Cannot draw if the panel was opened with the panel toggle button - cy.window().then(win => { - const interactions = win.labelingImage.getInteractions().getArray(); - expect(interactions.some(interaction => interaction.constructor.name === "Draw")).to.be.false; - }); + // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 + // Add this once the api returns the correct file + // // Cannot draw if the panel was opened with the panel toggle button + // waitForLabelingImageLoaded(); + // cy.window().then(win => { + // const interactions = win.labelingImage.getInteractions().getArray(); + // expect(interactions.some(interaction => interaction.constructor.name === "Draw")).to.be.false; + // }); }); // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 & https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1545 From 94ecbe8bd6ec225110a82208bbbd41c17b2b5436 Mon Sep 17 00:00:00 2001 From: tschumpr Date: Thu, 26 Sep 2024 13:42:59 +0200 Subject: [PATCH 19/39] Fix merge conflict --- src/client/src/pages/detail/labeling/labelingPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index 3273ff6f8..af3d1c3ef 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -256,7 +256,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { sx={{ ...labelingButtonStyles, visibility: selectedFile ? "visible" : "hidden", - }} + }}> Date: Thu, 26 Sep 2024 14:53:03 +0200 Subject: [PATCH 20/39] Fix labeling bug --- .../form/location/coordinatesSegment.tsx | 8 +-- .../pages/detail/labeling/labelingContext.tsx | 7 ++- .../detail/labeling/labelingInterfaces.tsx | 3 +- .../pages/detail/labeling/labelingPanel.tsx | 50 +++++++++++-------- 4 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx index 51c0fa326..64acc46b9 100644 --- a/src/client/src/pages/detail/form/location/coordinatesSegment.tsx +++ b/src/client/src/pages/detail/form/location/coordinatesSegment.tsx @@ -35,7 +35,7 @@ const CoordinatesSegment: React.FC = ({ editingEnabled, }) => { const { t } = useTranslation(); - const { extractionObject, setExtractionObject } = useLabelingContext(); + const { extractionObject, setExtractionObject, setExtractionState, extractionState } = useLabelingContext(); // --- State variables --- const [currentReferenceSystem, setCurrentReferenceSystem] = useState(borehole.data.spatial_reference_system); @@ -280,14 +280,14 @@ const CoordinatesSegment: React.FC = ({ ]); useEffect(() => { - if (extractionObject?.type === "coordinates" && extractionObject.state === ExtractionState.success) { + if (extractionObject?.type === "coordinates" && extractionState === ExtractionState.success) { const coordinate = extractionObject.value as Coordinate; if (coordinate) { setCurrentReferenceSystem(referenceSystems[coordinate.projection].code); setValuesForReferenceSystem(coordinate.projection, coordinate.east.toString(), coordinate.north.toString()); } } - }, [extractionObject, setValuesForReferenceSystem]); + }, [extractionObject, extractionState, setValuesForReferenceSystem]); const isCoordinateExtraction = extractionObject?.type === "coordinates"; @@ -381,13 +381,13 @@ const CoordinatesSegment: React.FC = ({ currentReferenceSystem === referenceSystems.LV95.code ? ReferenceSystemKey.LV95 : ReferenceSystemKey.LV03; setExtractionObject({ type: "coordinates", - state: ExtractionState.start, previousValue: { east: formMethods.getValues(referenceSystems[referenceSystemKey].fieldName.X), north: formMethods.getValues(referenceSystems[referenceSystemKey].fieldName.Y), projection: referenceSystemKey, }, }); + setExtractionState(ExtractionState.start); }; return ( diff --git a/src/client/src/pages/detail/labeling/labelingContext.tsx b/src/client/src/pages/detail/labeling/labelingContext.tsx index 13565ab51..3d66be3ba 100644 --- a/src/client/src/pages/detail/labeling/labelingContext.tsx +++ b/src/client/src/pages/detail/labeling/labelingContext.tsx @@ -1,5 +1,5 @@ import { createContext, FC, PropsWithChildren, useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { ExtractionObject, LabelingContextInterface, PanelPosition } from "./labelingInterfaces.tsx"; +import { ExtractionObject, ExtractionState, LabelingContextInterface, PanelPosition } from "./labelingInterfaces.tsx"; export const LabelingContext = createContext({ panelPosition: "right", @@ -8,12 +8,15 @@ export const LabelingContext = createContext({ togglePanel: () => {}, extractionObject: undefined, setExtractionObject: () => {}, + extractionState: undefined, + setExtractionState: () => {}, }); export const LabelingProvider: FC = ({ children }) => { const [panelPosition, setPanelPosition] = useState("right"); const [panelOpen, setPanelOpen] = useState(false); const [extractionObject, setExtractionObject] = useState(); + const [extractionState, setExtractionState] = useState(); const togglePanel = useCallback((isOpen?: boolean) => { setPanelOpen(prevState => (isOpen !== undefined ? isOpen : !prevState)); @@ -52,6 +55,8 @@ export const LabelingProvider: FC = ({ children }) => { togglePanel, extractionObject, setExtractionObject, + extractionState, + setExtractionState, }}> {children} diff --git a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx index ec6527ef3..d1cc3dd7f 100644 --- a/src/client/src/pages/detail/labeling/labelingInterfaces.tsx +++ b/src/client/src/pages/detail/labeling/labelingInterfaces.tsx @@ -12,7 +12,6 @@ export enum ExtractionState { } export interface ExtractionObject { - state: ExtractionState; type?: ExtractionType; value?: string | number | Coordinate; previousValue?: string | number | Coordinate | null; @@ -51,6 +50,8 @@ export interface LabelingContextInterface { togglePanel: (isOpen?: boolean) => void; extractionObject?: ExtractionObject; setExtractionObject: (extractionObject: ExtractionObject | undefined) => void; + extractionState?: ExtractionState; + setExtractionState: (extractionState: ExtractionState) => void; } export const labelingFileFormat = "application/pdf"; diff --git a/src/client/src/pages/detail/labeling/labelingPanel.tsx b/src/client/src/pages/detail/labeling/labelingPanel.tsx index af3d1c3ef..3f9e5129d 100644 --- a/src/client/src/pages/detail/labeling/labelingPanel.tsx +++ b/src/client/src/pages/detail/labeling/labelingPanel.tsx @@ -63,13 +63,21 @@ interface LabelingPanelProps { const LabelingPanel: FC = ({ boreholeId }) => { const { t } = useTranslation(); - const { panelPosition, setPanelPosition, extractionObject, setExtractionObject } = useLabelingContext(); + const { + panelPosition, + setPanelPosition, + extractionObject, + setExtractionObject, + setExtractionState, + extractionState, + } = useLabelingContext(); const [isLoadingFiles, setIsLoadingFiles] = useState(true); const [files, setFiles] = useState(); const [selectedFile, setSelectedFile] = useState(); const [fileInfo, setFileInfo] = useState(); const [activePage, setActivePage] = useState(1); const [drawTooltipLabel, setDrawTooltipLabel] = useState(); + const [extractionExtent, setExtractionExtent] = useState([]); const [abortController, setAbortController] = useState(); const fileInputRef = useRef(null); const { alertIsOpen, text, severity, autoHideDuration, showAlert, closeAlert } = useAlertManager(); @@ -126,35 +134,30 @@ const LabelingPanel: FC = ({ boreholeId }) => { x1: Math.max(...[extent[0], extent[2]]), y1: Math.max(...[extent[1], extent[3]]), }; + setExtractionExtent([]); const request: ExtractionRequest = { filename: fileInfo.fileName.substring(0, fileInfo.fileName.lastIndexOf("-")) + ".pdf", page_number: activePage, bbox: bbox, format: extractionObject.type, }; - setExtractionObject({ - ...extractionObject, - state: ExtractionState.loading, - }); + setExtractionState(ExtractionState.loading); setDrawTooltipLabel(undefined); const abortController = new AbortController(); setAbortController(abortController); extractData(request, abortController.signal) .then(response => { if (extractionObject.type) { + setExtractionState(ExtractionState.success); setExtractionObject({ ...extractionObject, - state: ExtractionState.success, value: response[extractionObject.type], }); } }) .catch(error => { if (!error?.message?.includes("AbortError")) { - setExtractionObject({ - ...extractionObject, - state: ExtractionState.error, - }); + setExtractionState(ExtractionState.error); // TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546 // Check if error message is correct, resp. handle all error cases with different messages showAlert(t(error.message), "error"); @@ -165,15 +168,23 @@ const LabelingPanel: FC = ({ boreholeId }) => { }); } }, - [activePage, extractionObject, fileInfo, setExtractionObject, showAlert, t], + [activePage, extractionObject, fileInfo, setExtractionObject, setExtractionState, showAlert, t], ); + useEffect(() => { + if (extractionExtent?.length > 0) { + triggerDataExtraction(extractionExtent); + } + }, [extractionExtent, triggerDataExtraction]); + const cancelRequest = () => { if (abortController) { abortController.abort(); setAbortController(undefined); } - setExtractionObject({ type: "coordinates", state: ExtractionState.start }); + setExtractionObject({ type: "coordinates" }); + setExtractionState(ExtractionState.start); + setExtractionExtent([]); }; useEffect(() => { @@ -183,16 +194,13 @@ const LabelingPanel: FC = ({ boreholeId }) => { }, [files, loadFiles]); useEffect(() => { - if (extractionObject?.state === ExtractionState.start) { - setExtractionObject({ - ...extractionObject, - state: ExtractionState.drawing, - }); - if (extractionObject.type === "coordinates") { + if (extractionState === ExtractionState.start) { + setExtractionState(ExtractionState.drawing); + if (extractionObject?.type === "coordinates") { setDrawTooltipLabel("drawCoordinateBox"); } } - }, [extractionObject, setExtractionObject]); + }, [extractionObject, extractionState, setExtractionObject, setExtractionState]); useEffect(() => { if (selectedFile) { @@ -298,7 +306,7 @@ const LabelingPanel: FC = ({ boreholeId }) => { {text} ) : ( - extractionObject?.state === ExtractionState.loading && ( + extractionState === ExtractionState.loading && (