From d919fb12db40c7eaee1eee37a0bd7e3a6509143a Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 13:25:37 +0100 Subject: [PATCH 001/113] Add control to filter out archived projects in ProjectList --- lib/www/client/source/src/views/ProjectList.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectList.vue b/lib/www/client/source/src/views/ProjectList.vue index 9fd7a82..8594057 100644 --- a/lib/www/client/source/src/views/ProjectList.vue +++ b/lib/www/client/source/src/views/ProjectList.vue @@ -110,7 +110,6 @@ export default { }, computed: { - displayItems () { return this.showArchived ? this.items From e7c29ba14c7aa4df902d19c0589506939cc61b3c Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 15:12:19 +0100 Subject: [PATCH 002/113] Add file browsing components. Essentially, these are a file selection dialog. --- .../file-browser/file-browser-dialog.vue | 83 ++++++++++ .../components/file-browser/file-browser.vue | 150 ++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 lib/www/client/source/src/components/file-browser/file-browser-dialog.vue create mode 100644 lib/www/client/source/src/components/file-browser/file-browser.vue diff --git a/lib/www/client/source/src/components/file-browser/file-browser-dialog.vue b/lib/www/client/source/src/components/file-browser/file-browser-dialog.vue new file mode 100644 index 0000000..52a03fa --- /dev/null +++ b/lib/www/client/source/src/components/file-browser/file-browser-dialog.vue @@ -0,0 +1,83 @@ + + + diff --git a/lib/www/client/source/src/components/file-browser/file-browser.vue b/lib/www/client/source/src/components/file-browser/file-browser.vue new file mode 100644 index 0000000..92fbc37 --- /dev/null +++ b/lib/www/client/source/src/components/file-browser/file-browser.vue @@ -0,0 +1,150 @@ + + + From 642f5a758578129c56614a5d6cae8db17ac8466a Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 15:13:01 +0100 Subject: [PATCH 003/113] Add project configuration components. The configuration settings are quite complex so we divide the GUI into modular components. --- .../src/components/project-settings/asaqc.vue | 94 +++++ .../components/project-settings/binning.vue | 172 ++++++++++ .../file-matching-parameters.vue | 249 ++++++++++++++ .../components/project-settings/file-path.vue | 85 +++++ .../project-settings/fixed-width-format.vue | 322 ++++++++++++++++++ .../components/project-settings/geodetics.vue | 78 +++++ .../components/project-settings/groups.vue | 158 +++++++++ .../project-settings/input-final-p111.vue | 85 +++++ .../project-settings/input-final-pending.vue | 85 +++++ .../project-settings/input-raw-ntbp.vue | 85 +++++ .../project-settings/input-raw-p111.vue | 85 +++++ .../project-settings/input-smartsource.vue | 85 +++++ .../components/project-settings/name-id.vue | 85 +++++ .../project-settings/not-implemented.vue | 65 ++++ .../online-line-name-format.vue | 80 +++++ .../components/project-settings/planner.vue | 107 ++++++ .../components/project-settings/preplots.vue | 182 ++++++++++ .../project-settings/production.vue | 79 +++++ .../regex-pattern-captures.vue | 117 +++++++ 19 files changed, 2298 insertions(+) create mode 100644 lib/www/client/source/src/components/project-settings/asaqc.vue create mode 100644 lib/www/client/source/src/components/project-settings/binning.vue create mode 100644 lib/www/client/source/src/components/project-settings/file-matching-parameters.vue create mode 100644 lib/www/client/source/src/components/project-settings/file-path.vue create mode 100644 lib/www/client/source/src/components/project-settings/fixed-width-format.vue create mode 100644 lib/www/client/source/src/components/project-settings/geodetics.vue create mode 100644 lib/www/client/source/src/components/project-settings/groups.vue create mode 100644 lib/www/client/source/src/components/project-settings/input-final-p111.vue create mode 100644 lib/www/client/source/src/components/project-settings/input-final-pending.vue create mode 100644 lib/www/client/source/src/components/project-settings/input-raw-ntbp.vue create mode 100644 lib/www/client/source/src/components/project-settings/input-raw-p111.vue create mode 100644 lib/www/client/source/src/components/project-settings/input-smartsource.vue create mode 100644 lib/www/client/source/src/components/project-settings/name-id.vue create mode 100644 lib/www/client/source/src/components/project-settings/not-implemented.vue create mode 100644 lib/www/client/source/src/components/project-settings/online-line-name-format.vue create mode 100644 lib/www/client/source/src/components/project-settings/planner.vue create mode 100644 lib/www/client/source/src/components/project-settings/preplots.vue create mode 100644 lib/www/client/source/src/components/project-settings/production.vue create mode 100644 lib/www/client/source/src/components/project-settings/regex-pattern-captures.vue diff --git a/lib/www/client/source/src/components/project-settings/asaqc.vue b/lib/www/client/source/src/components/project-settings/asaqc.vue new file mode 100644 index 0000000..d5adb57 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/asaqc.vue @@ -0,0 +1,94 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/binning.vue b/lib/www/client/source/src/components/project-settings/binning.vue new file mode 100644 index 0000000..b2aaf28 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/binning.vue @@ -0,0 +1,172 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/file-matching-parameters.vue b/lib/www/client/source/src/components/project-settings/file-matching-parameters.vue new file mode 100644 index 0000000..84bee15 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/file-matching-parameters.vue @@ -0,0 +1,249 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/file-path.vue b/lib/www/client/source/src/components/project-settings/file-path.vue new file mode 100644 index 0000000..06b8501 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/file-path.vue @@ -0,0 +1,85 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/fixed-width-format.vue b/lib/www/client/source/src/components/project-settings/fixed-width-format.vue new file mode 100644 index 0000000..0a4fdb8 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/fixed-width-format.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/geodetics.vue b/lib/www/client/source/src/components/project-settings/geodetics.vue new file mode 100644 index 0000000..c31ead4 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/geodetics.vue @@ -0,0 +1,78 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/groups.vue b/lib/www/client/source/src/components/project-settings/groups.vue new file mode 100644 index 0000000..fa3fb5a --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/groups.vue @@ -0,0 +1,158 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/input-final-p111.vue b/lib/www/client/source/src/components/project-settings/input-final-p111.vue new file mode 100644 index 0000000..d54623a --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/input-final-p111.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/input-final-pending.vue b/lib/www/client/source/src/components/project-settings/input-final-pending.vue new file mode 100644 index 0000000..d23bc44 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/input-final-pending.vue @@ -0,0 +1,85 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/input-raw-ntbp.vue b/lib/www/client/source/src/components/project-settings/input-raw-ntbp.vue new file mode 100644 index 0000000..a46c268 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/input-raw-ntbp.vue @@ -0,0 +1,85 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/input-raw-p111.vue b/lib/www/client/source/src/components/project-settings/input-raw-p111.vue new file mode 100644 index 0000000..ef68701 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/input-raw-p111.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/input-smartsource.vue b/lib/www/client/source/src/components/project-settings/input-smartsource.vue new file mode 100644 index 0000000..e1ead07 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/input-smartsource.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/name-id.vue b/lib/www/client/source/src/components/project-settings/name-id.vue new file mode 100644 index 0000000..62c91f1 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/name-id.vue @@ -0,0 +1,85 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/not-implemented.vue b/lib/www/client/source/src/components/project-settings/not-implemented.vue new file mode 100644 index 0000000..9488eb7 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/not-implemented.vue @@ -0,0 +1,65 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/online-line-name-format.vue b/lib/www/client/source/src/components/project-settings/online-line-name-format.vue new file mode 100644 index 0000000..7049c7a --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/online-line-name-format.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/planner.vue b/lib/www/client/source/src/components/project-settings/planner.vue new file mode 100644 index 0000000..29c1a03 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/planner.vue @@ -0,0 +1,107 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/preplots.vue b/lib/www/client/source/src/components/project-settings/preplots.vue new file mode 100644 index 0000000..a8ad9a1 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/preplots.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/production.vue b/lib/www/client/source/src/components/project-settings/production.vue new file mode 100644 index 0000000..39b3c69 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/production.vue @@ -0,0 +1,79 @@ + + + diff --git a/lib/www/client/source/src/components/project-settings/regex-pattern-captures.vue b/lib/www/client/source/src/components/project-settings/regex-pattern-captures.vue new file mode 100644 index 0000000..944f6ef --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/regex-pattern-captures.vue @@ -0,0 +1,117 @@ + + + + + From 4595dddc2453a58f0c72fb9c7cbb81e6a165673c Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 15:14:15 +0100 Subject: [PATCH 004/113] Add ProjectSettings view --- .../source/src/views/ProjectSettings.vue | 607 ++++++++++++++++++ 1 file changed, 607 insertions(+) create mode 100644 lib/www/client/source/src/views/ProjectSettings.vue diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue new file mode 100644 index 0000000..c18b01a --- /dev/null +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -0,0 +1,607 @@ + + + + From 3d42ce6fbc10c85d2ae00216545402689cf91673 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 15:25:45 +0100 Subject: [PATCH 005/113] =?UTF-8?q?Add=20context=20menu=20with=20=E2=80=98?= =?UTF-8?q?Edit=20project=20settings=E2=80=99=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/source/src/views/ProjectList.vue | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/www/client/source/src/views/ProjectList.vue b/lib/www/client/source/src/views/ProjectList.vue index 8594057..009b5c2 100644 --- a/lib/www/client/source/src/views/ProjectList.vue +++ b/lib/www/client/source/src/views/ProjectList.vue @@ -6,6 +6,7 @@ :items="displayItems" :options.sync="options" :loading="loading" + @contextmenu:row="contextMenu" > @@ -106,6 +122,11 @@ export default { showArchived: true, + // Context menu stuff + contextMenuShow: false, + contextMenuX: 0, + contextMenuY: 0, + contextMenuItem: null } }, @@ -156,6 +177,15 @@ export default { } }, + contextMenu (e, {item}) { + e.preventDefault(); + this.contextMenuShow = false; + this.contextMenuX = e.clientX; + this.contextMenuY = e.clientY; + this.contextMenuItem = item; + this.$nextTick( () => this.contextMenuShow = true ); + }, + ...mapActions(["api"]) }, From f157f49312ed5bf46fd420d698aaafe02c379842 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 15:27:40 +0100 Subject: [PATCH 006/113] Use project list from Vuex --- lib/www/client/source/src/views/ProjectList.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectList.vue b/lib/www/client/source/src/views/ProjectList.vue index 009b5c2..97d9070 100644 --- a/lib/www/client/source/src/views/ProjectList.vue +++ b/lib/www/client/source/src/views/ProjectList.vue @@ -151,8 +151,9 @@ export default { }, methods: { + async list () { - this.items = await this.api(["/project"]) || []; + this.items = [...this.projects]; }, async summary (item) { From 1b85b5cd4bad955d6eff2fb7d2d8ca056dc9bb14 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 19:15:02 +0100 Subject: [PATCH 007/113] Remove cloning control stub. Cloning takes place from the project list, we don't really need to duplicate that functionality here for the time being. --- .../source/src/views/ProjectSettings.vue | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index c18b01a..7a547f9 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -21,37 +21,6 @@ - - - - - - Create a survey based on this one - -

Only a subset of fields will be changed.

- - - - - - -
-
-
-
-
Date: Sun, 29 Oct 2023 19:25:26 +0100 Subject: [PATCH 008/113] Add project settings cloning component. Asks for the new ID, name and root file path. --- .../project-settings/name-id-rootpath.vue | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 lib/www/client/source/src/components/project-settings/name-id-rootpath.vue diff --git a/lib/www/client/source/src/components/project-settings/name-id-rootpath.vue b/lib/www/client/source/src/components/project-settings/name-id-rootpath.vue new file mode 100644 index 0000000..43ead5f --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/name-id-rootpath.vue @@ -0,0 +1,101 @@ + + + From 15242de2d95a58198311f587a789605138ab08ec Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 19:29:35 +0100 Subject: [PATCH 009/113] Add configuration settings tab to project navigation bar. Only for admin users. --- .../src/components/app-bar-extension-project.vue | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/components/app-bar-extension-project.vue b/lib/www/client/source/src/components/app-bar-extension-project.vue index 5ac8907..5bf35c7 100644 --- a/lib/www/client/source/src/components/app-bar-extension-project.vue +++ b/lib/www/client/source/src/components/app-bar-extension-project.vue @@ -1,6 +1,15 @@ @@ -35,6 +44,7 @@ export default { return this.tabs.findIndex(t => t.href == this.page); }, + ...mapGetters(["adminaccess"]) }, methods: { From 2131cdf0c16f51ec019d6d5655d7e175656fba82 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 19:32:47 +0100 Subject: [PATCH 010/113] Add project cloning option to ProjectList --- .../client/source/src/views/ProjectList.vue | 162 +++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectList.vue b/lib/www/client/source/src/views/ProjectList.vue index 97d9070..52944a8 100644 --- a/lib/www/client/source/src/views/ProjectList.vue +++ b/lib/www/client/source/src/views/ProjectList.vue @@ -67,9 +67,26 @@ mdi-file-document-edit-outline Edit project settings + + + mdi-sheep + Clone project + + + + + + @@ -81,10 +98,15 @@ td p:last-of-type { From 402a3f9cce3801d598e5b0104db226f6035884da Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sun, 29 Oct 2023 19:40:07 +0100 Subject: [PATCH 013/113] =?UTF-8?q?Add=20code=20for=20a=20=E2=80=98new=20p?= =?UTF-8?q?roject=E2=80=99=20button=20to=20project=20list=20navigation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is currently disabled though (value in route/index.js is commented out) as it is not possible at the moment to create new projects fully from scratch from the frontend. See comment on previous commit. NB: projects may be created fully from scratch by making an API request with a suitable YAML / JSON configuration file, thusly: curl -vs "https://[hostname]/api/project" -X POST \ -H "Content-Type: application/yaml" --data-binary @/path/to/configuration.yaml --- .../app-bar-extension-project-list.vue | 85 +++++++++++++++++++ lib/www/client/source/src/router/index.js | 12 ++- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 lib/www/client/source/src/components/app-bar-extension-project-list.vue diff --git a/lib/www/client/source/src/components/app-bar-extension-project-list.vue b/lib/www/client/source/src/components/app-bar-extension-project-list.vue new file mode 100644 index 0000000..fb2b252 --- /dev/null +++ b/lib/www/client/source/src/components/app-bar-extension-project-list.vue @@ -0,0 +1,85 @@ + + + diff --git a/lib/www/client/source/src/router/index.js b/lib/www/client/source/src/router/index.js index 199eae7..4cc2ddb 100644 --- a/lib/www/client/source/src/router/index.js +++ b/lib/www/client/source/src/router/index.js @@ -16,7 +16,9 @@ import Log from '../views/Log.vue' import QC from '../views/QC.vue' import Graphs from '../views/Graphs.vue' import Map from '../views/Map.vue' +import ProjectSettings from '../views/ProjectSettings.vue' import DougalAppBarExtensionProject from '../components/app-bar-extension-project' +import DougalAppBarExtensionProjectList from '../components/app-bar-extension-project-list' Vue.use(VueRouter) @@ -80,7 +82,10 @@ Vue.use(VueRouter) meta: { breadcrumbs: [ { text: "Projects", href: "/projects", disabled: true } - ] + ], + appBarExtension: { + // component: DougalAppBarExtensionProjectList + } } }, { @@ -168,6 +173,11 @@ Vue.use(VueRouter) path: "map", name: "map", component: Map + }, + { + path: "configuration", + name: "configuration", + component: ProjectSettings } ] } From a55d2cc6fc730885dcb31b3e63dfba9360aab5ba Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Thu, 2 Nov 2023 20:15:51 +0100 Subject: [PATCH 014/113] Update database templates to v0.4.5 --- etc/db/database-version.sql | 4 +- etc/db/schema-template.sql | 115 +++++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/etc/db/database-version.sql b/etc/db/database-version.sql index d4530ce..15ae342 100644 --- a/etc/db/database-version.sql +++ b/etc/db/database-version.sql @@ -1,5 +1,5 @@ \connect dougal -INSERT INTO public.info VALUES ('version', '{"db_schema": "0.4.2"}') +INSERT INTO public.info VALUES ('version', '{"db_schema": "0.4.5"}') ON CONFLICT (key) DO UPDATE - SET value = public.info.value || '{"db_schema": "0.4.2"}' WHERE public.info.key = 'version'; + SET value = public.info.value || '{"db_schema": "0.4.5"}' WHERE public.info.key = 'version'; diff --git a/etc/db/schema-template.sql b/etc/db/schema-template.sql index 0f6ed68..02726e7 100644 --- a/etc/db/schema-template.sql +++ b/etc/db/schema-template.sql @@ -399,6 +399,62 @@ $$; ALTER FUNCTION _SURVEY__TEMPLATE_.clear_shot_qc() OWNER TO postgres; +-- +-- Name: event_log_uid_seq; Type: SEQUENCE; Schema: _SURVEY__TEMPLATE_; Owner: postgres +-- + +CREATE SEQUENCE _SURVEY__TEMPLATE_.event_log_uid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE _SURVEY__TEMPLATE_.event_log_uid_seq OWNER TO postgres; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: event_log_full; Type: TABLE; Schema: _SURVEY__TEMPLATE_; Owner: postgres +-- + +CREATE TABLE _SURVEY__TEMPLATE_.event_log_full ( + uid integer DEFAULT nextval('_SURVEY__TEMPLATE_.event_log_uid_seq'::regclass) NOT NULL, + id integer NOT NULL, + tstamp timestamp with time zone, + sequence integer, + point integer, + remarks text DEFAULT ''::text NOT NULL, + labels text[] DEFAULT ARRAY[]::text[] NOT NULL, + meta jsonb DEFAULT '{}'::jsonb NOT NULL, + validity tstzrange NOT NULL, + CONSTRAINT event_log_full_check CHECK ((((tstamp IS NOT NULL) AND (sequence IS NOT NULL) AND (point IS NOT NULL)) OR ((tstamp IS NOT NULL) AND (sequence IS NULL) AND (point IS NULL)) OR ((tstamp IS NULL) AND (sequence IS NOT NULL) AND (point IS NOT NULL)))), + CONSTRAINT event_log_full_validity_check CHECK ((NOT isempty(validity))) +); + + +ALTER TABLE _SURVEY__TEMPLATE_.event_log_full OWNER TO postgres; + +-- +-- Name: event_log_changes(timestamp with time zone); Type: FUNCTION; Schema: _SURVEY__TEMPLATE_; Owner: postgres +-- + +CREATE FUNCTION _SURVEY__TEMPLATE_.event_log_changes(ts0 timestamp with time zone) RETURNS SETOF _SURVEY__TEMPLATE_.event_log_full + LANGUAGE sql + AS $$ + SELECT * + FROM event_log_full + WHERE lower(validity) > ts0 OR upper(validity) IS NOT NULL AND upper(validity) > ts0 + ORDER BY lower(validity); + $$; + + +ALTER FUNCTION _SURVEY__TEMPLATE_.event_log_changes(ts0 timestamp with time zone) OWNER TO postgres; + -- -- Name: event_log_full_insert(); Type: FUNCTION; Schema: _SURVEY__TEMPLATE_; Owner: postgres -- @@ -881,46 +937,6 @@ $$; ALTER FUNCTION _SURVEY__TEMPLATE_.ij_error(line double precision, point double precision, geom public.geometry) OWNER TO postgres; --- --- Name: event_log_uid_seq; Type: SEQUENCE; Schema: _SURVEY__TEMPLATE_; Owner: postgres --- - -CREATE SEQUENCE _SURVEY__TEMPLATE_.event_log_uid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE _SURVEY__TEMPLATE_.event_log_uid_seq OWNER TO postgres; - -SET default_tablespace = ''; - -SET default_table_access_method = heap; - --- --- Name: event_log_full; Type: TABLE; Schema: _SURVEY__TEMPLATE_; Owner: postgres --- - -CREATE TABLE _SURVEY__TEMPLATE_.event_log_full ( - uid integer DEFAULT nextval('_SURVEY__TEMPLATE_.event_log_uid_seq'::regclass) NOT NULL, - id integer NOT NULL, - tstamp timestamp with time zone, - sequence integer, - point integer, - remarks text DEFAULT ''::text NOT NULL, - labels text[] DEFAULT ARRAY[]::text[] NOT NULL, - meta jsonb DEFAULT '{}'::jsonb NOT NULL, - validity tstzrange NOT NULL, - CONSTRAINT event_log_full_check CHECK ((((tstamp IS NOT NULL) AND (sequence IS NOT NULL) AND (point IS NOT NULL)) OR ((tstamp IS NOT NULL) AND (sequence IS NULL) AND (point IS NULL)) OR ((tstamp IS NULL) AND (sequence IS NOT NULL) AND (point IS NOT NULL)))), - CONSTRAINT event_log_full_validity_check CHECK ((NOT isempty(validity))) -); - - -ALTER TABLE _SURVEY__TEMPLATE_.event_log_full OWNER TO postgres; - -- -- Name: event_log; Type: VIEW; Schema: _SURVEY__TEMPLATE_; Owner: postgres -- @@ -1519,9 +1535,9 @@ CREATE VIEW _SURVEY__TEMPLATE_.final_lines_summary AS s.ts1, (s.ts1 - s.ts0) AS duration, s.num_points, - ( SELECT count(*) AS count - FROM _SURVEY__TEMPLATE_.missing_sequence_final_points - WHERE missing_sequence_final_points.sequence = s.sequence) AS missing_shots, + (( SELECT count(*) AS count + FROM _SURVEY__TEMPLATE_.preplot_points + WHERE ((preplot_points.line = fl.line) AND (((preplot_points.point >= s.fsp) AND (preplot_points.point <= s.lsp)) OR ((preplot_points.point >= s.lsp) AND (preplot_points.point <= s.fsp))))) - s.num_points) AS missing_shots, s.length, s.azimuth, fl.remarks, @@ -2077,10 +2093,10 @@ CREATE VIEW _SURVEY__TEMPLATE_.preplot_summary AS ALTER TABLE _SURVEY__TEMPLATE_.preplot_summary OWNER TO postgres; -- --- Name: project_summary; Type: VIEW; Schema: _SURVEY__TEMPLATE_; Owner: postgres +-- Name: project_summary; Type: MATERIALIZED VIEW; Schema: _SURVEY__TEMPLATE_; Owner: postgres -- -CREATE VIEW _SURVEY__TEMPLATE_.project_summary AS +CREATE MATERIALIZED VIEW _SURVEY__TEMPLATE_.project_summary AS WITH fls AS ( SELECT avg((final_lines_summary.duration / ((final_lines_summary.num_points - 1))::double precision)) AS shooting_rate, avg((final_lines_summary.length / date_part('epoch'::text, final_lines_summary.duration))) AS speed, @@ -2123,7 +2139,8 @@ CREATE VIEW _SURVEY__TEMPLATE_.project_summary AS fls.speed AS shooting_rate FROM _SURVEY__TEMPLATE_.preplot_summary ps, fls, - project; + project + WITH NO DATA; ALTER TABLE _SURVEY__TEMPLATE_.project_summary OWNER TO postgres; @@ -2168,9 +2185,9 @@ CREATE VIEW _SURVEY__TEMPLATE_.raw_lines_summary AS (s.ts1 - s.ts0) AS duration, s.num_points, s.num_preplots, - (SELECT count(*) AS count - FROM _SURVEY__TEMPLATE_.missing_sequence_raw_points - WHERE missing_sequence_raw_points.sequence = s.sequence) AS missing_shots, + (( SELECT count(*) AS count + FROM _SURVEY__TEMPLATE_.preplot_points + WHERE ((preplot_points.line = rl.line) AND (((preplot_points.point >= s.fsp) AND (preplot_points.point <= s.lsp)) OR ((preplot_points.point >= s.lsp) AND (preplot_points.point <= s.fsp))))) - s.num_preplots) AS missing_shots, s.length, s.azimuth, rl.remarks, From 4486fc4afc02b1758890d07daae6148cf32dd296 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Thu, 2 Nov 2023 20:44:07 +0100 Subject: [PATCH 015/113] Improve contrast of new group item --- lib/www/client/source/src/components/project-settings/groups.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/www/client/source/src/components/project-settings/groups.vue b/lib/www/client/source/src/components/project-settings/groups.vue index fa3fb5a..31bcbaa 100644 --- a/lib/www/client/source/src/components/project-settings/groups.vue +++ b/lib/www/client/source/src/components/project-settings/groups.vue @@ -20,6 +20,7 @@ New group: From 544c4ead76897314cfb50f940fcadc0b30ce5fae Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Thu, 2 Nov 2023 20:46:26 +0100 Subject: [PATCH 016/113] Remove trailing slash from URL --- lib/www/client/source/src/views/ProjectSettings.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index 7a547f9..0327fc1 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -503,7 +503,7 @@ export default { async getConfiguration () { this.configuration = null; - const url = `/project/${this.$route.params.project}/configuration/`; + const url = `/project/${this.$route.params.project}/configuration`; this.configuration = await this.api([url]); }, @@ -540,7 +540,7 @@ export default { }, async patch (data) { - const url = `/project/${this.$route.params.project}/configuration/`; + const url = `/project/${this.$route.params.project}/configuration`; const init = { method: "PATCH", body: data From beeba966dd55e006d01c18e1a14d03a39fbd82f0 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Thu, 2 Nov 2023 22:55:28 +0100 Subject: [PATCH 017/113] Cope with empty result --- .../client/source/src/components/file-browser/file-browser.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/www/client/source/src/components/file-browser/file-browser.vue b/lib/www/client/source/src/components/file-browser/file-browser.vue index 92fbc37..2e578ee 100644 --- a/lib/www/client/source/src/components/file-browser/file-browser.vue +++ b/lib/www/client/source/src/components/file-browser/file-browser.vue @@ -115,7 +115,7 @@ export default { const url = `/files/${item? item.path : (this.root || this.path || "")}`; const list = await this.api([url]); this.loading = false; - const items = list.map(item => { + const items = list?.map(item => { if (item["Content-Type"] == "inode/directory") { item.children = []; } From cd739e603f46839f3f671f74b483cba633f9c96a Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Fri, 3 Nov 2023 16:31:02 +0100 Subject: [PATCH 018/113] Fix configuration object data corruption --- lib/www/client/source/src/views/ProjectSettings.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index 0327fc1..122837c 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -266,7 +266,7 @@ export default { rootPath: obj.rootPath }), save: async (data, cfg) => { - await this.patch({preplots: {...data.preplots}}) + await this.patch({preplots: [...data.preplots]}) } }, { From cae57e2a64afbd86402ec3f3c6161c40bc78b5a2 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Fri, 3 Nov 2023 16:31:58 +0100 Subject: [PATCH 019/113] Ensure we get a fresh response --- lib/www/client/source/src/views/ProjectSettings.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index 122837c..72c1ba2 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -504,7 +504,12 @@ export default { async getConfiguration () { this.configuration = null; const url = `/project/${this.$route.params.project}/configuration`; - this.configuration = await this.api([url]); + const init = { + headers: { + "If-None-Match": "" // Ensure we get a fresh response + } + }; + this.configuration = await this.api([url, init]); }, makeTree (obj, id=0) { From b74419f770372982b87b4e4048cea14eabff1187 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 18:02:11 +0100 Subject: [PATCH 020/113] Reuse deepMerge.js from the backend libs --- lib/www/client/source/src/lib/deepMerge.js | 1 + 1 file changed, 1 insertion(+) create mode 120000 lib/www/client/source/src/lib/deepMerge.js diff --git a/lib/www/client/source/src/lib/deepMerge.js b/lib/www/client/source/src/lib/deepMerge.js new file mode 120000 index 0000000..4df8a54 --- /dev/null +++ b/lib/www/client/source/src/lib/deepMerge.js @@ -0,0 +1 @@ +/home/dberge/Development/projects/dougal/software/lib/www/server/lib/utils/deepMerge.js \ No newline at end of file From 6d417a92720115ae3e9e29fe0506b89d99b08f09 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 18:03:18 +0100 Subject: [PATCH 021/113] Add utility functions. The functions are: - deepMerge() Merge two objects - deepCompare() Loose deep comparison - deepEqual() Strict deep comparison - deepSet() Set nested object property value - deepValue() Retrive nested object property value --- lib/www/client/source/src/lib/utils.js | 90 +++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/lib/utils.js b/lib/www/client/source/src/lib/utils.js index 5ca7aa1..6a3ade8 100644 --- a/lib/www/client/source/src/lib/utils.js +++ b/lib/www/client/source/src/lib/utils.js @@ -133,9 +133,97 @@ function preferencesλ (preferences) { } +/** Compare two possibly complex values for + * loose equality, going as deep as required in the + * case of complex objects. + */ +function deepCompare (a, b) { + if (typeof a == "object" && typeof b == "object") { + return !Object.entries(a).some( ([k, v]) => !deepCompare(v, b[k])) && + !Object.entries(b).some( ([k, v]) => !deepCompare(v, a[k])); + } else { + return a == b; + } +} + +/** Compare two possibly complex values for + * strict equality. + */ +function deepEqual (a, b) { + if (typeof a === "object" && typeof b === "object") { + return !Object.entries(a).some( ([k, v]) => !deepEqual(v, b[k])) && + !Object.entries(b).some( ([k, v]) => !deepEqual(v, a[k])); + } else { + return a === b; + } +} + +/** Traverses an object and sets a nested value. + * + * Example: + * + * const obj = {a: {b: {c: "X"} } } + * deepSet(obj, ["a", "b", "c"], "d") + * → {a: {b: {c: "d"} } } + * + * This would be the equivalent of: + * + * obj?.a?.b?.c = "d"; + * + * Except that the above is not a legal expression. + * + * If a non-leaf property does not exist, this function + * creates it as an empty object ({}) and keeps traversing. + * + */ +function deepSet (obj, path, value) { + const key = path.shift(); + if (!path.length) { + obj[key] = value; + } else { + if (!Object.hasOwn(obj, key)) { + obj[key] = {}; + } + deepSet(obj[key], path, value); + } +} + +/** Returns a nested property. + * + * Example: + * + * const obj = {a: {b: {c: "d"} } } + * deepSet(obj, ["a", "b", "c"]) + * → "d" + * + * If `path` is known in advance, this is effectively + * the same as: + * + * obj?.a?.b?.c + * + * This might be useful when `path` is dynamic. + */ +function deepValue (obj, path) { + if (obj !== undefined) { + const key = path.shift(); + if (!path.length) { + return obj[key]; + } else { + return deepValue(obj[key], path); + } + } +} + +// Just to have all the deep*()s in one place +import deepMerge from './deepMerge' export { withParentProps, geometryAsString, - preferencesλ + preferencesλ, + deepMerge, + deepCompare, + deepEqual, + deepSet, + deepValue } From b3dbc0f4179ff097696f2619d8d14d0234f170d2 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 18:05:50 +0100 Subject: [PATCH 022/113] Add utility function to create HSL colours --- lib/www/client/source/src/lib/hsl.js | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lib/www/client/source/src/lib/hsl.js diff --git a/lib/www/client/source/src/lib/hsl.js b/lib/www/client/source/src/lib/hsl.js new file mode 100644 index 0000000..a3e342f --- /dev/null +++ b/lib/www/client/source/src/lib/hsl.js @@ -0,0 +1,47 @@ +/** Return an HSL colour as a function of an input value + * `str`. + * + * Consider using as getHSL.bind(this) in Vue components + * in order to get access to the Vuetify theme configuration. + */ +function getHSL (str, saturation = 1, lightness = 0.25, offset = 0) { + + function getHash (v) { + if (typeof (v??false)[Symbol.iterator] != "function") { + // Not an iterable, make it one + v = String(v); + } + + return Math.abs([...v, ..." "].reduce( (acc, cur) => String(cur).charCodeAt(0) + ((acc << 5) - acc), 0 )); + } + + const h = (getHash(str) + offset) % 360; + const s = saturation * 100; + const l = this?.$vuetify?.theme?.isDark + ? (1-lightness) * 100 + : lightness * 100; + + return {h, s, l}; + +} + +/** Return a CSS hsl() or hsla() colour + * representation as a function of an input value. + * + * Consider using as getHSLColourFor.bind(this) – See + * note for getHSL() above. + */ +function getHSLColourFor (str, opacity = 1, saturation, lightness, offset) { + const _getHSL = getHSL.bind(this); + const {h, s, l} = _getHSL(str, saturation, lightness, offset); + if (opacity == 1) { + return `hsl(${h},${s}%,${l}%)`; + } else { + return `hsla(${h},${s}%,${l}%, ${opacity})`; + } +} + +export { + getHSL, + getHSLColourFor +} From 436a9b8289a17ee47440399715f49113ba3b90e3 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 18:06:20 +0100 Subject: [PATCH 023/113] Add utility function to truncate long strings --- lib/www/client/source/src/lib/truncate-text.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lib/www/client/source/src/lib/truncate-text.js diff --git a/lib/www/client/source/src/lib/truncate-text.js b/lib/www/client/source/src/lib/truncate-text.js new file mode 100644 index 0000000..554190b --- /dev/null +++ b/lib/www/client/source/src/lib/truncate-text.js @@ -0,0 +1,10 @@ + +function truncateText (text, length=20) { + if (text?.length <= length) { + return text; + } else { + return text.slice(0, length/2)+"…"+text.slice(-(length/2)); + } +} + +export default truncateText; From 12b28cbb8df82d6c9cdc60b8114e61653de2f271 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 18:08:54 +0100 Subject: [PATCH 024/113] Add csv-parse dependency to frontend. Also requires a Buffer polyfill. --- lib/www/client/source/package-lock.json | 74 +++++++++++++++++++------ lib/www/client/source/package.json | 2 + 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/lib/www/client/source/package-lock.json b/lib/www/client/source/package-lock.json index 8f0f191..7189b50 100644 --- a/lib/www/client/source/package-lock.json +++ b/lib/www/client/source/package-lock.json @@ -10,7 +10,9 @@ "license": "UNLICENSED", "dependencies": { "@mdi/font": "^7.2.96", + "buffer": "^6.0.3", "core-js": "^3.6.5", + "csv-parse": "^5.5.2", "d3": "^7.0.1", "jwt-decode": "^3.0.0", "leaflet": "^1.7.1", @@ -3457,7 +3459,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -3508,6 +3509,30 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3647,10 +3672,9 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -3667,7 +3691,7 @@ ], "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-from": { @@ -4632,6 +4656,11 @@ "node": ">=8.0.0" } }, + "node_modules/csv-parse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.2.tgz", + "integrity": "sha512-YRVtvdtUNXZCMyK5zd5Wty1W6dNTpGKdqQd4EQ8tl/c6KW1aMBB1Kg1ppky5FONKmEqGJ/8WjLlTNLPne4ioVA==" + }, "node_modules/d3": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz", @@ -6448,7 +6477,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -13920,8 +13948,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch": { "version": "0.6.1", @@ -13950,6 +13977,18 @@ "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } } }, "bluebird": { @@ -14060,13 +14099,12 @@ } }, "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "requires": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "buffer-from": { @@ -14765,6 +14803,11 @@ "css-tree": "^1.1.2" } }, + "csv-parse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.2.tgz", + "integrity": "sha512-YRVtvdtUNXZCMyK5zd5Wty1W6dNTpGKdqQd4EQ8tl/c6KW1aMBB1Kg1ppky5FONKmEqGJ/8WjLlTNLPne4ioVA==" + }, "d3": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz", @@ -16108,8 +16151,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "5.2.4", diff --git a/lib/www/client/source/package.json b/lib/www/client/source/package.json index 004831a..694232e 100644 --- a/lib/www/client/source/package.json +++ b/lib/www/client/source/package.json @@ -8,7 +8,9 @@ }, "dependencies": { "@mdi/font": "^7.2.96", + "buffer": "^6.0.3", "core-js": "^3.6.5", + "csv-parse": "^5.5.2", "d3": "^7.0.1", "jwt-decode": "^3.0.0", "leaflet": "^1.7.1", From 2fa9d99eebf96a8e50714f89a810217dc5d582f4 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 18:09:56 +0100 Subject: [PATCH 025/113] Add YAML frontend dependency. To download / upload configurations. --- lib/www/client/source/package-lock.json | 53 ++++++++++++++++++++----- lib/www/client/source/package.json | 3 +- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/www/client/source/package-lock.json b/lib/www/client/source/package-lock.json index 7189b50..779629e 100644 --- a/lib/www/client/source/package-lock.json +++ b/lib/www/client/source/package-lock.json @@ -28,7 +28,8 @@ "vue-debounce": "^2.6.0", "vue-router": "^3.5.1", "vuetify": "^2.5.0", - "vuex": "^3.6.2" + "vuex": "^3.6.2", + "yaml": "^2.3.4" }, "devDependencies": { "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", @@ -4307,6 +4308,15 @@ "node": ">=10" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -4644,6 +4654,15 @@ "postcss": "^8.2.15" } }, + "node_modules/cssnano/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/csso": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", @@ -11369,12 +11388,11 @@ "dev": true }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/yargs": { @@ -14556,6 +14574,14 @@ "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } } }, "cross-spawn": { @@ -14748,6 +14774,14 @@ "cssnano-preset-default": "^5.2.14", "lilconfig": "^2.0.3", "yaml": "^1.10.2" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } } }, "cssnano-preset-default": { @@ -19742,10 +19776,9 @@ "dev": true }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==" }, "yargs": { "version": "16.2.0", diff --git a/lib/www/client/source/package.json b/lib/www/client/source/package.json index 694232e..36aa669 100644 --- a/lib/www/client/source/package.json +++ b/lib/www/client/source/package.json @@ -26,7 +26,8 @@ "vue-debounce": "^2.6.0", "vue-router": "^3.5.1", "vuetify": "^2.5.0", - "vuex": "^3.6.2" + "vuex": "^3.6.2", + "yaml": "^2.3.4" }, "devDependencies": { "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", From 873d7cfea718c6af87d9cd5b66dd5d32f013e5f7 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 19:13:03 +0100 Subject: [PATCH 026/113] Add utility Vue components. This commit adds and , which can be used to configure certain properties of an object. Intended for use while editing project configurations. --- .../fields/field-content-dialog.vue | 109 ++++++++ .../src/components/fields/field-content.vue | 242 ++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 lib/www/client/source/src/components/fields/field-content-dialog.vue create mode 100644 lib/www/client/source/src/components/fields/field-content.vue diff --git a/lib/www/client/source/src/components/fields/field-content-dialog.vue b/lib/www/client/source/src/components/fields/field-content-dialog.vue new file mode 100644 index 0000000..70e94e0 --- /dev/null +++ b/lib/www/client/source/src/components/fields/field-content-dialog.vue @@ -0,0 +1,109 @@ + + + diff --git a/lib/www/client/source/src/components/fields/field-content.vue b/lib/www/client/source/src/components/fields/field-content.vue new file mode 100644 index 0000000..682c9d7 --- /dev/null +++ b/lib/www/client/source/src/components/fields/field-content.vue @@ -0,0 +1,242 @@ + + + From 9f1fc3d19cf02925e863dfe6e651af50058642be Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 19:16:35 +0100 Subject: [PATCH 027/113] Make Vue component reusable. This converts into a more reusable component. --- .../decoder/fixed-string-decoder-field.vue | 140 +++++++ .../decoder/fixed-string-decoder.vue | 370 ++++++++++++++++++ .../project-settings/fixed-width-format.vue | 322 --------------- 3 files changed, 510 insertions(+), 322 deletions(-) create mode 100644 lib/www/client/source/src/components/decoder/fixed-string-decoder-field.vue create mode 100644 lib/www/client/source/src/components/decoder/fixed-string-decoder.vue delete mode 100644 lib/www/client/source/src/components/project-settings/fixed-width-format.vue diff --git a/lib/www/client/source/src/components/decoder/fixed-string-decoder-field.vue b/lib/www/client/source/src/components/decoder/fixed-string-decoder-field.vue new file mode 100644 index 0000000..b9b3f84 --- /dev/null +++ b/lib/www/client/source/src/components/decoder/fixed-string-decoder-field.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue b/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue new file mode 100644 index 0000000..09d2c98 --- /dev/null +++ b/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue @@ -0,0 +1,370 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/fixed-width-format.vue b/lib/www/client/source/src/components/project-settings/fixed-width-format.vue deleted file mode 100644 index 0a4fdb8..0000000 --- a/lib/www/client/source/src/components/project-settings/fixed-width-format.vue +++ /dev/null @@ -1,322 +0,0 @@ - - - - - From f82f2c78c727d1d68f61f75ca081fa8d748b14f3 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 19:18:53 +0100 Subject: [PATCH 028/113] Add Vue component for handling delimited strings. is intended for providing a UI for configuring text-delimited import settings (such as CSV imports). --- .../delimited-string-decoder-field.vue | 82 ++++ .../decoder/delimited-string-decoder.vue | 366 ++++++++++++++++++ 2 files changed, 448 insertions(+) create mode 100644 lib/www/client/source/src/components/decoder/delimited-string-decoder-field.vue create mode 100644 lib/www/client/source/src/components/decoder/delimited-string-decoder.vue diff --git a/lib/www/client/source/src/components/decoder/delimited-string-decoder-field.vue b/lib/www/client/source/src/components/decoder/delimited-string-decoder-field.vue new file mode 100644 index 0000000..7f714f0 --- /dev/null +++ b/lib/www/client/source/src/components/decoder/delimited-string-decoder-field.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/lib/www/client/source/src/components/decoder/delimited-string-decoder.vue b/lib/www/client/source/src/components/decoder/delimited-string-decoder.vue new file mode 100644 index 0000000..960afa8 --- /dev/null +++ b/lib/www/client/source/src/components/decoder/delimited-string-decoder.vue @@ -0,0 +1,366 @@ + + + + + From 0137bd84d5d3361ee1daf6187b5264b576de1305 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 19:22:19 +0100 Subject: [PATCH 029/113] Add Vue component for configuring sailline CSV imports. Sailline CSV imports are related to issue #264. Not yet implemented server-side. --- .../decoder/saillines-string-decoder.vue | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 lib/www/client/source/src/components/decoder/saillines-string-decoder.vue diff --git a/lib/www/client/source/src/components/decoder/saillines-string-decoder.vue b/lib/www/client/source/src/components/decoder/saillines-string-decoder.vue new file mode 100644 index 0000000..49e1e85 --- /dev/null +++ b/lib/www/client/source/src/components/decoder/saillines-string-decoder.vue @@ -0,0 +1,295 @@ + + + + + From 09fb65381227959ebef1053cea5b6c4584983ad0 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 8 Nov 2023 20:59:58 +0100 Subject: [PATCH 030/113] Strip whitespace --- lib/www/client/source/src/lib/utils.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/www/client/source/src/lib/utils.js b/lib/www/client/source/src/lib/utils.js index 6a3ade8..3b55302 100644 --- a/lib/www/client/source/src/lib/utils.js +++ b/lib/www/client/source/src/lib/utils.js @@ -93,25 +93,25 @@ function geometryAsString (item, opts = {}) { } /** Extract preferences by prefix. - * + * * This function returns a lambda which, given * a key or a prefix, extracts the relevant * preferences from the designated preferences * store. - * + * * For instance, assume preferences = { * "a.b.c.d": 1, * "a.b.e.f": 2, * "g.h": 3 * } - * + * * And λ = preferencesλ(preferences). Then: - * + * * λ("a.b") → { "a.b.c.d": 1, "a.b.e.f": 2 } * λ("a.b.e.f") → { "a.b.e.f": 2 } * λ("g.x", {"g.x.": 99}) → { "g.x.": 99 } * λ("a.c", {"g.x.": 99}) → { "g.x.": 99 } - * + * * Note from the last two examples that a default value * may be provided and will be returned if a key does * not exist or is not searched for. From 313e9687bd31219749ab95b08b8c8da0431e85a5 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 18:11:58 +0100 Subject: [PATCH 031/113] Bring all the lib/utils from the frontend to the backend. The idea being that eventually we will symlink the lib/utils directory so that the same routines are available on both the frontend and the backend. --- lib/www/server/lib/utils/FormatTimestamp.js | 13 ++ lib/www/server/lib/utils/deep.js | 168 ++++++++++++++++++++ lib/www/server/lib/utils/hsl.js | 47 ++++++ lib/www/server/lib/utils/index.js | 23 ++- lib/www/server/lib/utils/markdown.js | 11 ++ lib/www/server/lib/utils/preferencesλ.js | 44 +++++ lib/www/server/lib/utils/throttle.js | 33 ++++ lib/www/server/lib/utils/truncateText.js | 10 ++ lib/www/server/lib/utils/unpack.js | 35 ++++ lib/www/server/lib/utils/withParentProps.js | 28 ++++ 10 files changed, 406 insertions(+), 6 deletions(-) create mode 100644 lib/www/server/lib/utils/FormatTimestamp.js create mode 100644 lib/www/server/lib/utils/deep.js create mode 100644 lib/www/server/lib/utils/hsl.js create mode 100644 lib/www/server/lib/utils/markdown.js create mode 100644 lib/www/server/lib/utils/preferencesλ.js create mode 100644 lib/www/server/lib/utils/throttle.js create mode 100644 lib/www/server/lib/utils/truncateText.js create mode 100644 lib/www/server/lib/utils/unpack.js create mode 100644 lib/www/server/lib/utils/withParentProps.js diff --git a/lib/www/server/lib/utils/FormatTimestamp.js b/lib/www/server/lib/utils/FormatTimestamp.js new file mode 100644 index 0000000..1ed936f --- /dev/null +++ b/lib/www/server/lib/utils/FormatTimestamp.js @@ -0,0 +1,13 @@ + + +function FormatTimestamp (str) { + const d = new Date(str); + if (isNaN(d)) { + return str; + } else { + // Get rid of milliseconds + return d.toISOString().substring(0,19)+"Z"; + } +} + +module.exports = FormatTimestamp; diff --git a/lib/www/server/lib/utils/deep.js b/lib/www/server/lib/utils/deep.js new file mode 100644 index 0000000..f33ce56 --- /dev/null +++ b/lib/www/server/lib/utils/deep.js @@ -0,0 +1,168 @@ + +/** Compare two possibly complex values for + * loose equality, going as deep as required in the + * case of complex objects. + */ +function deepCompare (a, b) { + if (typeof a == "object" && typeof b == "object") { + return !Object.entries(a).some( ([k, v]) => !deepCompare(v, b[k])) && + !Object.entries(b).some( ([k, v]) => !deepCompare(v, a[k])); + } else { + return a == b; + } +} + + +/** Compare two possibly complex values for + * strict equality. + */ +function deepEqual (a, b) { + if (typeof a === "object" && typeof b === "object") { + return !Object.entries(a).some( ([k, v]) => !deepEqual(v, b[k])) && + !Object.entries(b).some( ([k, v]) => !deepEqual(v, a[k])); + } else { + return a === b; + } +} + +/** Traverses an object and sets a nested value. + * + * Example: + * + * const obj = {a: {b: {c: "X"} } } + * deepSet(obj, ["a", "b", "c"], "d") + * → {a: {b: {c: "d"} } } + * + * This would be the equivalent of: + * + * obj?.a?.b?.c = "d"; + * + * Except that the above is not a legal expression. + * + * If a non-leaf property does not exist, this function + * creates it as an empty object ({}) and keeps traversing. + * + * The last member of `path` may be `null`, in which case, + * if the object pointed to by the next to last member is + * an array, an insert operation will take place. + * + */ +function deepSet (obj, path, value) { + const key = path.shift(); + if (!path.length) { + if (key === null && Array.isArray(obj)) { + obj.push(value); + } else { + obj[key] = value; + } + } else { + if (!Object.hasOwn(obj, key)) { + obj[key] = {}; + } + deepSet(obj[key], path, value); + } +} + +/** Returns a nested property. + * + * Example: + * + * const obj = {a: {b: {c: "d"} } } + * deepSet(obj, ["a", "b", "c"]) + * → "d" + * + * If `path` is known in advance, this is effectively + * the same as: + * + * obj?.a?.b?.c + * + * This might be useful when `path` is dynamic. + */ +function deepValue (obj, path) { + if (obj !== undefined) { + const key = path.shift(); + if (!path.length) { + if (key === undefined) { + return obj; + } else { + return obj[key]; + } + } else { + return deepValue(obj[key], path); + } + } +} + +// Copied from: +// https://gomakethings.com/how-to-deep-merge-arrays-and-objects-with-javascript/ + +/*! + * Deep merge two or more objects or arrays. + * (c) 2023 Chris Ferdinandi, MIT License, https://gomakethings.com + * @param {*} ...objs The arrays or objects to merge + * @returns {*} The merged arrays or objects + */ +function deepMerge (...objs) { + + /** + * Get the object type + * @param {*} obj The object + * @return {String} The object type + */ + function getType (obj) { + return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase(); + } + + /** + * Deep merge two objects + * @return {Object} + */ + function mergeObj (clone, obj) { + for (let [key, value] of Object.entries(obj)) { + let type = getType(value); + if (clone[key] !== undefined && getType(clone[key]) === type && ['array', 'object'].includes(type)) { + clone[key] = deepMerge(clone[key], value); + } else { + clone[key] = structuredClone(value); + } + } + } + + // Create a clone of the first item in the objs array + let clone = structuredClone(objs.shift()); + + // Loop through each item + for (let obj of objs) { + + // Get the object type + let type = getType(obj); + + // If the current item isn't the same type as the clone, replace it + if (getType(clone) !== type) { + clone = structuredClone(obj); + continue; + } + + // Otherwise, merge + if (type === 'array') { + // Replace old array with new + clone = [...structuredClone(obj)]; + } else if (type === 'object') { + mergeObj(clone, obj); + } else { + clone = obj; + } + + } + + return clone; + +} + +module.exports = { + deepCompare, + deepEqual, + deepSet, + deepValue, + deepMerge +}; diff --git a/lib/www/server/lib/utils/hsl.js b/lib/www/server/lib/utils/hsl.js new file mode 100644 index 0000000..5d6de6f --- /dev/null +++ b/lib/www/server/lib/utils/hsl.js @@ -0,0 +1,47 @@ +/** Return an HSL colour as a function of an input value + * `str`. + * + * Consider using as getHSL.bind(this) in Vue components + * in order to get access to the Vuetify theme configuration. + */ +function getHSL (str, saturation = 1, lightness = 0.25, offset = 0) { + + function getHash (v) { + if (typeof (v??false)[Symbol.iterator] != "function") { + // Not an iterable, make it one + v = String(v); + } + + return Math.abs([...v, ..." "].reduce( (acc, cur) => String(cur).charCodeAt(0) + ((acc << 5) - acc), 0 )); + } + + const h = (getHash(str) + offset) % 360; + const s = saturation * 100; + const l = this?.$vuetify?.theme?.isDark + ? (1-lightness) * 100 + : lightness * 100; + + return {h, s, l}; + +} + +/** Return a CSS hsl() or hsla() colour + * representation as a function of an input value. + * + * Consider using as getHSLColourFor.bind(this) – See + * note for getHSL() above. + */ +function getHSLColourFor (str, opacity = 1, saturation, lightness, offset) { + const _getHSL = getHSL.bind(this); + const {h, s, l} = _getHSL(str, saturation, lightness, offset); + if (opacity == 1) { + return `hsl(${h},${s}%,${l}%)`; + } else { + return `hsla(${h},${s}%,${l}%, ${opacity})`; + } +} + +module.exports = { + getHSL, + getHSLColourFor +} diff --git a/lib/www/server/lib/utils/index.js b/lib/www/server/lib/utils/index.js index bae0fee..42a47c8 100644 --- a/lib/www/server/lib/utils/index.js +++ b/lib/www/server/lib/utils/index.js @@ -1,13 +1,24 @@ module.exports = { - geometryAsString: require('./geometryAsString'), + ...require('./deep'), dms: require('./dms'), - replaceMarkers: require('./replaceMarkers'), + ...require('./flatEntries'), flattenQCDefinitions: require('./flattenQCDefinitions'), - deepMerge: require('./deepMerge'), + FormatTimestamp: require('./FormatTimestamp'), + geometryAsString: require('./geometryAsString'), + ...require('./hsl'), + ...require('./logicalPath'), // FIXME Breaking change (used to be logicalPath.…) + logicalPath: require('./logicalPath'), // NOTE For compatibility, see above + ...require('./markdown'), + preferencesλ: require('./preferencesλ'), + ...require('./ranges'), // FIXME Breaking change (used to be ranges.…) + ranges: require('./ranges'), // NOTE For compatibility, see above. removeNulls: require('./removeNulls'), - logicalPath: require('./logicalPath'), - ranges: require('./ranges'), + replaceMarkers: require('./replaceMarkers'), + setContentDisposition: require('./setContentDisposition'), + throttle: require('./throttle'), + truncateText: require('./truncateText'), unique: require('./unique'), - setContentDisposition: require('./setContentDisposition') + unpack: require('./unpack'), + withParentProps: require('./withParentProps') }; diff --git a/lib/www/server/lib/utils/markdown.js b/lib/www/server/lib/utils/markdown.js new file mode 100644 index 0000000..4957207 --- /dev/null +++ b/lib/www/server/lib/utils/markdown.js @@ -0,0 +1,11 @@ +const { marked, parseInline } = require('marked'); + +function markdown (str) { + return marked(String(str)); +} + +function markdownInline (str) { + return parseInline(String(str)); +} + +module.exports = { markdown, markdownInline }; diff --git a/lib/www/server/lib/utils/preferencesλ.js b/lib/www/server/lib/utils/preferencesλ.js new file mode 100644 index 0000000..429a252 --- /dev/null +++ b/lib/www/server/lib/utils/preferencesλ.js @@ -0,0 +1,44 @@ + + +/** Extract preferences by prefix. + * + * This function returns a lambda which, given + * a key or a prefix, extracts the relevant + * preferences from the designated preferences + * store. + * + * For instance, assume preferences = { + * "a.b.c.d": 1, + * "a.b.e.f": 2, + * "g.h": 3 + * } + * + * And λ = preferencesλ(preferences). Then: + * + * λ("a.b") → { "a.b.c.d": 1, "a.b.e.f": 2 } + * λ("a.b.e.f") → { "a.b.e.f": 2 } + * λ("g.x", {"g.x.": 99}) → { "g.x.": 99 } + * λ("a.c", {"g.x.": 99}) → { "g.x.": 99 } + * + * Note from the last two examples that a default value + * may be provided and will be returned if a key does + * not exist or is not searched for. + */ +function preferencesλ (preferences) { + + return function (key, defaults={}) { + const keys = Object.keys(preferences).filter(str => str.startsWith(key+".") || str == key); + + const settings = {...defaults}; + for (const str of keys) { + const k = str == key ? str : str.substring(key.length+1); + const v = preferences[str]; + settings[k] = v; + } + + return settings; + } + +} + +module.exports = preferencesλ; diff --git a/lib/www/server/lib/utils/throttle.js b/lib/www/server/lib/utils/throttle.js new file mode 100644 index 0000000..ff9a90e --- /dev/null +++ b/lib/www/server/lib/utils/throttle.js @@ -0,0 +1,33 @@ +/** + * Throttle a function call. + * + * It delays `callback` by `delay` ms and ignores any + * repeated calls from `caller` within at most `maxWait` + * milliseconds. + * + * Used to react to server events in cases where we get + * a separate notification for each row of a bulk update. + */ +function throttle (callback, caller, delay = 100, maxWait = 500) { + + const schedule = async () => { + caller.triggeredAt = Date.now(); + caller.timer = setTimeout(async () => { + await callback(); + caller.timer = null; + }, delay); + } + + if (!caller.timer) { + schedule(); + } else { + const elapsed = Date.now() - caller.triggeredAt; + if (elapsed > maxWait) { + cancelTimeout(caller.timer); + schedule(); + } + } + +} + +module.exports = throttle; diff --git a/lib/www/server/lib/utils/truncateText.js b/lib/www/server/lib/utils/truncateText.js new file mode 100644 index 0000000..a38ce71 --- /dev/null +++ b/lib/www/server/lib/utils/truncateText.js @@ -0,0 +1,10 @@ + +function truncateText (text, length=20) { + if (text?.length <= length) { + return text; + } else { + return text.slice(0, length/2)+"…"+text.slice(-(length/2)); + } +} + +module.exports = truncateText; diff --git a/lib/www/server/lib/utils/unpack.js b/lib/www/server/lib/utils/unpack.js new file mode 100644 index 0000000..1728eb1 --- /dev/null +++ b/lib/www/server/lib/utils/unpack.js @@ -0,0 +1,35 @@ +/** Unpacks attributes from array items. + * + * At it simplest, given an array of objects, + * the call unpack(rows, "x") returns an array + * of the "x" attribute of every item in rows. + * + * `key` may also be: + * + * - a function with the signature + * (Object) => any + * the result of applying the function to + * the object will be used as the unpacked + * value. + * + * - an array of strings, functions or other + * arrays. In this case, it does a recursive + * fold operation. NOTE: it mutates `key`. + * + */ +function unpack(rows, key) { + if (typeof key === "function") { + return rows && rows.map( row => key(row) ); + } else if (Array.isArray(key)) { + const car = key.shift(); + if (key.length) { + return unpack(unpack(rows, car), key); + } else { + return unpack(rows, car); + } + } else { + return rows && rows.map( row => row?.[key] ); + } +}; + +module.exports = unpack; diff --git a/lib/www/server/lib/utils/withParentProps.js b/lib/www/server/lib/utils/withParentProps.js new file mode 100644 index 0000000..e813a59 --- /dev/null +++ b/lib/www/server/lib/utils/withParentProps.js @@ -0,0 +1,28 @@ + +function withParentProps(item, parent, childrenKey, prop, currentValue) { + if (!Array.isArray(parent)) { + return; + } + + let currentPropValue = currentValue || parent[prop]; + + for (const entry of parent) { + if (entry[prop]) { + currentPropValue = entry[prop]; + } + + if (entry === item) { + return [item, currentPropValue]; + } + + if (entry[childrenKey]) { + const res = withParentProps(item, entry[childrenKey], childrenKey, prop, currentPropValue); + if (res[1]) { + return res; + } + } + } + return []; +} + +module.exports = withParentProps; From efe64f0a8c5c0d0b39a54afb9be50218bbe656bf Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 20:18:17 +0100 Subject: [PATCH 032/113] Implement PUT method for project configuration endpoint. In short: POST creates a new project PUT overwrites a project configuration with a new one PATCH merges the request body with the existing configuration --- lib/www/server/api/index.js | 1 + .../middleware/project/configuration/index.js | 2 +- .../middleware/project/configuration/put.js | 16 +++++ .../lib/db/project/configuration/index.js | 2 +- .../lib/db/project/configuration/put.js | 58 +++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 lib/www/server/api/middleware/project/configuration/put.js create mode 100644 lib/www/server/lib/db/project/configuration/put.js diff --git a/lib/www/server/api/index.js b/lib/www/server/api/index.js index 3065a0b..fa94437 100644 --- a/lib/www/server/api/index.js +++ b/lib/www/server/api/index.js @@ -114,6 +114,7 @@ app.map({ '/project/:project/configuration': { get: [ mw.project.configuration.get ], // Get project configuration patch: [ mw.auth.access.admin, mw.project.configuration.patch ], // Modify project configuration + put: [ mw.auth.access.admin, mw.project.configuration.put ], // Overwrite configuration }, /* diff --git a/lib/www/server/api/middleware/project/configuration/index.js b/lib/www/server/api/middleware/project/configuration/index.js index 6b40791..821d84c 100644 --- a/lib/www/server/api/middleware/project/configuration/index.js +++ b/lib/www/server/api/middleware/project/configuration/index.js @@ -2,7 +2,7 @@ module.exports = { get: require('./get'), // post: require('./post'), - // put: require('./put'), + put: require('./put'), patch: require('./patch'), // delete: require('./delete'), }; diff --git a/lib/www/server/api/middleware/project/configuration/put.js b/lib/www/server/api/middleware/project/configuration/put.js new file mode 100644 index 0000000..082877b --- /dev/null +++ b/lib/www/server/api/middleware/project/configuration/put.js @@ -0,0 +1,16 @@ + +const { project } = require('../../../../lib/db'); + +module.exports = async function (req, res, next) { + + try { + // TODO + // Implement If-Match header requirements + res.send(await project.configuration.put(req.params.project, req.body)); + next(); + } catch (err) { + next(err); + } + + +}; diff --git a/lib/www/server/lib/db/project/configuration/index.js b/lib/www/server/lib/db/project/configuration/index.js index 6b40791..821d84c 100644 --- a/lib/www/server/lib/db/project/configuration/index.js +++ b/lib/www/server/lib/db/project/configuration/index.js @@ -2,7 +2,7 @@ module.exports = { get: require('./get'), // post: require('./post'), - // put: require('./put'), + put: require('./put'), patch: require('./patch'), // delete: require('./delete'), }; diff --git a/lib/www/server/lib/db/project/configuration/put.js b/lib/www/server/lib/db/project/configuration/put.js new file mode 100644 index 0000000..ace06cc --- /dev/null +++ b/lib/www/server/lib/db/project/configuration/put.js @@ -0,0 +1,58 @@ +const { setSurvey } = require('../../connection'); +const { deepMerge, removeNulls } = require('../../../utils'); +const { modify } = require('../create'); + + +async function put (projectId, payload, opts = {}) { + let client; + try { + client = await setSurvey(); // Use public schema + + const text = ` + SELECT meta + FROM projects + WHERE pid = $1; + `; + + const res = await client.query(text, [projectId]); + + const source = res.rows[0].meta; + + if (!source) { + throw { status: 404, message: "Not found" }; + } + + console.log("PAYLOAD ID", payload.id, typeof payload); + if (("id" in payload) && (projectId.toLowerCase() != payload.id.toLowerCase())) { + throw { + status: 422, + message: "Project ID cannot be changed in this Dougal version" + } + } + + if (("name" in payload) && source.name && (source.name != payload.name)) { + throw { + status: 422, + message: "Project name cannot be changed in this Dougal version" + } + } + + // We do not allow users to change the schema + delete payload.schema; + + const dest = removeNulls(payload); + await modify(projectId, dest); + return dest; + + } catch (err) { + if (err.code == "42P01") { + throw { status: 404, message: "Not found" }; + } else { + throw err; + } + } finally { + client.release(); + } +} + +module.exports = put; From 53e7a06a18a5cbe4138b415ece4b2b1522c95719 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 20:21:37 +0100 Subject: [PATCH 033/113] Add Vue watch mixin to update a variable on changes to another. To be used where adding .sync to props is not convenient for one reason or another. --- lib/www/client/source/src/lib/watcher-mixin.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/www/client/source/src/lib/watcher-mixin.js diff --git a/lib/www/client/source/src/lib/watcher-mixin.js b/lib/www/client/source/src/lib/watcher-mixin.js new file mode 100644 index 0000000..f0ab525 --- /dev/null +++ b/lib/www/client/source/src/lib/watcher-mixin.js @@ -0,0 +1,14 @@ +import { deepCompare } from './utils'; + +function setIfDifferent(propsLocals) { + return Object.fromEntries(Object.entries(propsLocals).map( ([prop, local]) => [ + local, + () => { + if (!deepCompare(this[prop], this[local])){ + this[local] = structuredClone(this[prop]); + } + } + ])); +} + +export default setIfDifferent; From 26a487aa4788e48489c70978b81ce4433dc01835 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 20:50:02 +0100 Subject: [PATCH 034/113] Add json-builder component. It displays a JSON object as a , with editing capabilities. --- .../components/json-builder/json-builder.vue | 557 ++++++++++++++++++ .../json-builder/property-dialog.vue | 125 ++++ 2 files changed, 682 insertions(+) create mode 100644 lib/www/client/source/src/components/json-builder/json-builder.vue create mode 100644 lib/www/client/source/src/components/json-builder/property-dialog.vue diff --git a/lib/www/client/source/src/components/json-builder/json-builder.vue b/lib/www/client/source/src/components/json-builder/json-builder.vue new file mode 100644 index 0000000..ed3bdca --- /dev/null +++ b/lib/www/client/source/src/components/json-builder/json-builder.vue @@ -0,0 +1,557 @@ + + + diff --git a/lib/www/client/source/src/components/json-builder/property-dialog.vue b/lib/www/client/source/src/components/json-builder/property-dialog.vue new file mode 100644 index 0000000..db5de0c --- /dev/null +++ b/lib/www/client/source/src/components/json-builder/property-dialog.vue @@ -0,0 +1,125 @@ + + + From 80de0c1bb01f3a17b1fd094e96f2be08ba749441 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 20:52:08 +0100 Subject: [PATCH 035/113] Modify deepSet() to allow appending to arrays --- lib/www/client/source/src/lib/utils.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/lib/utils.js b/lib/www/client/source/src/lib/utils.js index 3b55302..415d4f2 100644 --- a/lib/www/client/source/src/lib/utils.js +++ b/lib/www/client/source/src/lib/utils.js @@ -175,11 +175,19 @@ function deepEqual (a, b) { * If a non-leaf property does not exist, this function * creates it as an empty object ({}) and keeps traversing. * + * The last member of `path` may be `null`, in which case, + * if the object pointed to by the next to last member is + * an array, an insert operation will take place. + * */ function deepSet (obj, path, value) { const key = path.shift(); if (!path.length) { - obj[key] = value; + if (key === null && Array.isArray(obj)) { + obj.push(value); + } else { + obj[key] = value; + } } else { if (!Object.hasOwn(obj, key)) { obj[key] = {}; From 13f68d731442959f4583b5042169c9ca1bdaefe7 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 20:52:33 +0100 Subject: [PATCH 036/113] deepValue with an empty path returns the object itself --- lib/www/client/source/src/lib/utils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/lib/utils.js b/lib/www/client/source/src/lib/utils.js index 415d4f2..a88095c 100644 --- a/lib/www/client/source/src/lib/utils.js +++ b/lib/www/client/source/src/lib/utils.js @@ -215,7 +215,11 @@ function deepValue (obj, path) { if (obj !== undefined) { const key = path.shift(); if (!path.length) { - return obj[key]; + if (key === undefined) { + return obj; + } else { + return obj[key]; + } } else { return deepValue(obj[key], path); } From 399e86be874815ba70ad925ca893a0ebcd401f4b Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 21:00:07 +0100 Subject: [PATCH 037/113] Refactor the interface between ProjectSettings and subcomponents. This is still not set in stone and not fully consistent from one subcomponent to another, but the general idea is that instead of passing everything in one property via v-model we use v-bind instead with a variable list of props depending on the needs of the subcomponent. We listen for @input and a new @merge event in order to apply any changes to the *local* configuration. The local configuration then needs to be uploaded to the server via a separate step. We might change this in a later commit, so that changes made in subcomponents are automatically applied to the local configuration. --- .../source/src/views/ProjectSettings.vue | 169 ++++-------------- 1 file changed, 37 insertions(+), 132 deletions(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index 72c1ba2..165fc35 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -55,7 +55,12 @@ - + @@ -206,68 +211,41 @@ export default { values: (obj) => ({ id: obj?.id, name: obj?.name - }), - save: async (data, cfg) => { - await this.patch({ - id: data.id, - name: data.name, - }); - } + }) }, { id: "groups", name: "Groups", values: (obj) => ({ groups: obj?.groups - }), - save: async (data, cfg) => { - await this.patch({ - groups: data.groups, - }); - } + }) }, { id: "geodetics", name: "Geodetics", values: (obj) => ({ epsg: obj?.epsg - }), - save: async (data, cfg) => { - await this.patch({ - epsg: data.epsg - }); - } + }) }, { id: "binning", name: "Binning", - values: (obj) => { - const data = {...obj.binning}; - data.origin = {...obj.binning.origin}; - return data; - }, - save: async (data, cfg) => { - await this.patch({binning: {...data}}); - } + values: (obj) => ({ + ...obj.binning + }) }, { id: "input_files", name: "Input files", - values: obj => ({ path: obj.rootPath}), - save: async (data, cfg) => { - await this.patch({rootPath: data.path}); - }, + values: obj => ({ rootPath: obj.rootPath}), children: [ { id: "preplots", name: "Preplots", values: (obj) => ({ - preplots: obj.preplots, + preplots: structuredClone(obj.preplots), rootPath: obj.rootPath - }), - save: async (data, cfg) => { - await this.patch({preplots: [...data.preplots]}) - } + }) }, { id: "raw_data", @@ -278,16 +256,14 @@ export default { name: "P1/11", values: (obj) => ({ rootPath: obj.rootPath, - globs: [...obj.raw.p111.globs], - paths: [...obj.raw.p111.paths], - pattern: structuredClone(obj.raw.p111.pattern) - }), - save: async (data, cfg) => { - await this.patch({raw: {p111: {...data}}}); - } + globs: obj.raw.p111.globs, + paths: obj.raw.p111.paths, + pattern: obj.raw?.p111?.pattern, + lineNameInfo: obj.raw?.p111?.lineNameInfo + }) }, { - id: "raw_data_smartsource", + id: "raw_data_smsrc", name: "Smartsource", values: (obj) => ({ rootPath: obj.rootPath, @@ -305,19 +281,7 @@ export default { values: (obj) => ({ regex: obj.raw.ntbp?.pattern?.regex, flags: obj.raw.ntbp?.pattern?.flags - }), - save: async (data, cfg) => { - await this.patch({ - raw:{ - ntbp: { - pattern: { - regex: data.regex, - flags: data.flags - } - } - } - }) - } + }) } ] }, @@ -330,13 +294,10 @@ export default { name: "P1/11", values: (obj) => ({ rootPath: obj.rootPath, - globs: [...obj.final.p111.globs], - paths: [...obj.final.p111.paths], - pattern: structuredClone(obj.final.p111.pattern) - }), - save: async (data, cfg) => { - await this.patch({final: {p111: {...data}}}); - } + globs: obj.final.p111.globs, + paths: obj.final.p111.paths, + pattern: obj.final.p111.pattern + }) }, { id: "final_data_pending", @@ -344,19 +305,7 @@ export default { values: (obj) => ({ regex: obj.final.pending?.pattern?.regex, flags: obj.final.pending?.pattern?.flags - }), - save: async (data, cfg) => { - await this.patch({ - final:{ - pending: { - pattern: { - regex: data.regex, - flags: data.flags - } - } - } - }) - } + }) } ] }, @@ -365,52 +314,19 @@ export default { { id: "line_name_format", name: "Line name format", - values: (obj) => ({...obj.online?.line?.pattern}), - save: async (data, cfg) => { - await this.patch({ - online: { - line: { - pattern: { - ...data - } - } - } - }); - } + values: (obj) => ({ + lineNameInfo: obj?.online?.line?.lineNameInfo + }) }, { id: "planner_settings", name: "Planner settings", - values: (obj) => ({...obj.planner}), - save: async (data, cfg) => { - await this.patch({ - planner: {...data} - }); - } + values: (obj) => ({planner: obj?.planner}) }, { id: "production", name: "Production settings", - values: (obj) => ({...obj.production}), - save: async (data, cfg) => { - await this.patch({ - production: {...data} - }); - } - }, - { - id: "logging", - name: "Logging", - children: [ - { - id: "logging_preset_comments", - name: "Preset comments" - }, - { - id: "logging_labels", - name: "Labels" - } - ] + values: (obj) => ({production: obj?.production}) }, { id: "cloud_apis", @@ -419,10 +335,7 @@ export default { { id: "asaqc", name: "ASAQC", - values: (obj) => ({...obj.asaqc}), - save: async (data, cfg) => { - await this.patch({asaqc: {...cfg}}); - } + values: (obj) => ({value: obj?.cloud?.asaqc}), } ] } @@ -530,20 +443,12 @@ export default { this.active.pop(); }, - async save (data) { - console.log("SAVING", data); - if (this.activeItem?.save) { - await this.activeItem.save(data, this.configuration); - /* - const cfg = this.activeItem.save(data, this.configuration); - this.configuration = {...cfg}; - - this.surveyState = false; - */ - // TODO And now push to the server - } + merge ([path, value]) { + console.log("MERGING", path, value); + deepSet(this.configuration, path, value); }, + // Use to change the project's archival status async patch (data) { const url = `/project/${this.$route.params.project}/configuration`; const init = { From d1c041995d53d7709cbcfdeaf4803b2cb700e390 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 21:08:06 +0100 Subject: [PATCH 038/113] Refactor --- .../components/project-settings/name-id.vue | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/www/client/source/src/components/project-settings/name-id.vue b/lib/www/client/source/src/components/project-settings/name-id.vue index 62c91f1..d60f27c 100644 --- a/lib/www/client/source/src/components/project-settings/name-id.vue +++ b/lib/www/client/source/src/components/project-settings/name-id.vue @@ -8,14 +8,14 @@ label="ID" hint="Short survey ID" persistent-hint - v-model="id" + v-model="id_" > @@ -44,31 +44,41 @@ export default { name: "DougalProjectSettingsNameId", - props: [ "value" ], + props: { + id: String, + name: String + }, data () { return { - id: "", - name: "" + id_: "", + name_: "" } }, watch: { - value (newValue) { - this.reset(); + id () { + if (this.id != this.id_) { + this.id_ = this.id; + } + }, + + name () { + if (this.name != this.name_) { + this.name_ = this.name; + } } }, methods: { reset () { - for (const key of Object.keys(this.$data)) { - this[key] = this.value[key]; - } + this.id_ = this.id; + this.name_ = this.name; }, save () { - this.$emit('input', {...this.$data}); + this.$emit('input', {id: this.id_, name: this.name_}); }, back () { From 2f56d377c510829f23f621f1eb5f3eaf9e3f11cc Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 21:09:42 +0100 Subject: [PATCH 039/113] Refactor --- .../components/project-settings/groups.vue | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/www/client/source/src/components/project-settings/groups.vue b/lib/www/client/source/src/components/project-settings/groups.vue index 31bcbaa..1404051 100644 --- a/lib/www/client/source/src/components/project-settings/groups.vue +++ b/lib/www/client/source/src/components/project-settings/groups.vue @@ -1,11 +1,11 @@ diff --git a/lib/www/client/source/src/components/project-settings/preplots.vue b/lib/www/client/source/src/components/project-settings/preplots.vue index a8ad9a1..3893c37 100644 --- a/lib/www/client/source/src/components/project-settings/preplots.vue +++ b/lib/www/client/source/src/components/project-settings/preplots.vue @@ -3,51 +3,43 @@ Preplots Preplot files location and format. - - {{tabNameFor(item)}} - - - - - - - - - + + + + {{ titleFor(preplot) }} + + + - - + + mdi-delete-outline + Delete + + + + + + + + + mdi-file-document-plus-outline + New preplot + - - - Column settings - - - - - - - - - - - diff --git a/lib/www/client/source/src/components/project-settings/input-smartsource.vue b/lib/www/client/source/src/components/project-settings/input-smartsource.vue deleted file mode 100644 index e1ead07..0000000 --- a/lib/www/client/source/src/components/project-settings/input-smartsource.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - - - diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index 165fc35..f7e99d4 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -154,7 +154,7 @@ import DougalProjectSettingsRawP111 from '@/components/project-settings/input-ra import DougalProjectSettingsFinalP111 from '@/components/project-settings/input-final-p111'; import DougalProjectSettingsRawNTBP from '@/components/project-settings/input-raw-ntbp'; import DougalProjectSettingsFinalPending from '@/components/project-settings/input-final-pending'; -import DougalProjectSettingsSmartsource from '@/components/project-settings/input-smartsource'; +import DougalProjectSettingsSmartsourceHeader from '@/components/project-settings/input-smartsource-header'; import DougalProjectSettingsPlanner from '@/components/project-settings/planner'; import DougalProjectSettingsOnlineLineNameFormat from '@/components/project-settings/online-line-name-format'; import DougalProjectSettingsASAQC from '@/components/project-settings/asaqc'; @@ -171,7 +171,7 @@ const components = { preplots: DougalProjectSettingsPreplots, //raw_data: DougalProjectSettingsNotImplemented, raw_data_p111: DougalProjectSettingsRawP111, - raw_data_smartsource: DougalProjectSettingsSmartsource, + raw_data_smsrc_header: DougalProjectSettingsSmartsourceHeader, raw_data_ntbp: DougalProjectSettingsRawNTBP, //final_data: DougalProjectSettingsNotImplemented, final_data_p111: DougalProjectSettingsFinalP111, @@ -265,15 +265,19 @@ export default { { id: "raw_data_smsrc", name: "Smartsource", - values: (obj) => ({ - rootPath: obj.rootPath, - globs: [...obj.raw.smsrc.globs], - paths: [...obj.raw.smsrc.paths], - pattern: structuredClone(obj.raw.smsrc.pattern) - }), - save: async (data, cfg) => { - await this.patch({raw: {smsrc: {...data}}}); - } + children: [ + { + id: "raw_data_smsrc_header", + name: "Headers", + values: (obj) => ({ + rootPath: obj.rootPath, + globs: obj?.raw?.source?.smsrc?.header?.globs, + paths: obj?.raw?.source?.smsrc?.header?.paths, + pattern: obj?.raw?.source?.smsrc?.header?.pattern, + lineNameInfo: obj?.raw?.source?.smsrc?.header?.lineNameInfo + }) + }, + ] }, { id: "raw_data_ntbp", From 3fbc2668099ac5e94868764aa272d0c239df1cbe Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 22:42:08 +0100 Subject: [PATCH 054/113] Add configuration GUI for SEG-Y near field hydrophone data --- .../input-smartsource-segy.vue | 135 ++++++++++++++++++ .../source/src/views/ProjectSettings.vue | 13 ++ 2 files changed, 148 insertions(+) create mode 100644 lib/www/client/source/src/components/project-settings/input-smartsource-segy.vue diff --git a/lib/www/client/source/src/components/project-settings/input-smartsource-segy.vue b/lib/www/client/source/src/components/project-settings/input-smartsource-segy.vue new file mode 100644 index 0000000..9711da3 --- /dev/null +++ b/lib/www/client/source/src/components/project-settings/input-smartsource-segy.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index f7e99d4..e0303ca 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -155,6 +155,7 @@ import DougalProjectSettingsFinalP111 from '@/components/project-settings/input- import DougalProjectSettingsRawNTBP from '@/components/project-settings/input-raw-ntbp'; import DougalProjectSettingsFinalPending from '@/components/project-settings/input-final-pending'; import DougalProjectSettingsSmartsourceHeader from '@/components/project-settings/input-smartsource-header'; +import DougalProjectSettingsSmartsourceSegy from '@/components/project-settings/input-smartsource-segy'; import DougalProjectSettingsPlanner from '@/components/project-settings/planner'; import DougalProjectSettingsOnlineLineNameFormat from '@/components/project-settings/online-line-name-format'; import DougalProjectSettingsASAQC from '@/components/project-settings/asaqc'; @@ -172,6 +173,7 @@ const components = { //raw_data: DougalProjectSettingsNotImplemented, raw_data_p111: DougalProjectSettingsRawP111, raw_data_smsrc_header: DougalProjectSettingsSmartsourceHeader, + raw_data_smsrc_segy: DougalProjectSettingsSmartsourceSegy, raw_data_ntbp: DougalProjectSettingsRawNTBP, //final_data: DougalProjectSettingsNotImplemented, final_data_p111: DougalProjectSettingsFinalP111, @@ -277,6 +279,17 @@ export default { lineNameInfo: obj?.raw?.source?.smsrc?.header?.lineNameInfo }) }, + { + id: "raw_data_smsrc_segy", + name: "Hydrophone data", + values: (obj) => ({ + rootPath: obj.rootPath, + globs: obj?.raw?.source?.smsrc?.segy?.globs, + paths: obj?.raw?.source?.smsrc?.segy?.paths, + pattern: obj?.raw?.source?.smsrc?.segy?.pattern, + lineNameInfo: obj?.raw?.source?.smsrc?.segy?.lineNameInfo + }) + } ] }, { From 53b4213a056fbe92b9a447787859c289f1f426cb Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 22:58:41 +0100 Subject: [PATCH 055/113] Fix configuration not being refreshed --- lib/www/client/source/src/views/ProjectSettings.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index e0303ca..cfb64ef 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -480,7 +480,7 @@ export default { const refreshedConfiguration = await this.api([url, init, callback]); if (refreshedConfiguration) { - this.configuration == refreshedConfiguration; + this.configuration = refreshedConfiguration; } }, From fd84eb1ebb71f408396b442e537c9338f27f6c27 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Mon, 13 Nov 2023 23:01:44 +0100 Subject: [PATCH 056/113] Add "advanced configuration" view. This view shows a tree view of the raw JSON configuration object, allowing the user to add / edit / delete any properties whatsoever. It is semi-hidden behind a context menu. The user has to right-click on the header of the left-hand column showing the list of configuration sections and then click on the red "advanced configuration" button. In the advanced configuration view there is another button to go back to normal configuration. It is only possible to save / refresh the configuration from the normal view. --- .../source/src/views/ProjectSettings.vue | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/lib/www/client/source/src/views/ProjectSettings.vue b/lib/www/client/source/src/views/ProjectSettings.vue index cfb64ef..020212c 100644 --- a/lib/www/client/source/src/views/ProjectSettings.vue +++ b/lib/www/client/source/src/views/ProjectSettings.vue @@ -21,11 +21,14 @@ + + Survey configuration @@ -137,12 +140,64 @@ + + + + + + + + Advanced survey configuration + + + Go to normal configuration + + + + + + + + + + + + + + + + Advanced configuration… + + + From 59971a43fef014310e639cfeccd4d27a62bd23c3 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Sat, 4 May 2024 17:27:08 +0200 Subject: [PATCH 081/113] Support fixed text in --- .../decoder/fixed-string-decoder.vue | 128 +++++++++++++++++- 1 file changed, 122 insertions(+), 6 deletions(-) diff --git a/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue b/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue index 09d2c98..425021f 100644 --- a/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue +++ b/lib/www/client/source/src/components/decoder/fixed-string-decoder.vue @@ -30,6 +30,12 @@ + + + +

Variable fields

+
+ + + + +

Fixed strings

+
+ + + + + + + + + + + + + + + + +
@@ -115,11 +181,16 @@ top: -1px; } -.input, .multiline >>> .chunk { +.input, .multiline >>> .chunk-field { padding-inline: 1px; border: 1px solid; } +.input, .multiline >>> .chunk-fixed { + padding-inline: 1px; + border: 1px dashed; +} + .input, .multiline >>> .chunk-empty { padding-inline: 1px; } @@ -129,17 +200,24 @@ border: 1px solid grey; color: grey; } + +.input, .multiline >>> .chunk-mismatch { + padding-inline: 1px; + border: 2px solid red !important; +} diff --git a/lib/www/client/source/src/components/encoder/fixed-string-encoder-sample.vue b/lib/www/client/source/src/components/encoder/fixed-string-encoder-sample.vue new file mode 100644 index 0000000..02ac914 --- /dev/null +++ b/lib/www/client/source/src/components/encoder/fixed-string-encoder-sample.vue @@ -0,0 +1,351 @@ + + + + + diff --git a/lib/www/client/source/src/components/encoder/fixed-string-encoder.vue b/lib/www/client/source/src/components/encoder/fixed-string-encoder.vue new file mode 100644 index 0000000..f38fd11 --- /dev/null +++ b/lib/www/client/source/src/components/encoder/fixed-string-encoder.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/lib/www/client/source/src/components/project-settings/online-line-name-format.vue b/lib/www/client/source/src/components/project-settings/online-line-name-format.vue index dfd95ec..03efedc 100644 --- a/lib/www/client/source/src/components/project-settings/online-line-name-format.vue +++ b/lib/www/client/source/src/components/project-settings/online-line-name-format.vue @@ -4,20 +4,16 @@ Line name decoding configuration for real-time data - - - + :properties="properties" + :fields.sync="fields_" + :values.sync="values_" + > @@ -50,16 +46,19 @@