From c8d53c51cb111f5798f293277cb3886782f6839f Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Mon, 19 Aug 2024 16:26:31 +0200 Subject: [PATCH 1/5] Adjustments and new menu utility components --- .../src/lib/components/MenuDivider/index.ts | 1 + .../components/MenuDivider/menuDivider.tsx | 3 + .../src/lib/components/MenuHeading/index.ts | 2 + .../components/MenuHeading/menuHeading.tsx | 9 ++ .../components/SortableList/sortableList.tsx | 90 ++++++++++++------- .../SortableList/sortableListGroup.tsx | 65 +++++++++----- .../SortableList/sortableListItem.tsx | 11 +-- 7 files changed, 119 insertions(+), 62 deletions(-) create mode 100644 frontend/src/lib/components/MenuDivider/index.ts create mode 100644 frontend/src/lib/components/MenuDivider/menuDivider.tsx create mode 100644 frontend/src/lib/components/MenuHeading/index.ts create mode 100644 frontend/src/lib/components/MenuHeading/menuHeading.tsx diff --git a/frontend/src/lib/components/MenuDivider/index.ts b/frontend/src/lib/components/MenuDivider/index.ts new file mode 100644 index 000000000..607a0e175 --- /dev/null +++ b/frontend/src/lib/components/MenuDivider/index.ts @@ -0,0 +1 @@ +export { MenuDivider } from "./menuDivider"; diff --git a/frontend/src/lib/components/MenuDivider/menuDivider.tsx b/frontend/src/lib/components/MenuDivider/menuDivider.tsx new file mode 100644 index 000000000..b72cfe9f9 --- /dev/null +++ b/frontend/src/lib/components/MenuDivider/menuDivider.tsx @@ -0,0 +1,3 @@ +export function MenuDivider(): React.ReactNode { + return
; +} diff --git a/frontend/src/lib/components/MenuHeading/index.ts b/frontend/src/lib/components/MenuHeading/index.ts new file mode 100644 index 000000000..deaa71b3c --- /dev/null +++ b/frontend/src/lib/components/MenuHeading/index.ts @@ -0,0 +1,2 @@ +export { MenuHeading } from "./menuHeading"; +export type { MenuHeadingProps } from "./menuHeading"; diff --git a/frontend/src/lib/components/MenuHeading/menuHeading.tsx b/frontend/src/lib/components/MenuHeading/menuHeading.tsx new file mode 100644 index 000000000..aa9eb94e4 --- /dev/null +++ b/frontend/src/lib/components/MenuHeading/menuHeading.tsx @@ -0,0 +1,9 @@ +export type MenuHeadingProps = { + children: React.ReactNode; +}; + +export function MenuHeading(props: MenuHeadingProps): React.ReactNode { + return ( +
{props.children}
+ ); +} diff --git a/frontend/src/lib/components/SortableList/sortableList.tsx b/frontend/src/lib/components/SortableList/sortableList.tsx index f06ad63bc..9ddd0e8c0 100644 --- a/frontend/src/lib/components/SortableList/sortableList.tsx +++ b/frontend/src/lib/components/SortableList/sortableList.tsx @@ -3,7 +3,7 @@ import React from "react"; import { createPortal } from "@lib/utils/createPortal"; import { MANHATTAN_LENGTH, rectContainsPoint } from "@lib/utils/geometry"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; -import { Vec2, point2Distance } from "@lib/utils/vec2"; +import { Vec2, point2Distance, vec2FromPointerEvent } from "@lib/utils/vec2"; import { isEqual } from "lodash"; @@ -146,6 +146,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { React.ReactElement[] >(props.children); + const mainDivRef = React.useRef(null); const listDivRef = React.useRef(null); const scrollDivRef = React.useRef(null); const upperScrollDivRef = React.useRef(null); @@ -178,6 +179,8 @@ export function SortableList(props: SortableListProps): React.ReactNode { let currentScrollTime = 100; function handlePointerDown(e: PointerEvent) { + e.preventDefault(); + e.stopPropagation(); const target = e.target; if (!target) { return; @@ -280,24 +283,32 @@ export function SortableList(props: SortableListProps): React.ReactNode { return items; } - function getHoveredElement(e: PointerEvent): HTMLElement | null { - const items = getDragElementsRecursively(); - for (const item of items) { - if (rectContainsPoint(item.getBoundingClientRect(), { x: e.clientX, y: e.clientY })) { - const type = getItemType(item); + function getHoveredElementAndArea(e: PointerEvent): { element: HTMLElement; area: HoveredArea } | null { + const elements = getDragElementsRecursively(); + for (const element of elements) { + if (rectContainsPoint(element.getBoundingClientRect(), vec2FromPointerEvent(e))) { + const type = getItemType(element); if (type === ItemType.GROUP) { - const content = item.querySelector(".sortable-list-group-content"); + const content = element.querySelector(".sortable-list-group-content"); if ( content && - rectContainsPoint(content.getBoundingClientRect(), { x: e.clientX, y: e.clientY }) + rectContainsPoint(content.getBoundingClientRect(), vec2FromPointerEvent(e)) && + content.getElementsByClassName("sortable-list-item").length > 0 ) { continue; } } - return item; + return { element, area: getHoveredAreaOfItem(element, e) }; } } + const directChildren = elements.filter((el) => el.parentElement === currentListDivRef); + if ( + mainDivRef.current && + rectContainsPoint(mainDivRef.current.getBoundingClientRect(), vec2FromPointerEvent(e)) + ) { + return { element: directChildren[directChildren.length - 1], area: HoveredArea.BOTTOM }; + } return null; } @@ -332,13 +343,11 @@ export function SortableList(props: SortableListProps): React.ReactNode { } const headerElement = item.querySelector(".sortable-list-item-header"); - if (!headerElement) { - return HoveredArea.CENTER; - } - - const headerRect = headerElement.getBoundingClientRect(); - if (rectContainsPoint(headerRect, { x: e.clientX, y: e.clientY })) { - return HoveredArea.HEADER; + if (headerElement) { + const headerRect = headerElement.getBoundingClientRect(); + if (rectContainsPoint(headerRect, { x: e.clientX, y: e.clientY })) { + return HoveredArea.HEADER; + } } return HoveredArea.CENTER; @@ -382,6 +391,9 @@ export function SortableList(props: SortableListProps): React.ReactNode { } function handlePointerMove(e: PointerEvent) { + e.preventDefault(); + e.stopPropagation(); + if (!pointerDownPosition || !draggedElement) { return; } @@ -413,9 +425,9 @@ export function SortableList(props: SortableListProps): React.ReactNode { return; } - const hoveredElement = getHoveredElement(e); - if (hoveredElement && hoveredElement instanceof HTMLElement) { - const area = getHoveredAreaOfItem(hoveredElement, e); + const hoveredElementAndArea = getHoveredElementAndArea(e); + if (hoveredElementAndArea) { + const { element: hoveredElement, area } = hoveredElementAndArea; const itemType = getItemType(hoveredElement); if (itemType === ItemType.ITEM && (area === HoveredArea.CENTER || area === HoveredArea.HEADER)) { currentlyHoveredElement = null; @@ -427,8 +439,17 @@ export function SortableList(props: SortableListProps): React.ReactNode { const parentType = parentElement ? getItemType(parentElement) : null; let destinationType = parentType; - if (itemType === ItemType.GROUP && area === HoveredArea.HEADER) { - destinationType = ItemType.GROUP; + let destinationId = getItemParentGroupId(hoveredElement); + + if (itemType === ItemType.GROUP) { + if (area === HoveredArea.HEADER) { + destinationType = ItemType.GROUP; + destinationId = hoveredElement.dataset.itemId ?? ""; + } + if (area === HoveredArea.CENTER) { + destinationType = ItemType.GROUP; + destinationId = hoveredElement.dataset.itemId ?? ""; + } } if ( @@ -438,8 +459,8 @@ export function SortableList(props: SortableListProps): React.ReactNode { movedItemType: draggedElement.type, originId: draggedElement.parentId, originType: draggedElement.parentType, - destinationId: getItemParentGroupId(hoveredElement), - destinationType: destinationType, + destinationId, + destinationType, }) ) { currentlyHoveredElement = null; @@ -452,8 +473,8 @@ export function SortableList(props: SortableListProps): React.ReactNode { id: hoveredElement.dataset.itemId ?? "", type: itemType, area, - parentId: getItemParentGroupId(hoveredElement), - parentType: parentType, + parentId: destinationId, + parentType: destinationType, }; } else { currentlyHoveredElement = null; @@ -470,11 +491,9 @@ export function SortableList(props: SortableListProps): React.ReactNode { return; } - if (currentlyHoveredElement.area === HoveredArea.CENTER) { - return; - } - if (isMoveAllowed !== undefined) { + const parentElement = getItemParent(currentlyHoveredElement.element); + const parentType = parentElement ? getItemType(parentElement) : null; if ( !isMoveAllowed({ movedItemId: draggedElement.id, @@ -482,14 +501,17 @@ export function SortableList(props: SortableListProps): React.ReactNode { originId: getItemParentGroupId(draggedElement.element), originType: getItemType(draggedElement.element), destinationId: getItemParentGroupId(currentlyHoveredElement.element), - destinationType: currentlyHoveredElement.type, + destinationType: parentType, }) ) { return; } } - if (currentlyHoveredElement.area === HoveredArea.HEADER) { + if ( + currentlyHoveredElement.area === HoveredArea.HEADER || + currentlyHoveredElement.area === HoveredArea.CENTER + ) { const originId = getItemParentGroupId(draggedElement.element); const destinationId = currentlyHoveredElement.id; const position = 0; @@ -518,6 +540,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { setIsDragging(false); setDraggedItemId(null); setHoveredItemIdAndArea(null); + doScroll = false; document.removeEventListener("pointermove", handlePointerMove); document.removeEventListener("pointerup", handlePointerUp); @@ -578,7 +601,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { } return ( -
+
{makeChildren()} +
+
+
{isDragging && diff --git a/frontend/src/lib/components/SortableList/sortableListGroup.tsx b/frontend/src/lib/components/SortableList/sortableListGroup.tsx index 7deaa0ec8..127d83d41 100644 --- a/frontend/src/lib/components/SortableList/sortableListGroup.tsx +++ b/frontend/src/lib/components/SortableList/sortableListGroup.tsx @@ -39,7 +39,10 @@ export function SortableListGroup(props: SortableListGroupProps): React.ReactNod const sortableListContext = React.useContext(SortableListContext); const isHovered = sortableListContext.hoveredElementId === props.id; - const isHeaderHovered = isHovered && sortableListContext.hoveredArea === HoveredArea.HEADER; + const isHeaderHovered = + isHovered && + (sortableListContext.hoveredArea === HoveredArea.HEADER || + sortableListContext.hoveredArea === HoveredArea.CENTER); const isDragging = sortableListContext.draggedElementId === props.id; const dragPosition = sortableListContext.dragPosition; @@ -47,14 +50,13 @@ export function SortableListGroup(props: SortableListGroupProps): React.ReactNod setIsExpanded(!isExpanded); } + const hasContent = props.children !== undefined && props.children.length > 0; + return ( <> {isHovered && sortableListContext.hoveredArea === HoveredArea.TOP && }
@@ -63,13 +65,19 @@ export function SortableListGroup(props: SortableListGroupProps): React.ReactNod hidden: !isDragging, })} >
-
+
{isDragging && dragPosition && createPortal(
-
+
)}
- {props.children === undefined || props.children.length === 0 - ? props.contentWhenEmpty - : props.children} + {hasContent ? props.children : props.contentWhenEmpty}
{isHovered && sortableListContext.hoveredArea === HoveredArea.BOTTOM && } @@ -101,6 +112,8 @@ export function SortableListGroup(props: SortableListGroupProps): React.ReactNod type HeaderProps = { title: React.ReactNode; expanded: boolean; + expandable: boolean; + hovered: boolean; onToggleExpanded?: () => void; icon?: React.ReactNode; startAdornment?: React.ReactNode; @@ -109,17 +122,27 @@ type HeaderProps = { function Header(props: HeaderProps): React.ReactNode { return ( -
+
-
- {props.expanded ? : } -
+ {props.expandable && ( +
+ {props.expanded ? : } +
+ )}
{props.startAdornment}
{props.title}
diff --git a/frontend/src/lib/components/SortableList/sortableListItem.tsx b/frontend/src/lib/components/SortableList/sortableListItem.tsx index 3b06e8de9..c9429b9ea 100644 --- a/frontend/src/lib/components/SortableList/sortableListItem.tsx +++ b/frontend/src/lib/components/SortableList/sortableListItem.tsx @@ -3,7 +3,7 @@ import React from "react"; import { useElementBoundingRect } from "@lib/hooks/useElementBoundingRect"; import { createPortal } from "@lib/utils/createPortal"; import { resolveClassNames } from "@lib/utils/resolveClassNames"; -import { DragIndicator, ExpandLess, ExpandMore } from "@mui/icons-material"; +import { DragIndicator } from "@mui/icons-material"; import { HoveredArea, SortableListContext } from "./sortableList"; import { SortableListDropIndicator } from "./sortableListDropIndicator"; @@ -93,7 +93,7 @@ type HeaderProps = { function Header(props: HeaderProps): React.ReactNode { return ( -
+
@@ -102,13 +102,6 @@ function Header(props: HeaderProps): React.ReactNode {
{props.title}
{props.endAdornment}
-
- {props.expanded ? : } -
); } From e5ee9e94b94c52b80e776a0f469d207a9ee1904a Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:16:03 +0200 Subject: [PATCH 2/5] Remove Structural uncertainty module (#698) --- .../components/Legend.tsx | 30 -- .../components/esvIntersection.tsx | 235 ----------- .../interfaces.ts | 51 --- .../loadModule.tsx | 12 - .../queryHooks.tsx | 84 ---- .../registerModule.ts | 18 - .../settings/atoms/baseAtoms.ts | 16 - .../components/intersectionSettings.tsx | 36 -- .../components/realizationsSelect.tsx | 55 --- .../settings/settings.tsx | 367 ------------------ .../state.ts | 13 - .../typesAndEnums.ts | 44 --- .../view.tsx | 134 ------- frontend/src/modules/registerAllModules.ts | 1 - 14 files changed, 1096 deletions(-) delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/components/Legend.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/components/esvIntersection.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/interfaces.ts delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/queryHooks.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/registerModule.ts delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/settings/atoms/baseAtoms.ts delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/settings/components/intersectionSettings.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/settings/settings.tsx delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/state.ts delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts delete mode 100644 frontend/src/modules/StructuralUncertaintyIntersection/view.tsx diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/components/Legend.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/components/Legend.tsx deleted file mode 100644 index 041ec41fa..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/components/Legend.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; - -type LegendItem = { - color: string; - label: string; -}; - - -type LegendProps = { - items: LegendItem[]; -}; - -const Legend: React.FC = ({ items }) => { - return ( -
- {items.map((item, index) => ( -
-
- {item.label} -
- ))} -
- ); -}; -export default Legend; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/components/esvIntersection.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/components/esvIntersection.tsx deleted file mode 100644 index bda9ec1e7..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/components/esvIntersection.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import React from "react"; - -import { StatisticFunction_api, SurfaceRealizationSampleValues_api } from "@api"; -import { - Controller, - GeomodelCanvasLayer, - GeomodelLayerV2, - GridLayer, - PixiRenderApplication, - ReferenceLine, - ReferenceLineLayer, - SurfaceData, - WellborepathLayer, -} from "@equinor/esv-intersection"; -import { addMDOverlay } from "@modules/SeismicIntersection/utils/esvIntersectionControllerUtils"; -import { makeReferenceSystemFromTrajectoryXyzPoints } from "@modules/SeismicIntersection/utils/esvIntersectionDataConversion"; - -import { isEqual } from "lodash"; - -import { SurfaceRealizationSampleValuesData } from "../queryHooks"; -import { StratigraphyColorMap, VisualizationMode } from "../typesAndEnums"; - -type EsvIntersectionProps = { - width: number; - height: number; - zScale: number; - extension: number; - wellborePath: number[][] | null; - cumLength: number[] | null; - surfaceRealizationSampleValuesData?: SurfaceRealizationSampleValuesData[] | null; - visualizationMode: VisualizationMode; - statisticFunctions: StatisticFunction_api[]; - stratigraphyColorMap: StratigraphyColorMap; -}; - -export const EsvIntersection: React.FC = (props) => { - const containerDiv = React.useRef(null); - const controller = React.useRef(null); - const pixiContent = React.useRef(null); - const [previousWellborePath, setPreviousWellborePath] = React.useState(null); - const width = props.width; - const height = props.height; - const seaAndRKBLayerData: ReferenceLine[] = [ - { text: "RKB", lineType: "dashed", color: "black", depth: 0 }, - { text: "MSL", lineType: "wavy", color: "blue", depth: 30 }, - { text: "Seabed", lineType: "solid", color: "slategray", depth: 91.1, lineWidth: 2 }, - ]; - const seaAndRKBLayer = new ReferenceLineLayer("sea-and-rkb-layer", { data: seaAndRKBLayerData }); - - React.useEffect(function initializeEsvIntersectionController() { - if (containerDiv.current) { - const axisOptions = { xLabel: "x", yLabel: "y", unitOfMeasure: "m" }; - controller.current = new Controller({ - container: containerDiv.current, - axisOptions, - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - pixiContent.current = new PixiRenderApplication({ width: width, height: height }); - - // Initialize/configure controller - addMDOverlay(controller.current); - controller.current.addLayer(new GridLayer("gridLayer")); - controller.current.addLayer(new WellborepathLayer("wellBorePathLayer")); - controller.current.addLayer( - new GeomodelCanvasLayer("statisticalSurfaceLayer", { order: 3, layerOpacity: 0.6 }) - ); - controller.current.addLayer( - new GeomodelLayerV2(pixiContent.current, "realizationsSurfaceLayer", { order: 4, layerOpacity: 0.6 }) - ); - controller.current.addLayer(seaAndRKBLayer); - controller.current.setBounds([10, 1000], [0, 3000]); - controller.current.setViewport(1000, 1650, 6000); - controller.current.zoomPanHandler.zFactor = props.zScale; - } - return () => { - controller.current?.destroy(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - if (!isEqual(previousWellborePath, props.wellborePath)) { - setPreviousWellborePath(props.wellborePath); - } - if (controller.current && props.wellborePath && props.cumLength) { - controller.current.adjustToSize(Math.max(0, width), Math.max(0, height)); - const referenceSystem = makeReferenceSystemFromTrajectoryXyzPoints(props.wellborePath); - controller.current.setReferenceSystem(referenceSystem); - - controller.current?.getLayer("statisticalSurfaceLayer")?.clearData(); - controller.current?.getLayer("realizationsSurfaceLayer")?.clearData(); - if (props.surfaceRealizationSampleValuesData) { - if ( - props.visualizationMode === VisualizationMode.STATISTICAL_LINES || - props.visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS - ) { - const statData = surfaceSamplePointsToStatisticalLayerData( - props.surfaceRealizationSampleValuesData, - props.cumLength, - props.statisticFunctions, - props.stratigraphyColorMap - ); - controller.current.getLayer("statisticalSurfaceLayer")?.setData(statData); - } - if ( - props.visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS || - props.visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS - ) { - const realizationData = surfaceSamplePointsToRealizationLayerData( - props.surfaceRealizationSampleValuesData, - props.cumLength, - props.stratigraphyColorMap - ); - controller.current.getLayer("realizationsSurfaceLayer")?.setData(realizationData); - } - } - } - - return
; -}; - -function surfaceSamplePointsToRealizationLayerData( - samplePoints: SurfaceRealizationSampleValuesData[], - cumLength: number[], - stratigraphyColorMap: StratigraphyColorMap -): SurfaceData { - const surfaceValues: { name: string; realization: number; values: number[]; showLabel: boolean }[] = []; - samplePoints.forEach((surfaceSet) => { - surfaceSet.realizationPoints.forEach((realSamplePoints: SurfaceRealizationSampleValues_api, rdx: number) => { - surfaceValues.push({ - name: surfaceSet.surfaceName, - showLabel: rdx === 0, - realization: realSamplePoints.realization, - values: realSamplePoints.sampled_values, - }); - }); - }); - const geolayerdata: SurfaceData = { - areas: [], - lines: surfaceValues.map((realizationValues) => { - return { - data: realizationValues.values.map((z: number, idx) => { - return [cumLength[idx], z]; - }), - color: stratigraphyColorMap[realizationValues.name] || "black", - id: realizationValues.name + realizationValues.realization, - label: realizationValues.showLabel ? realizationValues.name : "", - textColor: "black", - width: 1, - }; - }), - }; - return geolayerdata; -} - -function surfaceSamplePointsToStatisticalLayerData( - samplePoints: SurfaceRealizationSampleValuesData[], - cumLength: number[], - statisticFunctions: StatisticFunction_api[], - stratigraphyColorMap: StratigraphyColorMap -): SurfaceData { - const statisticLines: { name: string; statistic: string; values: number[]; color: string }[] = []; - - samplePoints.forEach((surfaceSet) => { - const allValues: number[][] = surfaceSet.realizationPoints.map((p) => p.sampled_values); - const numPoints = allValues[0]?.length || 0; - const statistics: any = { - MEAN: new Array(numPoints).fill(0), - MIN: new Array(numPoints).fill(Infinity), - MAX: new Array(numPoints).fill(-Infinity), - P10: new Array(numPoints).fill(0), - P50: new Array(numPoints).fill(0), - P90: new Array(numPoints).fill(0), - }; - - for (let i = 0; i < numPoints; i++) { - const valuesAtPosition = allValues.map((values) => values[i]); - if (valuesAtPosition.some((value) => value === null)) { - statistics.MEAN[i] = null; - statistics.MIN[i] = null; - statistics.MAX[i] = null; - statistics.P10[i] = null; - statistics.P50[i] = null; - statistics.P90[i] = null; - continue; - } - statistics.MEAN[i] = mean(valuesAtPosition); - statistics.MIN[i] = Math.min(...valuesAtPosition); - statistics.MAX[i] = Math.max(...valuesAtPosition); - statistics.P10[i] = percentile(valuesAtPosition, 10); - statistics.P50[i] = percentile(valuesAtPosition, 50); - statistics.P90[i] = percentile(valuesAtPosition, 90); - } - - Object.keys(statistics).forEach((stat) => { - if (!statisticFunctions.includes(stat as StatisticFunction_api)) { - return; - } - statisticLines.push({ - name: surfaceSet.surfaceName, - statistic: stat, - values: statistics[stat], - color: stratigraphyColorMap[surfaceSet.surfaceName] || "black", - }); - }); - }); - - const geolayerdata: SurfaceData = { - areas: [], - lines: statisticLines.map((line) => ({ - data: line.values.map((value, idx) => [cumLength[idx], value]), - color: line.color, - id: line.name + line.statistic, - label: line.statistic, - width: 1, - })), - }; - - return geolayerdata; - - function mean(values: number[]): number { - return values.reduce((a, b) => a + b, 0) / values.length; - } - - function percentile(values: number[], p: number): number { - values.sort((a, b) => a - b); - const position = ((values.length - 1) * p) / 100; - const base = Math.floor(position); - const rest = position - base; - if (values[base + 1] !== undefined) { - return values[base] + rest * (values[base + 1] - values[base]); - } else { - return values[base]; - } - } -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/interfaces.ts b/frontend/src/modules/StructuralUncertaintyIntersection/interfaces.ts deleted file mode 100644 index e065d5084..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/interfaces.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { StatisticFunction_api } from "@api"; -import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; -import { Wellbore } from "@framework/types/wellbore"; -import { - IntersectionSettings, - SurfaceSetAddress, - VisualizationMode, -} from "@modules/StructuralUncertaintyIntersection/typesAndEnums"; - -import { - intersectionSettingsAtom, - statisticFunctionsAtom, - stratigraphyColorMapAtom, - surfaceSetAddressAtom, - visualizationModeAtom, - wellboreAddressAtom, -} from "./settings/atoms/baseAtoms"; - -type SettingsToViewInterface = { - wellboreAddress: Wellbore | null; - surfaceSetAddress: SurfaceSetAddress | null; - visualizationMode: VisualizationMode; - stratigraphyColorMap: { [name: string]: string }; - statisticFunctions: StatisticFunction_api[]; - intersectionSettings: IntersectionSettings; -}; - -export type Interfaces = { - settingsToView: SettingsToViewInterface; -}; - -export const settingsToViewInterfaceInitialization: InterfaceInitialization = { - wellboreAddress: (get) => { - return get(wellboreAddressAtom); - }, - surfaceSetAddress: (get) => { - return get(surfaceSetAddressAtom); - }, - visualizationMode: (get) => { - return get(visualizationModeAtom); - }, - stratigraphyColorMap: (get) => { - return get(stratigraphyColorMapAtom); - }, - statisticFunctions: (get) => { - return get(statisticFunctionsAtom); - }, - intersectionSettings: (get) => { - return get(intersectionSettingsAtom); - }, -}; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx deleted file mode 100644 index a7a99ca58..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ModuleRegistry } from "@framework/ModuleRegistry"; - -import { Interfaces, settingsToViewInterfaceInitialization } from "./interfaces"; -import { Settings } from "./settings/settings"; -import { View } from "./view"; - -const module = ModuleRegistry.initModule("StructuralUncertaintyIntersection", { - settingsToViewInterfaceInitialization, -}); - -module.viewFC = View; -module.settingsFC = Settings; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/queryHooks.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/queryHooks.tsx deleted file mode 100644 index e2fb307fa..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/queryHooks.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { SurfaceRealizationSampleValues_api } from "@api"; -import { apiService } from "@framework/ApiService"; -import { UseQueryResult, useQueries } from "@tanstack/react-query"; - -const STALE_TIME = 60 * 1000; -const CACHE_TIME = 60 * 1000; - -export type SurfacePolyLineSpec = { - x_points: number[]; - y_points: number[]; - cum_length: number[]; -}; - -export type SurfaceRealizationSampleValues = { - data: Array; - isFetching: boolean; -}; -export type SurfaceRealizationSampleValuesData = { - surfaceName: string; - realizationPoints: SurfaceRealizationSampleValues_api[]; -}; -export function useSampleSurfaceInPointsQueries( - caseUuid: string, - ensembleName: string, - surfaceNames: string[], - surfaceAttribute: string, - realizationNums: number[], - x_points: number[], - y_points: number[], - allowEnable: boolean -): SurfaceRealizationSampleValues { - const isEnabled = !!( - allowEnable && - caseUuid && - ensembleName && - surfaceNames.length > 0 && - surfaceAttribute && - realizationNums.length > 0 && - x_points.length > 0 && - y_points.length > 0 - ); - - return useQueries({ - queries: - (isEnabled && - surfaceNames.map((surfaceName: string) => { - const bodySamplePoints = { sample_points: { x_points, y_points } }; - const queryKey = [ - "postSampleSurfaceInPoints", - caseUuid, - ensembleName, - surfaceName, - surfaceAttribute, - realizationNums, - bodySamplePoints, - ]; - - return { - queryKey, - queryFn: () => { - return apiService.surface.postSampleSurfaceInPoints( - caseUuid, - ensembleName, - surfaceName, - surfaceAttribute, - realizationNums, - bodySamplePoints - ); - }, - staleTime: STALE_TIME, - gcTime: CACHE_TIME, - }; - })) || - [], - combine: (results: UseQueryResult>[]) => ({ - data: results.map((result, index) => ({ - surfaceName: surfaceNames[index] ?? "", - realizationPoints: result.data ?? [], - })), - - isFetching: results.some((result) => result.isFetching), - }), - }); -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/registerModule.ts b/frontend/src/modules/StructuralUncertaintyIntersection/registerModule.ts deleted file mode 100644 index 01b2f042d..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/registerModule.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ModuleCategory, ModuleDevState } from "@framework/Module"; -import { ModuleDataTagId } from "@framework/ModuleDataTags"; -import { ModuleRegistry } from "@framework/ModuleRegistry"; -import { SyncSettingKey } from "@framework/SyncSettings"; - -import { Interfaces } from "./interfaces"; - -const description = "Visualization of structural uncertainty in an intersection"; - -ModuleRegistry.registerModule({ - moduleName: "StructuralUncertaintyIntersection", - defaultTitle: "Structural Uncertainty Intersection", - category: ModuleCategory.MAIN, - devState: ModuleDevState.DEV, - dataTagIds: [ModuleDataTagId.SURFACE, ModuleDataTagId.DRILLED_WELLS], - syncableSettingKeys: [SyncSettingKey.ENSEMBLE], - description, -}); diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/settings/atoms/baseAtoms.ts b/frontend/src/modules/StructuralUncertaintyIntersection/settings/atoms/baseAtoms.ts deleted file mode 100644 index a6c85fc5c..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/settings/atoms/baseAtoms.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { StatisticFunction_api } from "@api"; -import { Wellbore } from "@framework/types/wellbore"; -import { - IntersectionSettings, - SurfaceSetAddress, - VisualizationMode, -} from "@modules/StructuralUncertaintyIntersection/typesAndEnums"; - -import { atom } from "jotai"; - -export const wellboreAddressAtom = atom({ uwi: "55/33-A-4", uuid: "drogon_horizontal", type: "smda" }); -export const surfaceSetAddressAtom = atom(null); -export const visualizationModeAtom = atom(VisualizationMode.STATISTICAL_LINES); -export const statisticFunctionsAtom = atom([StatisticFunction_api.MEAN]); -export const stratigraphyColorMapAtom = atom<{ [name: string]: string }>({}); -export const intersectionSettingsAtom = atom({ extension: 1000, zScale: 5 }); diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/intersectionSettings.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/intersectionSettings.tsx deleted file mode 100644 index 2109cf506..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/intersectionSettings.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from "react"; - -import { Input } from "@lib/components/Input"; -import { Label } from "@lib/components/Label"; - -import { IntersectionSettings } from "../../typesAndEnums"; - -type IntersectionSettingsProps = { - intersectionSettings: IntersectionSettings; - onChange: (intersectionSettings: IntersectionSettings) => void; -}; - -export const IntersectionSettingsSelect: React.FC = (props) => { - const handleExtensionChange = (e: any) => { - const extension = parseInt(e.target.value, 10); - if (extension >= 10) { - props.onChange({ ...props.intersectionSettings, extension }); - } - }; - const handleZScaleChange = (e: any) => { - const zScale = parseInt(e.target.value, 10); - if (zScale >= 0) { - props.onChange({ ...props.intersectionSettings, zScale }); - } - }; - return ( - <> - - - - ); -}; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx deleted file mode 100644 index 1d34e9e3f..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/settings/components/realizationsSelect.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from "react"; - -import { Button } from "@lib/components/Button"; -import { Label } from "@lib/components/Label"; -import { Select } from "@lib/components/Select"; - -import { isEqual } from "lodash"; - -type RealizationsSelectProps = { - availableRealizations: number[] | null; - selectedRealizations: number[]; // Added as prop - onChange: (values: number[] | null) => void; -}; - -export const RealizationsSelect: React.FC = (props) => { - // Removed useState for selectedRealizations - - if (!isEqual(props.availableRealizations, props.selectedRealizations)) { - const newValues = - props.availableRealizations?.filter((value) => props.selectedRealizations.includes(value)) ?? []; - if (!isEqual(newValues, props.selectedRealizations)) { - props.onChange(newValues); - } - } - - const realOptions = props.availableRealizations?.map((real) => ({ label: `${real}`, value: `${real}` })) ?? []; - - const onChange = (values: string[]) => { - props.onChange(values.map((value) => parseInt(value))); - }; - - return ( - - - - - } - > -
- -
-
-
- - { - return { value: val, label: VisualizationModeEnumToStringMapping[val] }; - })} - onChange={handleVisualizationModeChange} - /> -
- -
-
- - - -
- ); -} - -function fixupSyncedOrSelectedOrFirstWellbore( - syncedWellbore: Wellbore | null, - selectedWellbore: Wellbore | null, - legalWellbores: Wellbore[] -): Wellbore | null { - const allUuids = legalWellbores.map((elm) => elm.uuid); - if (syncedWellbore && allUuids.includes(syncedWellbore.uuid)) { - return syncedWellbore; - } - if (selectedWellbore && allUuids.includes(selectedWellbore.uuid)) { - return selectedWellbore; - } - if (legalWellbores.length !== 0) { - return legalWellbores[0]; - } - return null; -} - -export function createStratigraphyColors(surfaceNames: string[], colorSet: ColorSet): StratigraphyColorMap { - const colorMap: StratigraphyColorMap = {}; - surfaceNames.forEach((surfaceName, index) => { - colorMap[surfaceName] = index === 0 ? colorSet.getFirstColor() : colorSet.getNextColor(); - }); - return colorMap; -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/state.ts b/frontend/src/modules/StructuralUncertaintyIntersection/state.ts deleted file mode 100644 index b6412c1ec..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/state.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { StatisticFunction_api } from "@api"; -import { Wellbore } from "@framework/types/wellbore"; - -import { IntersectionSettings, SurfaceSetAddress, VisualizationMode } from "./typesAndEnums"; - -export interface State { - wellboreAddress: Wellbore | null; - SurfaceSetAddress: SurfaceSetAddress | null; - visualizationMode: VisualizationMode; - stratigraphyColorMap: { [name: string]: string }; - statisticFunctions: StatisticFunction_api[]; - intersectionSettings: IntersectionSettings; -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts b/frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts deleted file mode 100644 index c89108351..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/typesAndEnums.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { StatisticFunction_api } from "@api"; - -export type SeismicAddress = { - caseUuid: string; - ensemble: string; - realizationNumber: number; - attribute: string; - observed: boolean; - timeString?: string; -}; -export type SurfaceSetAddress = { - caseUuid: string; - ensembleName: string; - names: string[]; - attribute: string; - realizationNums: number[] | null; -}; - - -export type IntersectionSettings = { - extension: number; - zScale: number; -}; - -export const StatisticFunctionEnumToStringMapping = { - [StatisticFunction_api.MEAN]: "Mean", - [StatisticFunction_api.MIN]: "Min", - [StatisticFunction_api.MAX]: "Max", - [StatisticFunction_api.P10]: "P10", - [StatisticFunction_api.P50]: "P50", - [StatisticFunction_api.P90]: "P90", -}; - -export enum VisualizationMode { - INDIVIDUAL_REALIZATIONS = "IndividualRealizations", - STATISTICAL_LINES = "StatisticalLines", - STATISTICS_AND_REALIZATIONS = "StatisticsAndRealizations", -} -export const VisualizationModeEnumToStringMapping = { - [VisualizationMode.INDIVIDUAL_REALIZATIONS]: "Individual realizations", - [VisualizationMode.STATISTICAL_LINES]: "Statistical lines", - [VisualizationMode.STATISTICS_AND_REALIZATIONS]: "Statistics + Realizations", -}; -export type StratigraphyColorMap = { [name: string]: string }; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx deleted file mode 100644 index d4e0767ba..000000000 --- a/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React from "react"; - -import { IntersectionReferenceSystem, Trajectory } from "@equinor/esv-intersection"; -import { ModuleViewProps } from "@framework/Module"; -import { useViewStatusWriter } from "@framework/StatusWriter"; -import { useElementSize } from "@lib/hooks/useElementSize"; -import { - makeExtendedTrajectoryFromTrajectoryXyzPoints, - makeTrajectoryXyzPointsFromWellboreTrajectory, -} from "@modules/SeismicIntersection/utils/esvIntersectionDataConversion"; -import { useWellboreTrajectoriesQuery } from "@modules/_shared/WellBore/queryHooks"; -import { ContentError } from "@modules/_shared/components/ContentMessage"; -import { usePropagateApiErrorToStatusWriter } from "@modules/_shared/hooks/usePropagateApiErrorToStatusWriter"; - -import { isEqual } from "lodash"; - -import Legend from "./components/Legend"; -import { EsvIntersection } from "./components/esvIntersection"; -import { Interfaces } from "./interfaces"; -import { useSampleSurfaceInPointsQueries } from "./queryHooks"; - -export const View = ({ viewContext }: ModuleViewProps) => { - const wrapperDivRef = React.useRef(null); - const wrapperDivSize = useElementSize(wrapperDivRef); - - const statusWriter = useViewStatusWriter(viewContext); - - const surfaceSetAddress = viewContext.useSettingsToViewInterfaceValue("surfaceSetAddress"); - const visualizationMode = viewContext.useSettingsToViewInterfaceValue("visualizationMode"); - const statisticFunctions = viewContext.useSettingsToViewInterfaceValue("statisticFunctions"); - const wellboreAddress = viewContext.useSettingsToViewInterfaceValue("wellboreAddress"); - const intersectionSettings = viewContext.useSettingsToViewInterfaceValue("intersectionSettings"); - const stratigraphyColorMap = viewContext.useSettingsToViewInterfaceValue("stratigraphyColorMap"); - - // Extended wellbore trajectory for creating intersection/fence extended on both sides of wellbore - const [extendedWellboreTrajectory, setExtendedWellboreTrajectory] = React.useState(null); - - const [renderWellboreTrajectoryXyzPoints, setRenderWellboreTrajectoryXyzPoints] = React.useState( - null - ); - const width = wrapperDivSize.width; - const height = wrapperDivSize.height - 100; - - // Get well trajectories query - const getWellTrajectoriesQuery = useWellboreTrajectoriesQuery(wellboreAddress ? [wellboreAddress.uuid] : undefined); - usePropagateApiErrorToStatusWriter(getWellTrajectoriesQuery, statusWriter); - - // Use first trajectory and create polyline for seismic fence query, and extended wellbore trajectory for generating seismic fence image - - if (getWellTrajectoriesQuery.data && getWellTrajectoriesQuery.data.length !== 0) { - const trajectoryXyzPoints = makeTrajectoryXyzPointsFromWellboreTrajectory(getWellTrajectoriesQuery.data[0]); - const newExtendedWellboreTrajectory = makeExtendedTrajectoryFromTrajectoryXyzPoints( - trajectoryXyzPoints, - intersectionSettings.extension - ); - - // If the new extended trajectory is different, update the polyline, but keep the seismic fence image - if (!isEqual(newExtendedWellboreTrajectory, extendedWellboreTrajectory)) { - setExtendedWellboreTrajectory(newExtendedWellboreTrajectory); - } - - // When new well trajectory 3D points are loaded, update the render trajectory and clear the seismic fence image - if (!isEqual(trajectoryXyzPoints, renderWellboreTrajectoryXyzPoints)) { - setRenderWellboreTrajectoryXyzPoints(trajectoryXyzPoints); - } - } - - const x_points = extendedWellboreTrajectory?.points.map((coord) => coord[0]) ?? []; - const y_points = extendedWellboreTrajectory?.points.map((coord) => coord[1]) ?? []; - - const cum_length = extendedWellboreTrajectory - ? IntersectionReferenceSystem.toDisplacement( - extendedWellboreTrajectory.points, - extendedWellboreTrajectory.offset - ).map((coord) => coord[0] - intersectionSettings.extension) - : []; - - const sampleSurfaceInPointsQueries = useSampleSurfaceInPointsQueries( - surfaceSetAddress?.caseUuid ?? "", - surfaceSetAddress?.ensembleName ?? "", - surfaceSetAddress?.names ?? [], - surfaceSetAddress?.attribute ?? "", - surfaceSetAddress?.realizationNums ?? [], - x_points, - y_points, - true - ); - - statusWriter.setLoading(getWellTrajectoriesQuery.isFetching || sampleSurfaceInPointsQueries.isFetching); - // Build up an error string handling multiple errors. e.g. "Error loading well trajectories and seismic fence data" - // Do not useMemo - let errorString = ""; - if (getWellTrajectoriesQuery.isError) { - errorString += "Error loading well trajectories"; - } - - if (errorString !== "") { - statusWriter.addError(errorString); - } - const stratigraphyColorLegendItems = - surfaceSetAddress?.names.map((key) => { - return { color: stratigraphyColorMap[key], label: key }; - }) ?? []; - - return ( -
- {errorString !== "" ? ( - {errorString} - ) : ( - <> - -
- -
- - )} -
- ); -}; diff --git a/frontend/src/modules/registerAllModules.ts b/frontend/src/modules/registerAllModules.ts index 3b12c8256..7b692eb3a 100644 --- a/frontend/src/modules/registerAllModules.ts +++ b/frontend/src/modules/registerAllModules.ts @@ -13,7 +13,6 @@ import "./Rft/registerModule"; import "./SeismicIntersection/registerModule"; import "./SimulationTimeSeries/registerModule"; import "./SimulationTimeSeriesSensitivity/registerModule"; -import "./StructuralUncertaintyIntersection/registerModule"; import "./SubsurfaceMap/registerModule"; import "./TornadoChart/registerModule"; import "./WellCompletions/registerModule"; From f6ccca195d08a00acf8b6ce9203410133d39ce5b Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:38:38 +0200 Subject: [PATCH 3/5] Bump fmu-sumo==1.2.5 (#706) --- backend_py/primary/poetry.lock | 10 +++++----- backend_py/primary/pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend_py/primary/poetry.lock b/backend_py/primary/poetry.lock index c0f418289..fda2cdab3 100644 --- a/backend_py/primary/poetry.lock +++ b/backend_py/primary/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -793,13 +793,13 @@ files = [ [[package]] name = "fmu-sumo" -version = "1.2.4" +version = "1.2.5" description = "Python package for interacting with Sumo in an FMU setting" optional = false python-versions = ">=3.8" files = [ - {file = "fmu_sumo-1.2.4-py3-none-any.whl", hash = "sha256:d76ce6de25fe1009a78fcbbe6bf9b4cfd3c410398a81d9f4c073bd3a8b29d6a5"}, - {file = "fmu_sumo-1.2.4.tar.gz", hash = "sha256:a9b422e08af271dca4e6293ae94b343329d59f302bbd44c8430a670e0b4be5c7"}, + {file = "fmu_sumo-1.2.5-py3-none-any.whl", hash = "sha256:11e39d548a220a891892b5af4f94bf059a7db36bf345665bcf18a61c935d396f"}, + {file = "fmu_sumo-1.2.5.tar.gz", hash = "sha256:8d9c5f65df070fd90b737d78f9a7e9c1590a1af0ec57d54645b682dd5ece9737"}, ] [package.dependencies] @@ -3513,4 +3513,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "9c20526602b7ab84f5102041476f2af5da6763caba9935954c67db132b3a38ba" +content-hash = "9e4c2116a592ae012bb3be220132f9465b018c80ebfa3b66c963f3c944965fcb" diff --git a/backend_py/primary/pyproject.toml b/backend_py/primary/pyproject.toml index 5d4d11e2e..f28c866de 100644 --- a/backend_py/primary/pyproject.toml +++ b/backend_py/primary/pyproject.toml @@ -20,7 +20,7 @@ numpy = "^1.24.1" orjson = "^3.8.10" pandas = {version = "2.0.1", extras = ["performance"]} httpx = "^0.24.0" -fmu-sumo = "1.2.4" +fmu-sumo = "1.2.5" sumo-wrapper-python = "1.0.9" azure-monitor-opentelemetry = "^1.1.0" requests-toolbelt = "^1.0.0" From 8d75a33ca370dd4409decf1ee35b9143c0fb3db9 Mon Sep 17 00:00:00 2001 From: Sigurd Pettersen Date: Mon, 26 Aug 2024 13:21:05 +0200 Subject: [PATCH 4/5] Removed VTK user session and related code (#696) Co-authored-by: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> --- backend_py/primary/primary/main.py | 2 - .../primary/routers/grid3d/router_vtk.py | 187 ------------ .../primary/routers/grid3d/schemas_vtk.py | 25 -- .../user_session_manager.py | 7 - backend_py/user_grid3d_vtk/Dockerfile | 26 -- backend_py/user_grid3d_vtk/poetry.lock | 271 ------------------ backend_py/user_grid3d_vtk/pyproject.toml | 12 - .../user_grid3d_vtk/fastapi_app.py | 55 ---- backend_py/user_mock/pyproject.toml | 2 +- docker-compose.yml | 12 - frontend/src/api/index.ts | 2 - .../src/api/models/GridIntersectionVtk.ts | 14 - frontend/src/api/models/GridSurfaceVtk.ts | 17 -- frontend/src/api/services/Grid3DService.ts | 164 ----------- frontend/src/modules/_shared/vtkUtils.ts | 235 --------------- frontend/src/modules/registerAllModules.ts | 1 - radixconfig.yml | 9 - 17 files changed, 1 insertion(+), 1040 deletions(-) delete mode 100644 backend_py/primary/primary/routers/grid3d/router_vtk.py delete mode 100644 backend_py/primary/primary/routers/grid3d/schemas_vtk.py delete mode 100644 backend_py/user_grid3d_vtk/Dockerfile delete mode 100644 backend_py/user_grid3d_vtk/poetry.lock delete mode 100644 backend_py/user_grid3d_vtk/pyproject.toml delete mode 100644 backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py delete mode 100644 frontend/src/api/models/GridIntersectionVtk.ts delete mode 100644 frontend/src/api/models/GridSurfaceVtk.ts delete mode 100644 frontend/src/modules/_shared/vtkUtils.ts diff --git a/backend_py/primary/primary/main.py b/backend_py/primary/primary/main.py index 4a5e12d7e..441fd3e8a 100644 --- a/backend_py/primary/primary/main.py +++ b/backend_py/primary/primary/main.py @@ -18,7 +18,6 @@ from primary.routers.general import router as general_router from primary.routers.graph.router import router as graph_router from primary.routers.grid3d.router import router as grid3d_router -from primary.routers.grid3d.router_vtk import router as grid3d_router_vtk from primary.routers.group_tree.router import router as group_tree_router from primary.routers.inplace_volumetrics.router import router as inplace_volumetrics_router from primary.routers.observations.router import router as observations_router @@ -79,7 +78,6 @@ def custom_generate_unique_id(route: APIRoute) -> str: app.include_router(parameters_router, prefix="/parameters", tags=["parameters"]) app.include_router(correlations_router, prefix="/correlations", tags=["correlations"]) app.include_router(grid3d_router, prefix="/grid3d", tags=["grid3d"]) -app.include_router(grid3d_router_vtk, prefix="/grid3d", tags=["grid3d"]) app.include_router(group_tree_router, prefix="/group_tree", tags=["group_tree"]) app.include_router(pvt_router, prefix="/pvt", tags=["pvt"]) app.include_router(well_completions_router, prefix="/well_completions", tags=["well_completions"]) diff --git a/backend_py/primary/primary/routers/grid3d/router_vtk.py b/backend_py/primary/primary/routers/grid3d/router_vtk.py deleted file mode 100644 index dba4f65e2..000000000 --- a/backend_py/primary/primary/routers/grid3d/router_vtk.py +++ /dev/null @@ -1,187 +0,0 @@ -from typing import List - -from fastapi import APIRouter, Depends, Query, HTTPException, status -from starlette.requests import Request - -from primary.services.utils.authenticated_user import AuthenticatedUser -from primary.auth.auth_helper import AuthHelper - -from .schemas_vtk import GridSurfaceVtk, GridIntersectionVtk - -router = APIRouter() - -# pylint: disable=unused-argument -# pylint: disable=unused-variable - - -# Primary backend -@router.get("/grid_surface_vtk") -async def grid_surface_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - realization: str = Query(description="Realization"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> GridSurfaceVtk: - """Get a grid""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "realization": int(realization), - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/grid_parameter_vtk") -async def grid_parameter_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realization: str = Query(description="Realization"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> List[float]: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realization": int(realization), - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/grid_parameter_intersection_vtk") -async def grid_parameter_intersection_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realization: str = Query(description="Realization"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> GridIntersectionVtk: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realization": int(realization), - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/statistical_grid_parameter_intersection_vtk") -async def statistical_grid_parameter_intersection_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realizations: List[str] = Query(description="Realizations"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> GridIntersectionVtk: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realizations": [int(realization) for realization in realizations], - } - - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) - - -@router.get("/statistical_grid_parameter_vtk") -async def statistical_grid_parameter_vtk( - request: Request, - case_uuid: str = Query(description="Sumo case uuid"), - ensemble_name: str = Query(description="Ensemble name"), - grid_name: str = Query(description="Grid name"), - parameter_name: str = Query(description="Grid parameter"), - realizations: List[str] = Query(description="Realizations"), - authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user), -) -> List[float]: - """Get a grid parameter""" - - query_params = { - "case_uuid": case_uuid, - "ensemble_name": ensemble_name, - "grid_name": grid_name, - "parameter_name": parameter_name, - "realizations": [int(realization) for realization in realizations], - } - # Add query parameters to the request URL - updated_request = Request( - scope={ - "type": "http", - "method": request.method, - "path": request.url.path, - "query_string": request.url.include_query_params(**query_params).query.encode("utf-8"), - "headers": request.headers.raw, - }, - receive=request._receive, # pylint: disable=protected-access - ) - - raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED) diff --git a/backend_py/primary/primary/routers/grid3d/schemas_vtk.py b/backend_py/primary/primary/routers/grid3d/schemas_vtk.py deleted file mode 100644 index 1e91d0b4d..000000000 --- a/backend_py/primary/primary/routers/grid3d/schemas_vtk.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import List - -from pydantic import BaseModel -from webviz_pkg.core_utils.b64 import B64FloatArray, B64UintArray - - -class GridSurfaceVtk(BaseModel): - polys_b64arr: B64UintArray - points_b64arr: B64FloatArray - xmin: float - xmax: float - ymin: float - ymax: float - zmin: float - zmax: float - - -class GridIntersectionVtk(BaseModel): - image: str - polyline_x: List[float] - polyline_y: List[float] - x_min: float - x_max: float - y_min: float - y_max: float diff --git a/backend_py/primary/primary/services/user_session_manager/user_session_manager.py b/backend_py/primary/primary/services/user_session_manager/user_session_manager.py index 626a75921..8ec90de3c 100644 --- a/backend_py/primary/primary/services/user_session_manager/user_session_manager.py +++ b/backend_py/primary/primary/services/user_session_manager/user_session_manager.py @@ -20,7 +20,6 @@ class UserComponent(str, Enum): GRID3D_RI = "GRID3D_RI" - GRID3D_VTK = "GRID3D_VTK" MOCK = "MOCK" @@ -53,12 +52,6 @@ class _UserSessionDef: resource_req=RadixResourceRequests(cpu="4", memory="16Gi"), payload_dict={"ri_omp_num_treads": 4}, ), - UserComponent.GRID3D_VTK: _UserSessionDef( - job_component_name="user-grid3d-vtk", - port=8003, - resource_req=RadixResourceRequests(cpu="200m", memory="400Mi"), - payload_dict=None, - ), } diff --git a/backend_py/user_grid3d_vtk/Dockerfile b/backend_py/user_grid3d_vtk/Dockerfile deleted file mode 100644 index 199ffc8d1..000000000 --- a/backend_py/user_grid3d_vtk/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM python:3.11-slim - -RUN useradd --create-home --uid 1234 appuser -USER 1234 - -ENV PATH="${PATH}:/home/appuser/.local/bin" - -RUN python3 -m pip install --user pipx -RUN python3 -m pipx ensurepath -RUN pipx install poetry==1.8.2 - -ENV VIRTUAL_ENV=/home/appuser/venv -RUN python3 -m venv $VIRTUAL_ENV -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -WORKDIR /home/appuser/backend_py/user_grid3d_vtk - -COPY --chown=appuser ./backend_py/user_grid3d_vtk/pyproject.toml /home/appuser/backend_py/user_grid3d_vtk -COPY --chown=appuser ./backend_py/user_grid3d_vtk/poetry.lock /home/appuser/backend_py/user_grid3d_vtk -RUN poetry install --only main --no-root --no-directory - -COPY --chown=appuser ./backend_py/user_grid3d_vtk/user_grid3d_vtk /home/appuser/backend_py/user_grid3d_vtk/user_grid3d_vtk -RUN poetry install --only main - -# Relevant uvicorn environment variables are: UVICORN_PORT, UVICORN_RELOAD -CMD ["uvicorn", "user_grid3d_vtk.fastapi_app:app", "--host", "0.0.0.0"] diff --git a/backend_py/user_grid3d_vtk/poetry.lock b/backend_py/user_grid3d_vtk/poetry.lock deleted file mode 100644 index 97fcd2ff8..000000000 --- a/backend_py/user_grid3d_vtk/poetry.lock +++ /dev/null @@ -1,271 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.6.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, -] - -[[package]] -name = "anyio" -version = "3.7.1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "fastapi" -version = "0.103.2" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.7" -files = [ - {file = "fastapi-0.103.2-py3-none-any.whl", hash = "sha256:3270de872f0fe9ec809d4bd3d4d890c6d5cc7b9611d721d6438f9dacc8c4ef2e"}, - {file = "fastapi-0.103.2.tar.gz", hash = "sha256:75a11f6bfb8fc4d2bec0bd710c2d5f2829659c0e8c0afd5560fdda6ce25ec653"}, -] - -[package.dependencies] -anyio = ">=3.7.1,<4.0.0" -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.27.0,<0.28.0" -typing-extensions = ">=4.5.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "idna" -version = "3.6" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, -] - -[[package]] -name = "pydantic" -version = "2.6.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, - {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.16.3" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.16.3" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, - {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, - {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, - {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, - {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, - {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, - {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, - {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, - {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, - {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, - {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, - {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, - {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, - {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "starlette" -version = "0.27.0" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.7" -files = [ - {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, - {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] - -[[package]] -name = "typing-extensions" -version = "4.10.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, -] - -[[package]] -name = "uvicorn" -version = "0.20.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, - {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.11" -content-hash = "1c1900c103a60137142a48f7698b943ffebf2eab40628b1bf69c65fa232af688" diff --git a/backend_py/user_grid3d_vtk/pyproject.toml b/backend_py/user_grid3d_vtk/pyproject.toml deleted file mode 100644 index 53cad62ac..000000000 --- a/backend_py/user_grid3d_vtk/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -[tool.poetry] -package-mode = false -name = "user-grid3d-vtk" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - -[tool.poetry.dependencies] -python = "^3.11" -fastapi = "^0.103.1" -uvicorn = "^0.20.0" diff --git a/backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py b/backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py deleted file mode 100644 index b4de8582d..000000000 --- a/backend_py/user_grid3d_vtk/user_grid3d_vtk/fastapi_app.py +++ /dev/null @@ -1,55 +0,0 @@ -import asyncio -import datetime -import logging -from typing import Annotated - -from fastapi import FastAPI -from fastapi import Query - -logging.basicConfig(format="%(asctime)s %(levelname)-3s [%(name)s]: %(message)s", datefmt="%H:%M:%S") -logging.getLogger().setLevel(logging.DEBUG) - -LOGGER = logging.getLogger(__name__) - - -app = FastAPI() - - -@app.get("/") -async def root() -> str: - ret_str = f"user-grid3d-vtk is alive at this time: {datetime.datetime.now()}" - LOGGER.debug("Sending: ", ret_str) - return ret_str - - -# Probe if service is alive -# HTTP status code 200 means we're alive, all other status codes indicate trouble -# The response is only for debugging, and is basically ignored. -@app.get("/health/live") -async def health_live() -> str: - ret_str = f"LIVE at: {datetime.datetime.now()}" - LOGGER.debug(f"health_live() returning: {ret_str!r}") - return ret_str - - -# Probe if service is ready to receive requests -# HTTP status code 200 means we're ready, 500 (and all other status codes) signals we're not ready -# The response is only for debugging, and is basically ignored. -@app.get("/health/ready") -async def health_ready() -> str: - ret_str = f"READY at: {datetime.datetime.now()}" - LOGGER.debug(f"health_ready() returning: {ret_str!r}") - return ret_str - - -# Simulate doing some work -@app.get("/dowork") -async def dowork( - duration: Annotated[float, Query(description="Duration of work in seconds")] = 1.0, -) -> str: - LOGGER.debug(f"dowork() doing fake GRID3D VTK work for: {duration=}s") - await asyncio.sleep(duration) - - ret_str = f"GRID3D VTK work done at: {datetime.datetime.now()}" - LOGGER.debug(f"dowork() GRID3D VTK returning: {ret_str!r}") - return ret_str diff --git a/backend_py/user_mock/pyproject.toml b/backend_py/user_mock/pyproject.toml index 53cad62ac..740c0d586 100644 --- a/backend_py/user_mock/pyproject.toml +++ b/backend_py/user_mock/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] package-mode = false -name = "user-grid3d-vtk" +name = "user-mock" [build-system] requires = ["poetry-core"] diff --git a/docker-compose.yml b/docker-compose.yml index 89c2287f8..9e69fffef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,18 +81,6 @@ services: - ./backend_py/user_grid3d_ri/user_grid3d_ri:/home/appuser/backend_py/user_grid3d_ri/user_grid3d_ri - ./backend_py/libs:/home/appuser/backend_py/libs - user-grid3d-vtk: - build: - context: . - dockerfile: ./backend_py/user_grid3d_vtk/Dockerfile - ports: - - 8003:8003 - environment: - - UVICORN_PORT=8003 - - UVICORN_RELOAD=true - volumes: - - ./backend_py/user_grid3d_vtk:/home/appuser/backend_py/user_grid3d_vtk - redis-user-session: image: bitnami/redis:6.2.10@sha256:bd42fcdab5959ce2b21b6ea8410d4b3ee87ecb2e320260326ec731ecfcffbd0e expose: diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 2a591446f..af145a030 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -40,8 +40,6 @@ export type { Grid3dMappedProperty as Grid3dMappedProperty_api } from './models/ export type { Grid3dPropertyInfo as Grid3dPropertyInfo_api } from './models/Grid3dPropertyInfo'; export type { Grid3dZone as Grid3dZone_api } from './models/Grid3dZone'; export type { GridDimensions as GridDimensions_api } from './models/GridDimensions'; -export type { GridIntersectionVtk as GridIntersectionVtk_api } from './models/GridIntersectionVtk'; -export type { GridSurfaceVtk as GridSurfaceVtk_api } from './models/GridSurfaceVtk'; export type { GroupTreeData as GroupTreeData_api } from './models/GroupTreeData'; export type { GroupTreeMetadata as GroupTreeMetadata_api } from './models/GroupTreeMetadata'; export type { HTTPValidationError as HTTPValidationError_api } from './models/HTTPValidationError'; diff --git a/frontend/src/api/models/GridIntersectionVtk.ts b/frontend/src/api/models/GridIntersectionVtk.ts deleted file mode 100644 index 1e4672669..000000000 --- a/frontend/src/api/models/GridIntersectionVtk.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type GridIntersectionVtk = { - image: string; - polyline_x: Array; - polyline_y: Array; - x_min: number; - x_max: number; - y_min: number; - y_max: number; -}; - diff --git a/frontend/src/api/models/GridSurfaceVtk.ts b/frontend/src/api/models/GridSurfaceVtk.ts deleted file mode 100644 index 00065a55d..000000000 --- a/frontend/src/api/models/GridSurfaceVtk.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { B64FloatArray } from './B64FloatArray'; -import type { B64UintArray } from './B64UintArray'; -export type GridSurfaceVtk = { - polys_b64arr: B64UintArray; - points_b64arr: B64FloatArray; - xmin: number; - xmax: number; - ymin: number; - ymax: number; - zmin: number; - zmax: number; -}; - diff --git a/frontend/src/api/services/Grid3DService.ts b/frontend/src/api/services/Grid3DService.ts index cfd1e1f3e..24075137d 100644 --- a/frontend/src/api/services/Grid3DService.ts +++ b/frontend/src/api/services/Grid3DService.ts @@ -6,8 +6,6 @@ import type { Body_post_get_polyline_intersection } from '../models/Body_post_ge import type { Grid3dGeometry } from '../models/Grid3dGeometry'; import type { Grid3dInfo } from '../models/Grid3dInfo'; import type { Grid3dMappedProperty } from '../models/Grid3dMappedProperty'; -import type { GridIntersectionVtk } from '../models/GridIntersectionVtk'; -import type { GridSurfaceVtk } from '../models/GridSurfaceVtk'; import type { PolylineIntersection } from '../models/PolylineIntersection'; import type { CancelablePromise } from '../core/CancelablePromise'; import type { BaseHttpRequest } from '../core/BaseHttpRequest'; @@ -208,166 +206,4 @@ export class Grid3DService { }, }); } - /** - * Grid Surface Vtk - * Get a grid - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param realization Realization - * @returns GridSurfaceVtk Successful Response - * @throws ApiError - */ - public gridSurfaceVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - realization: string, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/grid_surface_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'realization': realization, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Grid Parameter Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realization Realization - * @returns number Successful Response - * @throws ApiError - */ - public gridParameterVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realization: string, - ): CancelablePromise> { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/grid_parameter_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realization': realization, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Grid Parameter Intersection Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realization Realization - * @returns GridIntersectionVtk Successful Response - * @throws ApiError - */ - public gridParameterIntersectionVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realization: string, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/grid_parameter_intersection_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realization': realization, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Statistical Grid Parameter Intersection Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realizations Realizations - * @returns GridIntersectionVtk Successful Response - * @throws ApiError - */ - public statisticalGridParameterIntersectionVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realizations: Array, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/statistical_grid_parameter_intersection_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realizations': realizations, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Statistical Grid Parameter Vtk - * Get a grid parameter - * @param caseUuid Sumo case uuid - * @param ensembleName Ensemble name - * @param gridName Grid name - * @param parameterName Grid parameter - * @param realizations Realizations - * @returns number Successful Response - * @throws ApiError - */ - public statisticalGridParameterVtk( - caseUuid: string, - ensembleName: string, - gridName: string, - parameterName: string, - realizations: Array, - ): CancelablePromise> { - return this.httpRequest.request({ - method: 'GET', - url: '/grid3d/statistical_grid_parameter_vtk', - query: { - 'case_uuid': caseUuid, - 'ensemble_name': ensembleName, - 'grid_name': gridName, - 'parameter_name': parameterName, - 'realizations': realizations, - }, - errors: { - 422: `Validation Error`, - }, - }); - } } diff --git a/frontend/src/modules/_shared/vtkUtils.ts b/frontend/src/modules/_shared/vtkUtils.ts deleted file mode 100644 index 71e13bc79..000000000 --- a/frontend/src/modules/_shared/vtkUtils.ts +++ /dev/null @@ -1,235 +0,0 @@ -//Based on code by Kitware Inc. (BSD-3-Clause) -// Copyright (c) 2016, Kitware Inc. -// All rights reserved. - -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: - -// - Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. - -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. - -// - Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. - -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/* eslint-disable no-bitwise */ - -// ---------------------------------------------------------------------------- -// Decoding infrastructure -// ---------------------------------------------------------------------------- - - -/** - * Convert a Base64 string to an ArrayBuffer. - * @param {string} b64Str - * @return An ArrayBuffer object. - */ - - -/** - * Convert a Base64 string to an ArrayBuffer. - * @param {string} b64Str - * @return An ArrayBuffer object. - */ - -// interface Base64 { -// toArrayBuffer: typeof toArrayBuffer, -// fromArrayBuffer: typeof fromArrayBuffer, -// } - -// export default Base64; -const REVERSE_LOOKUP: any = []; -REVERSE_LOOKUP['-'.charCodeAt(0)] = 62; -REVERSE_LOOKUP['_'.charCodeAt(0)] = 63; - -const BASE64_CODE: any = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; -for (let i = 0; i < BASE64_CODE.length; i++) { - REVERSE_LOOKUP[BASE64_CODE.charCodeAt(i)] = i; -} - -// ---------------------------------------------------------------------------- -// Base64 analysis -// ---------------------------------------------------------------------------- - -function isValidChar(c: any) { - return REVERSE_LOOKUP[c.charCodeAt(0)] !== undefined; -} - -function extractChunks(b64Str: any) { - const strSize = b64Str.length; - const chunks = []; - - let currentChunk: any = null; - for (let i = 0; i < strSize; i++) { - if (isValidChar(b64Str[i])) { - if (!currentChunk) { - currentChunk = { start: i, count: 0 }; - } - currentChunk.count++; - currentChunk.end = i; - } else if (b64Str[i] === '=' && currentChunk) { - // End of chunk (found padding char) - chunks.push(currentChunk); - currentChunk = null; - } - } - - if (currentChunk) { - chunks.push(currentChunk); - } - return chunks; -} - -function writeChunk(b64Str: any, chunk: any, dstOffset: any, uint8: any) { - const { start, count } = chunk; - const remain = count % 4; - const fourCharProcessCount = Math.floor(count / 4); - let charIdx = start; - let tmp = null; - let offset = dstOffset; - - // Handle 4=>3 - for (let i = 0; i < fourCharProcessCount; i++) { - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp = REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 18; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 12; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 6; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)]; - - uint8[offset++] = (tmp >> 16) & 0xff; - uint8[offset++] = (tmp >> 8) & 0xff; - uint8[offset++] = tmp & 0xff; - } - - // Handle remain - switch (remain) { - case 3: - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp = REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 10; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 4; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] >> 2; - uint8[offset++] = (tmp >> 8) & 0xff; - uint8[offset++] = tmp & 0xff; - break; - case 2: - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp = REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] << 2; - while (!isValidChar(b64Str[charIdx])) { - charIdx++; - } - tmp |= REVERSE_LOOKUP[b64Str.charCodeAt(charIdx++)] >> 4; - uint8[offset++] = tmp & 0xff; - break; - case 1: - throw new Error('BASE64: remain 1 should not happen'); - case 0: - break; - default: - break; - } - console.debug("offset", offset) - - return offset; -} - -export function toArrayBuffer(b64Str: any): ArrayBuffer { - console.debug(b64Str) - b64Str = b64Str["bvals"] - - const chunks = extractChunks(b64Str); - console.debug(chunks) - const totalEncodedLength = chunks[chunks.length - 1].end + 1; - const padding = (4 - (totalEncodedLength % 4)) % 4; // -length mod 4 - // Any padding chars in the middle of b64Str is to be interpreted as \x00, - // whereas the terminating padding chars are to be interpreted as literal padding. - const totalSize = ((totalEncodedLength + padding) * 3) / 4 - padding; - - const arrayBuffer = new ArrayBuffer(totalSize); - const view = new Uint8Array(arrayBuffer); - let dstOffset = 0; - for (let i = 0; i < chunks.length; i++) { - dstOffset += writeChunk(b64Str, chunks[i], dstOffset, view); - dstOffset += (4 - (chunks[i].count % 4)) % 4; - } - - return arrayBuffer; -} - -function encodeTriplet(v1: any, v2: any, v3: any) { - const triplet = (v1 << 16) + (v2 << 8) + v3; - return ( - BASE64_CODE[triplet >> 18] + - BASE64_CODE[(triplet >> 12) & 0x3f] + - BASE64_CODE[(triplet >> 6) & 0x3f] + - BASE64_CODE[triplet & 0x3f] - ); -} - -export function fromArrayBuffer(ab: any) { - const uint8 = new Uint8Array(ab); - const leftoverLength = ab.byteLength % 3; - const maxTripletIndex = ab.byteLength - leftoverLength; - const segments = Array(maxTripletIndex / 3); - for (let i = 0; i < segments.length; i++) { - const bufOffset = i * 3; - segments[i] = encodeTriplet( - uint8[bufOffset], - uint8[bufOffset + 1], - uint8[bufOffset + 2] - ); - } - if (leftoverLength > 0) { - const segment = encodeTriplet( - uint8[maxTripletIndex], - uint8[maxTripletIndex + 1] || 0, - uint8[maxTripletIndex + 2] || 0 - ); - if (leftoverLength === 1) { - segments.push(`${segment.substr(0, 2)}==`); - } else if (leftoverLength === 2) { - segments.push(`${segment.substr(0, 3)}=`); - } - } - return segments.join(''); -} - -export default { - toArrayBuffer, - fromArrayBuffer, -}; \ No newline at end of file diff --git a/frontend/src/modules/registerAllModules.ts b/frontend/src/modules/registerAllModules.ts index 7b692eb3a..dece6eeaf 100644 --- a/frontend/src/modules/registerAllModules.ts +++ b/frontend/src/modules/registerAllModules.ts @@ -3,7 +3,6 @@ import { isDevMode } from "@lib/utils/devMode"; import "./3DViewer/registerModule"; import "./DistributionPlot/registerModule"; import "./FlowNetwork/registerModule"; -// import "./Grid3DVTK/registerModule"; import "./InplaceVolumetrics/registerModule"; import "./Intersection/registerModule"; import "./Map/registerModule"; diff --git a/radixconfig.yml b/radixconfig.yml index 2600fba0c..ed7ef7a17 100644 --- a/radixconfig.yml +++ b/radixconfig.yml @@ -187,15 +187,6 @@ spec: payload: path: /compute/args - - name: user-grid3d-vtk - dockerFileName: ./backend_py/user_grid3d_vtk/Dockerfile - schedulerPort: 8003 - ports: - - name: http - port: 8003 - variables: - UVICORN_PORT: 8003 - dnsAppAlias: environment: prod component: frontend From 8f148ee41e19827e8977de32c7e422cb007faade Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Tue, 27 Aug 2024 17:18:33 +0200 Subject: [PATCH 5/5] Adjustments --- .../components/SortableList/sortableList.tsx | 452 +++++++++--------- .../SortableList/sortableListGroup.tsx | 1 + .../SortableList/sortableListItem.tsx | 19 +- 3 files changed, 243 insertions(+), 229 deletions(-) diff --git a/frontend/src/lib/components/SortableList/sortableList.tsx b/frontend/src/lib/components/SortableList/sortableList.tsx index 9ddd0e8c0..8d5a1f61e 100644 --- a/frontend/src/lib/components/SortableList/sortableList.tsx +++ b/frontend/src/lib/components/SortableList/sortableList.tsx @@ -10,96 +10,11 @@ import { isEqual } from "lodash"; import { SortableListGroupProps } from "./sortableListGroup"; import { SortableListItemProps } from "./sortableListItem"; -export enum HoveredArea { - TOP = "top", - BOTTOM = "bottom", - HEADER = "header", - CENTER = "center", -} - -type ElementWithInfos = { - element: HTMLElement; - id: string; - type: ItemType | null; - parentId: string | null; - parentType: ItemType | null; -}; - -type HoveredElementWithInfos = ElementWithInfos & { - area: HoveredArea; -}; - -export type SortableListContextType = { - draggedElementId: string | null; - hoveredElementId: string | null; - hoveredArea: HoveredArea | null; - dragPosition: Vec2 | null; -}; - -export const SortableListContext = React.createContext({ - draggedElementId: null, - hoveredElementId: null, - hoveredArea: null, - dragPosition: null, -}); - -function assertTargetIsSortableListItemAndExtractProps( - target: EventTarget | null -): { element: HTMLElement; id: string; parentElement: HTMLElement | null; parentId: string | null } | null { - if (!target) { - return null; - } - - const element = target as HTMLElement; - if (!element || !(element instanceof HTMLElement)) { - return null; - } - - const sortableListItemIndicator = element.closest(".sortable-list-element-indicator"); - if (!sortableListItemIndicator) { - return null; - } - - const sortableListElement = element.closest(".sortable-list-element"); - if (!sortableListElement) { - return null; - } - - if (!(sortableListElement instanceof HTMLElement)) { - return null; - } - - const id = sortableListElement.dataset.itemId; - if (!id) { - return null; - } - - const parentElement = sortableListElement.parentElement; - - if ( - parentElement && - parentElement instanceof HTMLElement && - parentElement.classList.contains("sortable-list-group") - ) { - const parentId = parentElement.dataset.itemId; - if (parentId) { - return { element: sortableListElement, id, parentElement: parentElement, parentId }; - } - } - - return { element: sortableListElement, id, parentElement: null, parentId: null }; -} - export enum ItemType { ITEM = "item", GROUP = "group", } -type HoveredItemIdAndArea = { - id: string; - area: HoveredArea; -}; - export type IsMoveAllowedArgs = { movedItemId: string; movedItemType: ItemType | null; @@ -111,6 +26,7 @@ export type IsMoveAllowedArgs = { const ITEM_TOP_AND_CENTER_AREA_SIZE_IN_PERCENT = 50; const GROUP_TOP_AND_CENTER_AREA_SIZE_IN_PERCENT = 30; +const DEFAULT_SCROLL_TIME = 100; export type SortableListProps = { contentWhenEmpty?: React.ReactNode; @@ -160,7 +76,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { } React.useEffect( - function handleMount() { + function addEventHandlers() { if (!listDivRef.current) { return; } @@ -176,11 +92,9 @@ export function SortableList(props: SortableListProps): React.ReactNode { let scrollTimeout: ReturnType | null = null; let doScroll: boolean = false; - let currentScrollTime = 100; + let currentScrollTime = DEFAULT_SCROLL_TIME; function handlePointerDown(e: PointerEvent) { - e.preventDefault(); - e.stopPropagation(); const target = e.target; if (!target) { return; @@ -204,7 +118,6 @@ export function SortableList(props: SortableListProps): React.ReactNode { pointerDownPosition = { x: e.clientX, y: e.clientY }; draggingActive = false; - setIsDragging(true); pointerDownPositionRelativeToElement = { x: e.clientX - element.getBoundingClientRect().left, @@ -213,7 +126,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { document.addEventListener("pointermove", handlePointerMove); document.addEventListener("pointerup", handlePointerUp); - setIsDragging(true); + e.preventDefault(); } function maybeScroll(position: Vec2) { @@ -264,27 +177,8 @@ export function SortableList(props: SortableListProps): React.ReactNode { } } - function getDragElementsRecursively(parent?: HTMLElement): HTMLElement[] { - const items: HTMLElement[] = []; - const parentElement = parent ?? currentListDivRef; - - for (const child of parentElement.children) { - if (child instanceof HTMLElement && child.classList.contains("sortable-list-item")) { - items.push(child); - } - if (child instanceof HTMLElement && child.classList.contains("sortable-list-group")) { - items.push(child); - const content = child.querySelector(".sortable-list-group-content"); - if (content && content instanceof HTMLElement) { - items.push(...getDragElementsRecursively(content)); - } - } - } - return items; - } - function getHoveredElementAndArea(e: PointerEvent): { element: HTMLElement; area: HoveredArea } | null { - const elements = getDragElementsRecursively(); + const elements = getDragElementsRecursively(currentListDivRef); for (const element of elements) { if (rectContainsPoint(element.getBoundingClientRect(), vec2FromPointerEvent(e))) { const type = getItemType(element); @@ -302,6 +196,8 @@ export function SortableList(props: SortableListProps): React.ReactNode { return { element, area: getHoveredAreaOfItem(element, e) }; } } + + // If no element was found, check if the pointer is in the bottom area of the main list const directChildren = elements.filter((el) => el.parentElement === currentListDivRef); if ( mainDivRef.current && @@ -313,62 +209,6 @@ export function SortableList(props: SortableListProps): React.ReactNode { return null; } - function getItemType(item: HTMLElement): ItemType | null { - if (item.classList.contains("sortable-list-item")) { - return ItemType.ITEM; - } else if (item.classList.contains("sortable-list-group")) { - return ItemType.GROUP; - } - return null; - } - - function getHoveredAreaOfItem(item: HTMLElement, e: PointerEvent): HoveredArea { - let factor = ITEM_TOP_AND_CENTER_AREA_SIZE_IN_PERCENT / 100; - if (getItemType(item) === ItemType.GROUP) { - factor = GROUP_TOP_AND_CENTER_AREA_SIZE_IN_PERCENT / 100; - } - const rect = item.getBoundingClientRect(); - const topAreaTop = rect.top; - const topAreaBottom = rect.top + factor * rect.height; - - if (e.clientY >= topAreaTop && e.clientY <= topAreaBottom) { - return HoveredArea.TOP; - } - - const bottomAreaTop = rect.bottom - factor * rect.height; - const bottomAreaBottom = rect.bottom; - - if (e.clientY >= bottomAreaTop && e.clientY <= bottomAreaBottom) { - return HoveredArea.BOTTOM; - } - - const headerElement = item.querySelector(".sortable-list-item-header"); - if (headerElement) { - const headerRect = headerElement.getBoundingClientRect(); - if (rectContainsPoint(headerRect, { x: e.clientX, y: e.clientY })) { - return HoveredArea.HEADER; - } - } - - return HoveredArea.CENTER; - } - - function getItemParent(item: HTMLElement): HTMLElement | null { - const group = item.parentElement?.closest(".sortable-list-group"); - if (!group || !(group instanceof HTMLElement)) { - return null; - } - return group; - } - - function getItemParentGroupId(item: HTMLElement): string | null { - const group = getItemParent(item); - if (!group) { - return null; - } - return group.dataset.itemId ?? null; - } - function getItemPositionInGroup(item: HTMLElement): number { let group = item.parentElement?.closest(".sortable-list-group-content"); if (!group || !(group instanceof HTMLElement)) { @@ -403,6 +243,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { point2Distance(pointerDownPosition, { x: e.clientX, y: e.clientY }) > MANHATTAN_LENGTH ) { draggingActive = true; + setIsDragging(true); setDraggedItemId(draggedElement.id); } @@ -426,60 +267,88 @@ export function SortableList(props: SortableListProps): React.ReactNode { } const hoveredElementAndArea = getHoveredElementAndArea(e); - if (hoveredElementAndArea) { - const { element: hoveredElement, area } = hoveredElementAndArea; - const itemType = getItemType(hoveredElement); - if (itemType === ItemType.ITEM && (area === HoveredArea.CENTER || area === HoveredArea.HEADER)) { - currentlyHoveredElement = null; - setHoveredItemIdAndArea(null); - return; - } + if (!hoveredElementAndArea) { + currentlyHoveredElement = null; + setHoveredItemIdAndArea(null); + return; + } - const parentElement = getItemParent(hoveredElement); - const parentType = parentElement ? getItemType(parentElement) : null; + const positionDelta = hoveredElementAndArea.area === HoveredArea.TOP ? 0 : 1; + const newPosition = getItemPositionInGroup(hoveredElementAndArea.element) + positionDelta; + const currentPosition = getItemPositionInGroup(draggedElement.element); + const hoveredItemParentGroupId = getItemParentGroupId(hoveredElementAndArea.element); + + console.debug( + "newPosition", + newPosition, + "currentPosition", + currentPosition, + "positionDelta", + positionDelta, + "draggedElementParentGroupId", + draggedElement.parentId, + "hoveredItemParentGroupId", + hoveredItemParentGroupId + ); - let destinationType = parentType; - let destinationId = getItemParentGroupId(hoveredElement); + if ( + draggedElement.parentId === getItemParentGroupId(hoveredElementAndArea.element) && + (newPosition === currentPosition || newPosition === currentPosition + 1) + ) { + currentlyHoveredElement = null; + setHoveredItemIdAndArea(null); + return; + } - if (itemType === ItemType.GROUP) { - if (area === HoveredArea.HEADER) { - destinationType = ItemType.GROUP; - destinationId = hoveredElement.dataset.itemId ?? ""; - } - if (area === HoveredArea.CENTER) { - destinationType = ItemType.GROUP; - destinationId = hoveredElement.dataset.itemId ?? ""; - } - } + const { element: hoveredElement, area } = hoveredElementAndArea; + const itemType = getItemType(hoveredElement); + if (itemType === ItemType.ITEM && (area === HoveredArea.CENTER || area === HoveredArea.HEADER)) { + currentlyHoveredElement = null; + setHoveredItemIdAndArea(null); + return; + } - if ( - isMoveAllowed !== undefined && - !isMoveAllowed({ - movedItemId: draggedElement.id, - movedItemType: draggedElement.type, - originId: draggedElement.parentId, - originType: draggedElement.parentType, - destinationId, - destinationType, - }) - ) { - currentlyHoveredElement = null; - setHoveredItemIdAndArea(null); - return; + const parentElement = getItemParent(hoveredElement); + const parentType = parentElement ? getItemType(parentElement) : null; + + let destinationType = parentType; + let destinationId = getItemParentGroupId(hoveredElement); + + if (itemType === ItemType.GROUP) { + if (area === HoveredArea.HEADER) { + destinationType = ItemType.GROUP; + destinationId = hoveredElement.dataset.itemId ?? ""; } - setHoveredItemIdAndArea({ id: hoveredElement.dataset.itemId ?? "", area }); - currentlyHoveredElement = { - element: hoveredElement, - id: hoveredElement.dataset.itemId ?? "", - type: itemType, - area, - parentId: destinationId, - parentType: destinationType, - }; - } else { + if (area === HoveredArea.CENTER) { + destinationType = ItemType.GROUP; + destinationId = hoveredElement.dataset.itemId ?? ""; + } + } + + if ( + isMoveAllowed !== undefined && + !isMoveAllowed({ + movedItemId: draggedElement.id, + movedItemType: draggedElement.type, + originId: draggedElement.parentId, + originType: draggedElement.parentType, + destinationId, + destinationType, + }) + ) { currentlyHoveredElement = null; setHoveredItemIdAndArea(null); + return; } + setHoveredItemIdAndArea({ id: hoveredElement.dataset.itemId ?? "", area }); + currentlyHoveredElement = { + element: hoveredElement, + id: hoveredElement.dataset.itemId ?? "", + type: itemType, + area, + parentId: destinationId, + parentType: destinationType, + }; } function maybeCallItemMoveCallback() { @@ -560,7 +429,7 @@ export function SortableList(props: SortableListProps): React.ReactNode { document.addEventListener("keydown", handleKeyDown); window.addEventListener("blur", handleWindowBlur); - return function handleUnmount() { + return function removeEventHandlers() { currentListDivRef.removeEventListener("pointerdown", handlePointerDown); document.removeEventListener("pointermove", handlePointerMove); document.removeEventListener("pointerup", handlePointerUp); @@ -643,3 +512,158 @@ export function SortableList(props: SortableListProps): React.ReactNode {
); } + +export enum HoveredArea { + TOP = "top", + BOTTOM = "bottom", + HEADER = "header", + CENTER = "center", +} + +type ElementWithInfos = { + element: HTMLElement; + id: string; + type: ItemType | null; + parentId: string | null; + parentType: ItemType | null; +}; + +type HoveredElementWithInfos = ElementWithInfos & { + area: HoveredArea; +}; + +type HoveredItemIdAndArea = { + id: string; + area: HoveredArea; +}; + +export type SortableListContextType = { + draggedElementId: string | null; + hoveredElementId: string | null; + hoveredArea: HoveredArea | null; + dragPosition: Vec2 | null; +}; + +export const SortableListContext = React.createContext({ + draggedElementId: null, + hoveredElementId: null, + hoveredArea: null, + dragPosition: null, +}); + +function assertTargetIsSortableListItemAndExtractProps( + target: EventTarget | null +): { element: HTMLElement; id: string; parentElement: HTMLElement | null; parentId: string | null } | null { + if (!target) { + return null; + } + + const element = target as HTMLElement; + if (!element || !(element instanceof HTMLElement)) { + return null; + } + + const sortableListItemIndicator = element.closest(".sortable-list-element-indicator"); + if (!sortableListItemIndicator) { + return null; + } + + const sortableListElement = element.closest(".sortable-list-element"); + if (!sortableListElement) { + return null; + } + + if (!(sortableListElement instanceof HTMLElement)) { + return null; + } + + const id = sortableListElement.dataset.itemId; + if (!id) { + return null; + } + + const parentGroup = getItemParent(sortableListElement); + + if (parentGroup) { + const parentId = parentGroup.dataset.itemId; + if (parentId) { + return { element: sortableListElement, id, parentElement: parentGroup, parentId }; + } + } + + return { element: sortableListElement, id, parentElement: null, parentId: null }; +} + +function getItemType(item: HTMLElement): ItemType | null { + if (item.classList.contains("sortable-list-item")) { + return ItemType.ITEM; + } else if (item.classList.contains("sortable-list-group")) { + return ItemType.GROUP; + } + return null; +} + +function getHoveredAreaOfItem(item: HTMLElement, e: PointerEvent): HoveredArea { + let factor = ITEM_TOP_AND_CENTER_AREA_SIZE_IN_PERCENT / 100; + if (getItemType(item) === ItemType.GROUP) { + factor = GROUP_TOP_AND_CENTER_AREA_SIZE_IN_PERCENT / 100; + } + const rect = item.getBoundingClientRect(); + const topAreaTop = rect.top; + const topAreaBottom = rect.top + factor * rect.height; + + if (e.clientY >= topAreaTop && e.clientY <= topAreaBottom) { + return HoveredArea.TOP; + } + + const bottomAreaTop = rect.bottom - factor * rect.height; + const bottomAreaBottom = rect.bottom; + + if (e.clientY >= bottomAreaTop && e.clientY <= bottomAreaBottom) { + return HoveredArea.BOTTOM; + } + + const headerElement = item.querySelector(".sortable-list-item-header"); + if (headerElement) { + const headerRect = headerElement.getBoundingClientRect(); + if (rectContainsPoint(headerRect, { x: e.clientX, y: e.clientY })) { + return HoveredArea.HEADER; + } + } + + return HoveredArea.CENTER; +} + +function getItemParent(item: HTMLElement): HTMLElement | null { + const group = item.parentElement?.closest(".sortable-list-group"); + if (!group || !(group instanceof HTMLElement)) { + return null; + } + return group; +} + +function getItemParentGroupId(item: HTMLElement): string | null { + const group = getItemParent(item); + if (!group) { + return null; + } + return group.dataset.itemId ?? null; +} + +function getDragElementsRecursively(parentElement: HTMLElement): HTMLElement[] { + const items: HTMLElement[] = []; + + for (const child of parentElement.children) { + if (child instanceof HTMLElement && child.classList.contains("sortable-list-item")) { + items.push(child); + } + if (child instanceof HTMLElement && child.classList.contains("sortable-list-group")) { + items.push(child); + const content = child.querySelector(".sortable-list-group-content"); + if (content && content instanceof HTMLElement) { + items.push(...getDragElementsRecursively(content)); + } + } + } + return items; +} diff --git a/frontend/src/lib/components/SortableList/sortableListGroup.tsx b/frontend/src/lib/components/SortableList/sortableListGroup.tsx index 127d83d41..e47ef8ff9 100644 --- a/frontend/src/lib/components/SortableList/sortableListGroup.tsx +++ b/frontend/src/lib/components/SortableList/sortableListGroup.tsx @@ -27,6 +27,7 @@ export type SortableListGroupProps = { * @param {boolean} props.initiallyExpanded Whether the group should be expanded by default. * @param {React.ReactNode} props.startAdornment Start adornment to display to the left of the title. * @param {React.ReactNode} props.endAdornment End adornment to display to the right of the title. + * @param {React.ReactNode} props.contentWhenEmpty Content to display when the group is empty. * @param {React.ReactNode} props.children Child components to display as the content of the list item. * * @returns {React.ReactNode} A sortable list group component. diff --git a/frontend/src/lib/components/SortableList/sortableListItem.tsx b/frontend/src/lib/components/SortableList/sortableListItem.tsx index c9429b9ea..7fbe6e5d2 100644 --- a/frontend/src/lib/components/SortableList/sortableListItem.tsx +++ b/frontend/src/lib/components/SortableList/sortableListItem.tsx @@ -11,10 +11,9 @@ import { SortableListDropIndicator } from "./sortableListDropIndicator"; export type SortableListItemProps = { id: string; title: React.ReactNode; - initiallyExpanded?: boolean; startAdornment?: React.ReactNode; endAdornment?: React.ReactNode; - children: React.ReactNode; + children?: React.ReactNode; }; /** @@ -29,8 +28,6 @@ export type SortableListItemProps = { * @returns {React.ReactNode} A sortable list item component. */ export function SortableListItem(props: SortableListItemProps): React.ReactNode { - const [isExpanded, setIsExpanded] = React.useState(props.initiallyExpanded ?? true); - const divRef = React.useRef(null); const boundingClientRect = useElementBoundingRect(divRef); @@ -40,10 +37,6 @@ export function SortableListItem(props: SortableListItemProps): React.ReactNode const isDragging = sortableListContext.draggedElementId === props.id; const dragPosition = sortableListContext.dragPosition; - function handleToggleExpanded() { - setIsExpanded(!isExpanded); - } - return ( <> {isHovered && sortableListContext.hoveredArea === HoveredArea.TOP && } @@ -57,7 +50,7 @@ export function SortableListItem(props: SortableListItemProps): React.ReactNode hidden: !isDragging, })} >
-
+
{isDragging && dragPosition && createPortal( @@ -71,12 +64,10 @@ export function SortableListItem(props: SortableListItemProps): React.ReactNode width: isDragging ? boundingClientRect.width : undefined, }} > -
+
)} -
- {props.children} -
+
{props.children}
{isHovered && sortableListContext.hoveredArea === HoveredArea.BOTTOM && } @@ -85,8 +76,6 @@ export function SortableListItem(props: SortableListItemProps): React.ReactNode type HeaderProps = { title: React.ReactNode; - expanded: boolean; - onToggleExpanded?: () => void; startAdornment?: React.ReactNode; endAdornment?: React.ReactNode; };