Files
dougal-software/lib/www/client/source/src/views/ProjectSettings.vue

578 lines
17 KiB
Vue
Raw Normal View History

2023-10-29 15:14:15 +01:00
<template>
<v-container fluid>
<v-overlay :value="loading && !configuration" absolute>
<v-progress-circular
indeterminate
size="64"
></v-progress-circular>
</v-overlay>
<v-overlay :value="!configuration && !loading" absolute opacity="0.8">
<v-row justify="center">
<v-alert
type="error"
>
The configuration could not be loaded.
</v-alert>
</v-row>
<v-row justify="center">
<v-btn color="primary" @click="getConfiguration">Retry</v-btn>
</v-row>
</v-overlay>
<v-window v-model="viewMode">
<v-window-item>
2023-10-29 15:14:15 +01:00
<v-row>
<v-col cols="4">
<v-toolbar
dense
flat
@contextmenu="showContextMenu"
2023-10-29 15:14:15 +01:00
>
<v-toolbar-title>
Survey configuration
</v-toolbar-title>
</v-toolbar>
<v-switch
dense
label="Active"
title="Inactive surveys do not take new data, but changes to the logs can still be made."
:disabled="!configuration"
v-model="surveyState"
></v-switch>
<v-treeview
dense
activatable
hoverable
:multiple-active="false"
:active.sync="active"
:open.sync="open"
:items="treeview"
style="cursor:pointer;"
>
</v-treeview>
</v-col>
<v-col cols="8" v-if="activeComponent">
<component
:is="activeComponent"
v-bind="activeValues"
@merge="merge"
@close="deselect"
></component>
2023-10-29 15:14:15 +01:00
</v-col>
<v-col cols="8" v-else>
<v-card>
<v-card-text>
<p>Select a configuration section to change its settings. When clicking <v-btn small color="primary">Save</v-btn> the changes are immediately saved to the server and start to take effect.</p>
<v-divider class="my-5"></v-divider>
<v-alert border="left" type="warning">
Be careful when changing configuration settings! It is rather easy to break things.
</v-alert>
<v-alert border="left" type="info">
On the first save, the survey will be switched to <em>inactive</em> if not already so. This means that no new data will be read. Remember to switch back to <strong>active</strong> when satisfied with your changes.
</v-alert>
<v-divider class="my-5"></v-divider>
<p>It is recommended that you download a backup of your configuration before making any changes.</p>
<v-divider class="my-5"></v-divider>
</v-card-text>
<v-card-actions>
<v-btn outlined
color="primary"
title="Any unsaved changes you might have will be lost."
@click="getConfiguration"
>
<v-icon small left>mdi-cloud-refresh</v-icon>
Refresh
</v-btn>
<v-spacer></v-spacer>
<v-btn
class="ml-2"
color="primary"
:disabled="!configuration"
:href="`/api/project/${this.$route.params.project}/configuration?mime=application/yaml`"
:download="`${this.$route.params.project}.yaml`"
>
<v-icon small left>mdi-cloud-download</v-icon>
Download
</v-btn>
<v-dialog
max-width="400px"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
class="ml-2"
color="warning"
v-bind="attrs"
v-on="on"
>
<v-icon small left>mdi-cloud-upload</v-icon>
Upload
</v-btn>
</template>
<v-card flat>
<v-card-text class="pt-5">
<v-file-input
class="mt-4"
show-size
accept="application/json"
label="Select configuration file"
append-outer-icon="mdi-cloud-upload"
@click-append-outer="upload"
></v-file-input>
</v-card-text>
</v-card>
</v-dialog>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-window-item>
<v-window-item>
<v-row>
<v-col cols="12">
<v-toolbar
dense
flat
@contextmenu="showContextMenu"
>
<v-toolbar-title>
Advanced survey configuration
</v-toolbar-title>
<v-spacer/>
<v-btn small outlined @click="viewMode=0">Go to normal configuration</v-btn>
</v-toolbar>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<dougal-json-builder
name="Dougal configuration"
v-model="configuration"
></dougal-json-builder>
</v-col>
</v-row>
</v-window-item>
</v-window>
<v-menu
v-model="contextMenu"
:position-x="contextMenuX"
:position-y="contextMenuY"
absolute
offset-y
>
<v-list dense>
<v-list-item>
<v-btn
small
outlined
color="red"
title="Not a good idea"
@click="viewMode=1"
>Advanced configuration</v-btn>
</v-list-item>
</v-list>
</v-menu>
2023-10-29 15:14:15 +01:00
</v-container>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import { deepSet } from '@/lib/utils';
import DougalJsonBuilder from '@/components/json-builder/json-builder';
2023-10-29 15:14:15 +01:00
import DougalProjectSettingsNameId from '@/components/project-settings/name-id';
import DougalProjectSettingsGroups from '@/components/project-settings/groups';
import DougalProjectSettingsGeodetics from '@/components/project-settings/geodetics';
import DougalProjectSettingsBinning from '@/components/project-settings/binning';
import DougalProjectSettingsFilePath from '@/components/project-settings/file-path';
import DougalProjectSettingsPreplots from '@/components/project-settings/preplots';
import DougalProjectSettingsRawP111 from '@/components/project-settings/input-raw-p111';
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 DougalProjectSettingsSmartsourceHeader from '@/components/project-settings/input-smartsource-header';
import DougalProjectSettingsSmartsourceSegy from '@/components/project-settings/input-smartsource-segy';
2023-10-29 15:14:15 +01:00
import DougalProjectSettingsPlanner from '@/components/project-settings/planner';
import DougalProjectSettingsOnlineLineNameFormat from '@/components/project-settings/online-line-name-format';
import DougalProjectSettingsASAQC from '@/components/project-settings/asaqc';
import DougalProjectSettingsProduction from '@/components/project-settings/production';
// Temporary placeholder component
import DougalProjectSettingsNotImplemented from '@/components/project-settings/not-implemented';
const components = {
name_id: DougalProjectSettingsNameId,
groups: DougalProjectSettingsGroups,
geodetics: DougalProjectSettingsGeodetics,
binning: DougalProjectSettingsBinning,
input_files: DougalProjectSettingsFilePath,
preplots: DougalProjectSettingsPreplots,
//raw_data: DougalProjectSettingsNotImplemented,
raw_data_p111: DougalProjectSettingsRawP111,
raw_data_smsrc_header: DougalProjectSettingsSmartsourceHeader,
raw_data_smsrc_segy: DougalProjectSettingsSmartsourceSegy,
2023-10-29 15:14:15 +01:00
raw_data_ntbp: DougalProjectSettingsRawNTBP,
//final_data: DougalProjectSettingsNotImplemented,
final_data_p111: DougalProjectSettingsFinalP111,
final_data_pending: DougalProjectSettingsFinalPending,
line_name_format: DougalProjectSettingsOnlineLineNameFormat,
planner_settings: DougalProjectSettingsPlanner,
logging: DougalProjectSettingsNotImplemented,
logging_preset_comments: DougalProjectSettingsNotImplemented,
logging_labels: DougalProjectSettingsNotImplemented,
asaqc: DougalProjectSettingsASAQC,
production: DougalProjectSettingsProduction
}
export default {
name: "DougalProjectSettings",
components: {
DougalJsonBuilder
},
2023-10-29 15:14:15 +01:00
data () {
return {
configuration: null,
active: [],
open: [],
treeview: [
/*
{
id: 0,
name: "Archive",
values: (cfg) => cfg.archived,
save: (data, cfg) => {
cfg.archived = data.archived;
return cfg;
}
},
*/
{
id: "name_id",
name: "Name and ID",
values: (obj) => ({
id: obj?.id,
name: obj?.name
})
2023-10-29 15:14:15 +01:00
},
{
id: "groups",
name: "Groups",
values: (obj) => ({
groups: obj?.groups
})
2023-10-29 15:14:15 +01:00
},
{
id: "geodetics",
name: "Geodetics",
values: (obj) => ({
epsg: obj?.epsg
})
2023-10-29 15:14:15 +01:00
},
{
id: "binning",
name: "Binning",
values: (obj) => ({
...obj.binning
})
2023-10-29 15:14:15 +01:00
},
{
id: "input_files",
name: "Input files",
values: obj => ({ rootPath: obj.rootPath}),
2023-10-29 15:14:15 +01:00
children: [
{
id: "preplots",
name: "Preplots",
values: (obj) => ({
preplots: structuredClone(obj.preplots),
2023-10-29 15:14:15 +01:00
rootPath: obj.rootPath
})
2023-10-29 15:14:15 +01:00
},
{
id: "raw_data",
name: "Raw data",
children: [
{
id: "raw_data_p111",
name: "P1/11",
values: (obj) => ({
rootPath: obj.rootPath,
globs: obj.raw.p111.globs,
paths: obj.raw.p111.paths,
pattern: obj.raw?.p111?.pattern,
lineNameInfo: obj.raw?.p111?.lineNameInfo
})
2023-10-29 15:14:15 +01:00
},
{
id: "raw_data_smsrc",
2023-10-29 15:14:15 +01:00
name: "Smartsource",
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_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
})
}
]
2023-10-29 15:14:15 +01:00
},
{
id: "raw_data_ntbp",
name: "NTBP detection",
values: (obj) => ({
regex: obj.raw.ntbp?.pattern?.regex,
flags: obj.raw.ntbp?.pattern?.flags
})
2023-10-29 15:14:15 +01:00
}
]
},
{
id: "final_data",
name: "Final data",
children: [
{
id: "final_data_p111",
name: "P1/11",
values: (obj) => ({
rootPath: obj.rootPath,
globs: obj.final.p111.globs,
paths: obj.final.p111.paths,
pattern: obj.final.p111.pattern
})
2023-10-29 15:14:15 +01:00
},
{
id: "final_data_pending",
name: "Pending line detection",
values: (obj) => ({
regex: obj.final.pending?.pattern?.regex,
flags: obj.final.pending?.pattern?.flags
})
2023-10-29 15:14:15 +01:00
}
]
},
]
},
{
id: "line_name_format",
name: "Line name format",
values: (obj) => ({
lineNameInfo: obj?.online?.line?.lineNameInfo
})
2023-10-29 15:14:15 +01:00
},
{
id: "planner_settings",
name: "Planner settings",
values: (obj) => ({planner: obj?.planner})
2023-10-29 15:14:15 +01:00
},
{
id: "production",
name: "Production settings",
values: (obj) => ({production: obj?.production})
2023-10-29 15:14:15 +01:00
},
{
id: "cloud_apis",
name: "Cloud APIs",
children: [
{
id: "asaqc",
name: "ASAQC",
values: (obj) => ({value: obj?.cloud?.asaqc}),
2023-10-29 15:14:15 +01:00
}
]
}
],
viewMode: 0,
dialog: false,
contextMenu: false,
contextMenuX: null,
contextMenuY: null
2023-10-29 15:14:15 +01:00
};
},
watch: {
active (cur, prev) {
if (cur == prev) {
return;
}
console.log("Current", cur);
console.log("Item", this.activeItem);
console.log("Component", this.activeComponent);
if (!this.activeComponent && this.activeItem?.children?.length) {
// Automatically expand this item
if (!this.open.includes(cur)) {
this.open.push(cur);
}
this.$nextTick( () => {
const idx = this.active.findIndex(i => i == cur);
if (idx != -1) {
this.active.push(this.activeItem.children[0].id);
this.active.splice(cur, 1);
}
});
}
}
},
computed: {
activeComponent () {
return components[this.active[0]];
},
activeItem () {
function walk (tree) {
const list = [];
for (const leaf of tree) {
if (leaf.children) {
list.push(...walk(leaf.children));
}
list.push(leaf);
}
return list;
}
return walk(this.treeview).find(i => i.id === this.active[0]);
},
activeValues () {
return this.activeItem?.values &&
this.activeItem.values(this.configuration);
},
surveyState: {
get () {
return this.configuration && !this.configuration.archived;
},
async set (value) {
if (this.configuration) {
await this.patch({archived: !value});
// this.configuration.archived = !value;
}
}
},
...mapGetters(['user', 'writeaccess', 'loading', 'serverEvent'])
},
methods: {
async getConfiguration () {
this.configuration = null;
2023-11-02 20:46:26 +01:00
const url = `/project/${this.$route.params.project}/configuration`;
2023-11-03 16:31:58 +01:00
const init = {
headers: {
"If-None-Match": "" // Ensure we get a fresh response
}
};
this.configuration = await this.api([url, init]);
2023-10-29 15:14:15 +01:00
},
makeTree (obj, id=0) {
const isObject = typeof obj === "object" && !Array.isArray(obj) && obj !== null;
return isObject
? Object.keys(obj).map(k => {
const children = this.makeTree(obj[k], id); //.filter(i => i !== null);
return {
id: id++,
name: k,
children
}
})
: null;
},
deselect () {
this.active.pop();
},
merge ([path, value]) {
console.log("MERGING", path, value);
deepSet(this.configuration, path, value);
2023-10-29 15:14:15 +01:00
},
// Use to change the project's archival status
2023-10-29 15:14:15 +01:00
async patch (data) {
2023-11-02 20:46:26 +01:00
const url = `/project/${this.$route.params.project}/configuration`;
2023-10-29 15:14:15 +01:00
const init = {
method: "PATCH",
body: data
};
const callback = async (err, res) => {
if (!err && res.ok) {
this.showSnack(["Configuration saved", "success"]);
}
};
const refreshedConfiguration = await this.api([url, init, callback]);
if (refreshedConfiguration) {
2023-11-13 22:58:41 +01:00
this.configuration = refreshedConfiguration;
2023-10-29 15:14:15 +01:00
}
},
upload () {
console.log("UPLOAD");
},
closeDialog () {
},
showContextMenu (e) {
e.preventDefault();
this.contextMenu = false
this.contextMenuX = e.clientX
this.contextMenuY = e.clientY
this.$nextTick(() => {
this.contextMenu = true
})
},
2023-10-29 15:14:15 +01:00
...mapActions(["api", "showSnack"])
},
async mounted () {
this.getConfiguration();
},
}
</script>