Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
aa86c91
feat(save_and_load): add save/load methods to all stores for project …
MaxNumerique Oct 27, 2025
5b2b4f3
Apply prepare changes
MaxNumerique Oct 28, 2025
f4ef491
cartStore becomes geodeStore
MaxNumerique Oct 28, 2025
5f3e0c6
Merge branch 'feat/save_and_load' of https://github.com/Geode-solutio…
MaxNumerique Oct 28, 2025
e6c54b5
Apply prepare changes
MaxNumerique Oct 28, 2025
01a9732
renames and move tests
MaxNumerique Oct 28, 2025
0b70454
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
MaxNumerique Oct 28, 2025
d577721
persist upload folder
MaxNumerique Oct 29, 2025
df419ec
renamed app_store store to app
MaxNumerique Oct 29, 2025
bb9d02d
rm unused var
MaxNumerique Oct 29, 2025
a93225c
importStore & exportStore refacto
MaxNumerique Oct 30, 2025
af41f9d
Apply prepare changes
MaxNumerique Oct 30, 2025
ea63e2b
testes with import/export
MaxNumerique Oct 30, 2025
97072b5
Merge branch 'feat/save_and_load' of https://github.com/Geode-solutio…
MaxNumerique Oct 30, 2025
63464b7
Apply prepare changes
MaxNumerique Oct 30, 2025
98991cf
test
MaxNumerique Oct 30, 2025
6321c8f
Merge branch 'feat/save_and_load' of https://github.com/Geode-solutio…
MaxNumerique Oct 30, 2025
35c75dd
Apply prepare changes
MaxNumerique Oct 30, 2025
22a67fe
test
MaxNumerique Oct 30, 2025
caf5a39
Merge branch 'feat/save_and_load' of https://github.com/Geode-solutio…
MaxNumerique Oct 30, 2025
18fc885
Apply prepare changes
MaxNumerique Oct 30, 2025
d4a1cd6
exportStores/importStores and createTestingPinia
MaxNumerique Oct 31, 2025
8aec8b8
Apply prepare changes
MaxNumerique Oct 31, 2025
6a282e4
export working
MaxNumerique Oct 31, 2025
196be52
Merge branch 'feat/save_and_load' of https://github.com/Geode-solutio…
MaxNumerique Oct 31, 2025
fdf547e
nothing appear in camera
MaxNumerique Nov 1, 2025
c208b30
Apply prepare changes
MaxNumerique Nov 1, 2025
3ef456f
test
MaxNumerique Nov 3, 2025
2ca553b
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
MaxNumerique Nov 3, 2025
9fd6c0b
Apply prepare changes
MaxNumerique Nov 3, 2025
3806d54
feat(import_project): add 2 buttons : Export and import
MaxNumerique Nov 3, 2025
3d04f42
Merge branch 'feat/save_and_load' of https://github.com/Geode-solutio…
MaxNumerique Nov 3, 2025
24eb5b6
test
MaxNumerique Nov 3, 2025
9866872
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWe…
MaxNumerique Nov 3, 2025
f01890f
Apply prepare changes
MaxNumerique Nov 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions composables/project_manager.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JulienChampagnol que penses tu de cette proposition ?

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"
import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"
import fileDownload from "js-file-download"

export function useProjectManager() {
const geode = useGeodeStore()
const appStore = useAppStore()

async function exportProject() {
geode.start_request()
try {
await useInfraStore().create_connection()
const snapshot = appStore.exportStores()
const schema = back_schemas.opengeodeweb_back.export_project
const defaultName = "project.zip"

await api_fetch(
{ schema, params: { snapshot, filename: defaultName } },
{
response_function: async (response) => {
const data = response._data
const downloadName =
response.headers?.get?.("new-file-name") || defaultName
fileDownload(data, downloadName)
},
},
)
} finally {
geode.stop_request()
}
}

async function importProjectFile(file) {
geode.start_request()
try {
const infra = useInfraStore()
await infra.create_connection()

const form = new FormData()
form.append("file", file, file.name || "project.zip")

const importPath =
back_schemas.opengeodeweb_back.import_project?.$id ||
"opengeodeweb_back/import_project"

const result = await $fetch(importPath, {
baseURL: geode.base_url,
method: "POST",
body: form,
})

await viewer_call({
schema: viewer_schemas.opengeodeweb_viewer.import_project,
params: {},
})
await viewer_call({
schema: viewer_schemas.opengeodeweb_viewer.viewer.reset_visualization,
params: {},
})

await appStore.importStores(result.snapshot)

const dataBaseStore = useDataBaseStore()
for (const [id, item] of Object.entries(dataBaseStore.items)) {
const registerSchema =
item.object_type === "model"
? viewer_schemas.opengeodeweb_viewer.model.register
: viewer_schemas.opengeodeweb_viewer.mesh.register
await viewer_call({ schema: registerSchema, params: { id } })

if (item.vtk_js?.binary_light_viewable) {
await viewer_call({
schema: viewer_schemas.opengeodeweb_viewer.viewer.update_data,
params: {
id,
vtk_js: {
binary_light_viewable: item.vtk_js.binary_light_viewable,
},
},
})
}

if (item.viewable_filename) {
await viewer_call({
schema: viewer_schemas.opengeodeweb_viewer.viewer.update_data,
params: {
id,
vtk_js: { viewable_file_name: item.viewable_filename },
},
})
}
}
} finally {
geode.stop_request()
}
}

return { exportProject, importProjectFile }
}

export default useProjectManager
File renamed without changes.
46 changes: 17 additions & 29 deletions stores/app_store.js → stores/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,69 @@ export const useAppStore = defineStore("app", () => {
const isAlreadyRegistered = stores.some(
(registeredStore) => registeredStore.$id === store.$id,
)

if (isAlreadyRegistered) {
console.log(
`[AppStore] Store "${store.$id}" already registered, skipping`,
)
return
}

console.log("[AppStore] Registering store", store.$id)
stores.push(store)
}

function save() {
function exportStores() {
const snapshot = {}
let savedCount = 0
let exportCount = 0

for (const store of stores) {
if (!store.save) {
if (!store.exportStores) {
continue
}
const storeId = store.$id
try {
snapshot[storeId] = store.save()
savedCount++
snapshot[storeId] = store.exportStores()
exportCount++
} catch (error) {
console.error(`[AppStore] Error saving store "${storeId}":`, error)
console.error(`[AppStore] Error exporting store "${storeId}":`, error)
}
}

console.log(`[AppStore] Saved ${savedCount} stores`)
console.log(`[AppStore] Exported ${exportCount} stores`)
return snapshot
}

function load(snapshot) {
async function importStores(snapshot) {
if (!snapshot) {
console.warn("[AppStore] load called with invalid snapshot")
console.warn("[AppStore] import called with invalid snapshot")
return
}

let loadedCount = 0
let importedCount = 0
const notFoundStores = []

for (const store of stores) {
if (!store.load) {
continue
}

if (!store.importStores) continue
const storeId = store.$id

if (!snapshot[storeId]) {
notFoundStores.push(storeId)
continue
}

try {
store.load(snapshot[storeId])
loadedCount++
await store.importStores(snapshot[storeId])
importedCount++
} catch (error) {
console.error(`[AppStore] Error loading store "${storeId}":`, error)
console.error(`[AppStore] Error importing store "${storeId}":`, error)
}
}

if (notFoundStores.length > 0) {
console.warn(
`[AppStore] Stores not found in snapshot: ${notFoundStores.join(", ")}`,
)
}

console.log(`[AppStore] Loaded ${loadedCount} stores`)
console.log(`[AppStore] Imported ${importedCount} stores`)
}

return {
stores,
registerStore,
save,
load,
exportStores,
importStores,
}
})
17 changes: 17 additions & 0 deletions stores/data_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ export const useDataBaseStore = defineStore("dataBase", () => {
return flat_indexes.filter((index) => index !== null)
}

function exportStores() {
return { db: JSON.parse(JSON.stringify(db)) }
}

async function importStores(snapshot) {
const entries = snapshot?.db || {}
const hybrid_store = useHybridViewerStore()
await hybrid_store.initHybridViewer()
hybrid_store.clear()
for (const [id, item] of Object.entries(entries)) {
await registerObject(id)
await addItem(id, item)
}
}

return {
db,
itemMetaDatas,
Expand All @@ -148,5 +163,7 @@ export const useDataBaseStore = defineStore("dataBase", () => {
getSurfacesUuids,
getBlocksUuids,
getFlatIndexes,
exportStores,
importStores,
}
})
38 changes: 34 additions & 4 deletions stores/data_style.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import useDataStyleState from "../internal_stores/data_style_state.js"
import useMeshStyle from "../internal_stores/mesh/index.js"
import useModelStyle from "../internal_stores/model/index.js"
import { defineStore } from "pinia"
import { useDataBaseStore } from "./data_base.js"

export const useDataStyleStore = defineStore("dataStyle", () => {
/** States **/
Expand All @@ -24,13 +26,31 @@ export const useDataStyleStore = defineStore("dataStyle", () => {
return Promise.all(promise_array)
}

function setVisibility(id, visibility) {
const object_type = dataBaseStore.itemMetaDatas(id).object_type
// useDataStyleStore: setVisibility
function setVisibility(payloadOrId, visibility) {
const id =
typeof payloadOrId === "string"
? payloadOrId
: (payloadOrId?.id ?? payloadOrId?.data_id ?? payloadOrId?.model_id)
if (!id) return Promise.resolve([])

const visible =
typeof visibility === "boolean"
? visibility
: payloadOrId?.visible != null
? !!payloadOrId.visible
: true

const meta = dataBaseStore.itemMetaDatas(id)
const object_type = meta?.object_type
if (!object_type) return Promise.resolve([])

if (object_type === "mesh") {
return Promise.all([meshStyleStore.setMeshVisibility(id, visibility)])
return Promise.all([meshStyleStore.setMeshVisibility(id, visible)])
} else if (object_type === "model") {
return Promise.all([modelStyleStore.setModelVisibility(id, visibility)])
return Promise.all([modelStyleStore.setModelVisibility(id, visible)])
}
return Promise.resolve([])
}

function setModelEdgesVisibility(id, visibility) {
Expand All @@ -46,12 +66,22 @@ export const useDataStyleStore = defineStore("dataStyle", () => {
return modelStyleStore.modelMeshComponentVisibility(id, "Edge", null)
}

function exportStores() {
return { styles: dataStyleState.styles }
}

async function importStores(snapshot) {
dataStyleState.styles = snapshot?.styles || {}
}

return {
...dataStyleState,
addDataStyle,
setVisibility,
setModelEdgesVisibility,
modelEdgesVisibility,
exportStores,
importStores,
...meshStyleStore,
...modelStyleStore,
}
Expand Down
22 changes: 22 additions & 0 deletions stores/hybrid_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
remoteRender()
}

function clear() {
const renderer = genericRenderWindow.value.getRenderer()
const actors = renderer.getActors()
actors.forEach((actor) => renderer.removeActor(actor))
genericRenderWindow.value.getRenderWindow().render()
Object.keys(db).forEach((id) => delete db[id])
}

function exportStores() {
return { zScale: zScale.value }
}

async function importStores(snapshot) {
const z_scale = snapshot?.zScale
if (z_scale != null) {
await setZScaling(z_scale)
}
}

return {
db,
genericRenderWindow,
Expand All @@ -195,5 +214,8 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
resize,
setContainer,
zScale,
clear,
exportStores,
importStores,
}
})
23 changes: 23 additions & 0 deletions stores/treeview.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,27 @@ export const useTreeviewStore = defineStore("treeview", () => {
panelWidth.value = width
}

function exportStores() {
return {
isAdditionnalTreeDisplayed: isAdditionnalTreeDisplayed.value,
panelWidth: panelWidth.value,
model_id: model_id.value,
isTreeCollection: isTreeCollection.value,
selectedTree: selectedTree.value,
selection: selection.value,
}
}

async function importStores(snapshot) {
selection.value = snapshot?.selection || []
isAdditionnalTreeDisplayed.value =
snapshot?.isAdditionnalTreeDisplayed || false
panelWidth.value = snapshot?.panelWidth || 300
model_id.value = snapshot?.model_id || ""
isTreeCollection.value = snapshot?.isTreeCollection || false
selectedTree.value = snapshot?.selectedTree || null
}

return {
items,
selection,
Expand All @@ -67,5 +88,7 @@ export const useTreeviewStore = defineStore("treeview", () => {
displayFileTree,
toggleTreeView,
setPanelWidth,
exportStores,
importStores,
}
})
1 change: 0 additions & 1 deletion tests/integration/microservices/back/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@
# pip-compile --output-file=tests/integration/microservices/back/requirements.txt tests/integration/microservices/back/requirements.in
#

opengeodeweb-back==5.*,>=5.11.1rc2
1 change: 0 additions & 1 deletion tests/integration/microservices/viewer/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@
# pip-compile --output-file=tests/integration/microservices/viewer/requirements.txt tests/integration/microservices/viewer/requirements.in
#

opengeodeweb-viewer==1.*,>=1.11.6rc4
Loading
Loading