diff --git a/app/components/FileUploader.vue b/app/components/FileUploader.vue index 83712a4d..757ddce4 100644 --- a/app/components/FileUploader.vue +++ b/app/components/FileUploader.vue @@ -1,36 +1,59 @@ - - + + + + { :accept :loading :show-extensions="false" - :inline="false" + :inline="!internal_files.length" :show-overlay="showOverlay" @files-selected="processSelectedFiles" /> @@ -132,23 +169,50 @@ watch(internal_files, (value) => { - - mdi-file-outline - {{ file.name }} - - mdi-close-circle - - + + + + {{ + isCsv(file) + ? file.isConfigured + ? "mdi-file-check" + : "mdi-file-table" + : "mdi-file-outline" + }} + + {{ file.displayName || file.name }} + + + + + mdi-cog + + + + + + mdi-close-circle + + + @@ -164,24 +228,40 @@ watch(internal_files, (value) => { @click="upload_files" > mdi-cloud-upload - Upload {{ internal_files.length }} files + Upload + {{ internal_files.length }} + files + + diff --git a/app/components/MissingFilesSelector.vue b/app/components/MissingFilesSelector.vue index f81a3b89..55776c7f 100644 --- a/app/components/MissingFilesSelector.vue +++ b/app/components/MissingFilesSelector.vue @@ -9,10 +9,11 @@ const schema = schemas.opengeodeweb_back.missing_files; const emit = defineEmits(["update_values", "increment_step", "decrement_step"]); -const { multiple, geode_object_type, filenames } = defineProps({ +const { multiple, geode_object_type, filenames, files } = defineProps({ multiple: { type: Boolean, required: true }, geode_object_type: { type: String, required: true }, filenames: { type: Array, required: true }, + files: { type: Array, required: false, default: () => [] }, }); const accept = ref(""); @@ -34,12 +35,17 @@ async function missing_files() { additional_files.value = []; const geodeStore = useGeodeStore(); - const promise_array = filenames.map(async (filename) => { - const response = await geodeStore.request(schema, { - geode_object_type, - filename, - }); - return response; + const promise_array = filenames.map((filename) => { + const isCsvFile = + filename.toLowerCase().endsWith(".csv") || filename.toLowerCase().endsWith(".csv.json"); + if (isCsvFile) { + return Promise.resolve({ + has_missing_files: false, + mandatory_files: [], + additional_files: [], + }); + } + return geodeStore.request(schema, { geode_object_type, filename }); }); const values = await Promise.all(promise_array); for (const value of values) { @@ -49,11 +55,19 @@ async function missing_files() { mandatory_files.value = [...mandatory_files.value, ...value.mandatory_files]; additional_files.value = [...additional_files.value, ...value.additional_files]; } - if (has_missing_files.value) { - accept.value = [...mandatory_files.value, ...additional_files.value] - .map((filename) => `.${filename.split(".").pop()}`) - .join(","); - } else { + const unconfigured_csvs = files.filter( + (file) => + (file.name.toLowerCase().endsWith(".csv") || file.name.toLowerCase().endsWith(".csv.json")) && + !file.isConfigured, + ); + if (unconfigured_csvs.length > 0) { + has_missing_files.value = true; + if (accept.value === "") { + accept.value = ".json"; + } + } + + if (!has_missing_files.value) { emit("increment_step"); } toggle_loading(); @@ -85,7 +99,10 @@ await missing_files(); - + diff --git a/app/components/ObjectSelector.vue b/app/components/ObjectSelector.vue index d06b8af8..157882f3 100644 --- a/app/components/ObjectSelector.vue +++ b/app/components/ObjectSelector.vue @@ -62,12 +62,20 @@ async function get_allowed_objects() { const load_scores = allowed_objects_list.map((obj) => obj[key].is_loadable); const priorities = allowed_objects_list .map((obj) => obj[key].object_priority) - .filter((priority) => priority !== undefined && priority !== null); + .filter((priority) => priority !== undefined); final_object[key] = { is_loadable: Math.min(...load_scores) }; if (priorities.length > 0) { final_object[key].object_priority = Math.max(...priorities); } } + const isCsv = filenames.some( + (filename) => + filename.toLowerCase().endsWith(".csv") || filename.toLowerCase().endsWith(".csv.json"), + ); + if (isCsv && !final_object["PointSet3D"]) { + final_object["PointSet3D"] = { is_loadable: 1, object_priority: 100 }; + } + allowed_objects.value = final_object; const selected_object = select_geode_object(final_object); if (selected_object) { diff --git a/app/components/csv-preview/CsvPreviewer.vue b/app/components/csv-preview/CsvPreviewer.vue new file mode 100644 index 00000000..66f6cd94 --- /dev/null +++ b/app/components/csv-preview/CsvPreviewer.vue @@ -0,0 +1,277 @@ + + + + + + + + + CSV Previewer & Configuration + + + + + + + + + + + + + + + + + + + Cancel + + + Confirm + + + + + + + diff --git a/app/components/csv-preview/CsvSettings.vue b/app/components/csv-preview/CsvSettings.vue new file mode 100644 index 00000000..f20485c1 --- /dev/null +++ b/app/components/csv-preview/CsvSettings.vue @@ -0,0 +1,170 @@ + + + + + Parser Settings + + + + + + + + Row Configuration + + + + + + + + Spatial Mapping + + + + + + + + + + + diff --git a/app/components/csv-preview/CsvTable.vue b/app/components/csv-preview/CsvTable.vue new file mode 100644 index 00000000..4a6c3c53 --- /dev/null +++ b/app/components/csv-preview/CsvTable.vue @@ -0,0 +1,112 @@ + + + + + + + + Live Preview (First 100 rows) + + + + + Parsing... + + + + + + + {{ item[header.key] }} + + + + + + + No preview available + Check your parser settings + + + + + + + diff --git a/app/utils/import_workflow.js b/app/utils/import_workflow.js index d316d184..5f656faa 100644 --- a/app/utils/import_workflow.js +++ b/app/utils/import_workflow.js @@ -9,26 +9,16 @@ import { useGeodeStore } from "@ogw_front/stores/geode"; import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer"; import { useTreeviewStore } from "@ogw_front/stores/treeview"; -const SECOND = 1000; - async function importWorkflow(files) { - console.log("importWorkflow", { files }); - const start = Date.now(); - const promise_array = []; - for (const file of files) { - const { filename, geode_object_type } = file; - console.log({ filename }, { geode_object_type }); - promise_array.push(importFile(filename, geode_object_type)); - } - const results = await Promise.all(promise_array); + const results = await Promise.all( + files.map(({ filename, geode_object_type }) => importFile(filename, geode_object_type)), + ); const hybridViewerStore = useHybridViewerStore(); hybridViewerStore.remoteRender(); - console.log("importWorkflow completed in", (Date.now() - start) / SECOND); return results; } function buildImportItemFromPayloadApi(value, geode_object_type) { - console.log("buildImportItemFromPayloadApi", { value, geode_object_type }); return { geode_object_type, ...value, diff --git a/tests/unit/components/CrsSelector.nuxt.test.js b/tests/unit/components/CrsSelector.nuxt.test.js index 158fa651..9a0fec18 100644 --- a/tests/unit/components/CrsSelector.nuxt.test.js +++ b/tests/unit/components/CrsSelector.nuxt.test.js @@ -17,7 +17,7 @@ describe("crs selector", () => { beforeEach(() => { pinia = setupActivePinia(); geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; }); test("default behavior", async () => { diff --git a/tests/unit/components/ExtensionSelector.nuxt.test.js b/tests/unit/components/ExtensionSelector.nuxt.test.js index 4bb32e5f..6f60fb41 100644 --- a/tests/unit/components/ExtensionSelector.nuxt.test.js +++ b/tests/unit/components/ExtensionSelector.nuxt.test.js @@ -21,7 +21,7 @@ const geodeStore = useGeodeStore(); describe("extension selector", () => { beforeEach(() => { - geodeStore.base_url = ""; + geodeStore.base_url = "/"; geodeStore.request = vi.fn(() => { const response = { diff --git a/tests/unit/components/FileSelector.nuxt.test.js b/tests/unit/components/FileSelector.nuxt.test.js index 83f01dab..f22131fe 100644 --- a/tests/unit/components/FileSelector.nuxt.test.js +++ b/tests/unit/components/FileSelector.nuxt.test.js @@ -21,7 +21,7 @@ const upload_file_schema = schemas.opengeodeweb_back.upload_file; describe("file selector", () => { const pinia = setupActivePinia(); const geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; test("select file", async () => { registerEndpoint(allowed_files_schema.$id, { @@ -52,6 +52,7 @@ describe("file selector", () => { writable: true, }); await v_file_input.trigger("change"); + await flushPromises(); const v_btn = wrapper.findComponent(components.VBtn); await v_btn.trigger("click"); await flushPromises(); diff --git a/tests/unit/components/FileUploader.nuxt.test.js b/tests/unit/components/FileUploader.nuxt.test.js index a80baa4a..4d1b7edf 100644 --- a/tests/unit/components/FileUploader.nuxt.test.js +++ b/tests/unit/components/FileUploader.nuxt.test.js @@ -18,7 +18,7 @@ const upload_file_schema = schemas.opengeodeweb_back.upload_file; describe("file uploader", () => { const pinia = setupActivePinia(); const geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; registerEndpoint(upload_file_schema.$id, { method: upload_file_schema.methods[FIRST_INDEX], @@ -37,7 +37,7 @@ describe("file uploader", () => { global: { plugins: [vuetify, pinia], }, - props: { multiple: false, accept: "*.txt" }, + props: { multiple: false, accept: "*.txt", auto_upload: false }, }); const v_file_input = wrapper.find('input[type="file"]'); @@ -46,6 +46,7 @@ describe("file uploader", () => { writable: true, }); await v_file_input.trigger("change"); + await flushPromises(); const v_btn = wrapper.findComponent(components.VBtn); await v_btn.trigger("click"); diff --git a/tests/unit/components/Inspector/InspectionButton.nuxt.test.js b/tests/unit/components/Inspector/InspectionButton.nuxt.test.js index b4d2db82..57d5666f 100644 --- a/tests/unit/components/Inspector/InspectionButton.nuxt.test.js +++ b/tests/unit/components/Inspector/InspectionButton.nuxt.test.js @@ -12,7 +12,7 @@ import { useGeodeStore } from "@ogw_front/stores/geode"; describe("inspector inspection button", () => { const pinia = setupActivePinia(); const geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; test("with issues", async () => { const inspection_result = { diff --git a/tests/unit/components/MissingFilesSelector.nuxt.test.js b/tests/unit/components/MissingFilesSelector.nuxt.test.js index 68f80c81..8350e536 100644 --- a/tests/unit/components/MissingFilesSelector.nuxt.test.js +++ b/tests/unit/components/MissingFilesSelector.nuxt.test.js @@ -20,7 +20,7 @@ const upload_file_schema = schemas.opengeodeweb_back.upload_file; describe("missing files selector", () => { const pinia = setupActivePinia(); const geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; test("select file", async () => { geodeStore.request = vi.fn((schema, params, callbacks) => { @@ -57,6 +57,7 @@ describe("missing files selector", () => { writable: true, }); await v_file_input.trigger("change"); + await flushPromises(); const v_btn = file_uploader.findComponent(components.VBtn); registerEndpoint(upload_file_schema.$id, { diff --git a/tests/unit/components/ObjectSelector.nuxt.test.js b/tests/unit/components/ObjectSelector.nuxt.test.js index f1ef682b..1c9ae120 100644 --- a/tests/unit/components/ObjectSelector.nuxt.test.js +++ b/tests/unit/components/ObjectSelector.nuxt.test.js @@ -19,7 +19,7 @@ const { allowed_objects } = schemas.opengeodeweb_back; describe("object selector", () => { const pinia = setupActivePinia(); const geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; test("loadable with one class", async () => { const response = { diff --git a/tests/unit/components/PackagesVersions.nuxt.test.js b/tests/unit/components/PackagesVersions.nuxt.test.js index 362168b2..de2785b2 100644 --- a/tests/unit/components/PackagesVersions.nuxt.test.js +++ b/tests/unit/components/PackagesVersions.nuxt.test.js @@ -11,7 +11,7 @@ describe("packages versions", () => { test("mount", async () => { const pinia = setupActivePinia(); const geodeStore = useGeodeStore(); - geodeStore.base_url = ""; + geodeStore.base_url = "/"; const schema = { $id: "/versions",