Skip to content

Commit

Permalink
Extract coordinates from attachment
Browse files Browse the repository at this point in the history
  • Loading branch information
flenny authored Oct 15, 2024
2 parents e35fd4e + f33a3c6 commit 9f479e3
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 86 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
run: dotnet build BDMS.sln -c Release /warnaserror

- name: Start db and api's
run: docker compose up --wait minio db api-legacy api
run: docker compose up --wait minio db api-legacy api dataextraction

- name: Run dotnet tests
run: dotnet test BDMS.sln -c Release --no-build --verbosity normal --filter TestCategory!=LongRunning
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
run: dotnet build BDMS.sln -c Release /warnaserror

- name: Start db and api's
run: docker compose up --wait minio db api-legacy api oidc-server
run: docker compose up --wait minio db api-legacy api oidc-server dataextraction

- working-directory: ./src/client
run: npm ci
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- WMTS Services are now supported as custom user layers.
- Added data extraction API.
- Added support to extract coordinates from a borehole attachment.

### Changed

Expand Down
88 changes: 59 additions & 29 deletions src/client/cypress/e2e/detailPage/labeling.cy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { evaluateCoordinate, evaluateSelect, hasAiStyle, hasError, isDisabled } from "../helpers/formHelpers.js";
import {
interceptShowLabelingCall,
newEditableBorehole,
newUneditableBorehole,
startBoreholeEditing,
Expand Down Expand Up @@ -55,10 +54,6 @@ const waitForLabelingImageLoaded = () => {
};

describe("Test labeling tool", () => {
beforeEach(() => {
interceptShowLabelingCall();
});

it("can show labeling panel", () => {
newUneditableBorehole().as("borehole_id");
// only show in editing mode
Expand Down Expand Up @@ -132,19 +127,15 @@ describe("Test labeling tool", () => {
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");

// 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;
// });
// 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
// We have to wait for the docker integration before this test can be enabled
it.skip("can extract data from image", () => {
it("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");
Expand Down Expand Up @@ -182,25 +173,43 @@ describe("Test labeling tool", () => {
hasError("location_y_lv03", false);
isDisabled("location_y_lv03");

// Can draw box around coordinates and extract correct coordinates
drawBox(400, 60, 600, 170);
// 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");
evaluateCoordinate("location_x", "2'646'359.7");
hasError("location_x", false);
isDisabled("location_x", false);
evaluateCoordinate("location_y", "1'249'017");
evaluateCoordinate("location_y", "1'249'017.82");
hasError("location_y", false);
isDisabled("location_y", false);
evaluateCoordinate("location_x_lv03", "646'358");
evaluateCoordinate("location_x_lv03", "646'358.97");
hasError("location_x_lv03", false);
isDisabled("location_x_lv03", true);
evaluateCoordinate("location_y_lv03", "249'017");
evaluateCoordinate("location_y_lv03", "249'017.66");
hasError("location_y_lv03", false);
isDisabled("location_y_lv03", true);
});

// TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546
// api call extract_data in drawBox times out
it.skip("can extract data from rotated and zoomed next page", () => {
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");
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");

// 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();
waitForLabelingImageLoaded();
Expand Down Expand Up @@ -240,8 +249,29 @@ describe("Test labeling tool", () => {
evaluateCoordinate("location_y_lv03", "249'931");
hasError("location_y_lv03", false);
isDisabled("location_y_lv03", false);
});

// TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546
// api call extract_data in drawBox times out
it.skip("shows alert if no coordinates are extracted", () => {
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");
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");

// Shows alert if no coordinates are extracted
cy.get('[data-cy="labeling-page-next"]').click();
waitForLabelingImageLoaded();
cy.get('[data-cy="labeling-page-count"]').contains("3 / 3");
Expand Down Expand Up @@ -269,9 +299,9 @@ describe("Test labeling tool", () => {
waitForLabelingImageLoaded();
drawBox(400, 60, 600, 170);
evaluateSelect("spatial_reference_system", "20104001");
evaluateCoordinate("location_x", "2'646'359");
evaluateCoordinate("location_y", "1'249'017");
evaluateCoordinate("location_x_lv03", "646'358");
evaluateCoordinate("location_y_lv03", "249'017");
evaluateCoordinate("location_x", "2'646'359.7");
evaluateCoordinate("location_y", "1'249'017.82");
evaluateCoordinate("location_x_lv03", "646'358.97");
evaluateCoordinate("location_y_lv03", "249'017.66");
});
});
9 changes: 1 addition & 8 deletions src/client/cypress/e2e/helpers/testHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import { startEditing, stopEditing } from "./buttonHelpers.js";

export const bearerAuth = token => ({ bearer: token });

export const interceptShowLabelingCall = () => {
cy.intercept("GET", "api/show-labeling-in-cypress-test", {
statusCode: 200,
}).as("show-labeling");
};
export const interceptApiCalls = () => {
// Api V1
cy.intercept("/api/v1/borehole").as("borehole");
Expand Down Expand Up @@ -107,9 +102,7 @@ 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");
cy.intercept("dataextraction/api/V1/extract_data").as("extract-data");
};

/**
Expand Down
26 changes: 26 additions & 0 deletions src/client/src/api/dataextraction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import store from "../reducers";
import { getAuthorizationHeader } from "./authentication";

export async function fetchCreatePngs(fileName) {
return await fetch("dataextraction/api/V1/create_pngs", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: getAuthorizationHeader(store.getState().core_user.authentication),
},
body: JSON.stringify({ filename: fileName + ".pdf" }),
});
}

export async function fetchExtractData(request, abortSignal) {
return await fetch("dataextraction/api/V1/extract_data", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: getAuthorizationHeader(store.getState().core_user.authentication),
},
body: JSON.stringify(request),
signal: abortSignal,
});
}
19 changes: 3 additions & 16 deletions src/client/src/api/file/file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ExtractionRequest, ExtractionResponse } from "../../pages/detail/labeling/labelingInterfaces.tsx";
import { ApiError } from "../apiInterfaces.ts";
import { fetchCreatePngs, fetchExtractData } from "../dataextraction";
import { download, fetchApiV2, fetchApiV2Base, upload } from "../fetchApiV2";
import { DataExtractionResponse, maxFileSizeKB } from "./fileInterfaces.ts";

Expand Down Expand Up @@ -82,28 +83,14 @@ export async function loadImage(fileName: string) {
}

export async function createExtractionPngs(fileName: string) {
// 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" },
body: JSON.stringify({ filename: fileName + ".pdf" }),
});
const response = await fetchCreatePngs(fileName);
if (!response.ok) {
throw new ApiError("errorDataExtractionFileLoading", 500);
}
}

export async function extractData(request: ExtractionRequest, abortSignal: AbortSignal): Promise<ExtractionResponse> {
// 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" },
body: JSON.stringify(request),
signal: abortSignal,
});

const response = await fetchExtractData(request, abortSignal);
if (response.ok) {
const responseObject = await response.json();
// TODO: https://github.com/swisstopo/swissgeol-boreholes-suite/issues/1546
Expand Down
18 changes: 1 addition & 17 deletions src/client/src/pages/detail/detailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,16 @@ import LabelingPanel from "./labeling/labelingPanel.tsx";
interface DetailPageContentProps {
editingEnabled: boolean;
editableByCurrentUser: boolean;
showLabeling: boolean;
}

export const DetailPage: FC = () => {
const [editingEnabled, setEditingEnabled] = useState(false);
const [editableByCurrentUser, setEditableByCurrentUser] = useState(false);
const [showLabeling, setShowLabeling] = useState(false);
const borehole: Borehole = useSelector((state: ReduxRootState) => state.core_borehole);
const user = useSelector((state: ReduxRootState) => state.core_user);
const location = useLocation();
const { panelPosition, panelOpen, togglePanel } = useLabelingContext();

useEffect(() => {
// Fetch to be mocked in cypress test to show labeling area.
const checkLabelingVisibility = async () => {
try {
const response = await fetch("api/show-labeling-in-cypress-test");
setShowLabeling(response.status === 200);
} catch {
/* fetch will fail outside of test environment so state should not be updated */
}
};
checkLabelingVisibility().catch();
}, []);

useEffect(() => {
setEditingEnabled(borehole.data.lock !== null);
}, [borehole.data.lock]);
Expand Down Expand Up @@ -68,7 +53,6 @@ export const DetailPage: FC = () => {
const props: DetailPageContentProps = {
editingEnabled: editingEnabled,
editableByCurrentUser: editableByCurrentUser,
showLabeling: showLabeling,
};

return (
Expand All @@ -93,7 +77,7 @@ export const DetailPage: FC = () => {
width: panelOpen && panelPosition === "right" ? "50%" : "100%",
height: panelOpen && panelPosition === "bottom" ? "50%" : "100%",
}}>
{editingEnabled && showLabeling && (
{editingEnabled && (
<LabelingToggleButton panelOpen={panelOpen} panelPosition={panelPosition} onClick={() => togglePanel()} />
)}
<DetailPageContent {...props} />
Expand Down
2 changes: 0 additions & 2 deletions src/client/src/pages/detail/detailPageContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,6 @@ class DetailPageContent extends React.Component {
updateChange={this.updateChange}
editingEnabled={editingEnabled}></RestrictionSegment>
<LocationSegment
showLabeling={this.props.showLabeling}
borehole={borehole}
editingEnabled={editingEnabled}
updateChange={this.updateChange}
Expand Down Expand Up @@ -413,7 +412,6 @@ DetailPageContent.propTypes = {
workflow: PropTypes.object,
editingEnabled: PropTypes.bool,
editableByCurrentUser: PropTypes.bool,
showLabeling: PropTypes.bool,
};

DetailPageContent.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export interface CoordinatesSegmentProps extends SegmentProps {
updateNumber: (fieldName: keyof Borehole["data"], value: number | null) => void;
mapPointChange: boolean;
setMapPointChange: React.Dispatch<React.SetStateAction<boolean>>;
showLabeling: boolean;
}

export interface Location {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const CoordinatesSegment: React.FC<CoordinatesSegmentProps> = ({
updateNumber,
mapPointChange,
setMapPointChange,
showLabeling,
editingEnabled,
}) => {
const { t } = useTranslation();
Expand Down Expand Up @@ -400,7 +399,6 @@ const CoordinatesSegment: React.FC<CoordinatesSegmentProps> = ({
sx={{ p: 4, pb: 3 }}
titleTypographyProps={{ variant: "h5" }}
action={
showLabeling &&
editingEnabled && (
<LabelingButton
className={extractionObject?.type === "coordinates" ? "Mui-active" : ""}
Expand Down
10 changes: 1 addition & 9 deletions src/client/src/pages/detail/form/location/locationSegment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,11 @@ import ElevationSegment from "./elevationSegment";
import { SegmentProps } from "./segmentInterface.ts";

interface LocationSegmentProps extends SegmentProps {
showLabeling: boolean;
editingEnabled: boolean;
updateNumber: (fieldName: keyof Borehole["data"], value: number | null) => void;
}

const LocationSegment = ({
borehole,
updateChange,
updateNumber,
showLabeling,
editingEnabled,
}: LocationSegmentProps) => {
const LocationSegment = ({ borehole, updateChange, updateNumber, editingEnabled }: LocationSegmentProps) => {
const [mapPointChange, setMapPointChange] = useState(false);

return (
Expand All @@ -35,7 +28,6 @@ const LocationSegment = ({
updateNumber={updateNumber}
mapPointChange={mapPointChange}
setMapPointChange={setMapPointChange}
showLabeling={showLabeling}
editingEnabled={editingEnabled}
/>
<ElevationSegment
Expand Down
5 changes: 5 additions & 0 deletions src/client/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export default defineConfig({
target: "http://localhost:5000/",
changeOrigin: true,
},
"/dataextraction": {
target: "http://localhost:8000/",
changeOrigin: true,
rewrite: path => path.replace(/^\/dataextraction/, ""),
},
},
port: 3000,
},
Expand Down

0 comments on commit 9f479e3

Please sign in to comment.