Mercurial > gemma
changeset 3649:fb8a53c7c6d3
merged configuration branch into default
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Thu, 13 Jun 2019 11:20:17 +0200 |
parents | 0ec5c8ec1e44 (current diff) 6bb8def12f20 (diff) |
children | 2a079d0a71c1 |
files | client/src/components/map/layers.js |
diffstat | 11 files changed, 300 insertions(+), 104 deletions(-) [+] |
line wrap: on
line diff
--- a/client/src/assets/application.scss Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/assets/application.scss Thu Jun 13 11:20:17 2019 +0200 @@ -222,3 +222,8 @@ width: 50% !important; height: 50% !important; } + +.custom-control-input:checked~.custom-control-label::before { + border-color: $color-info; + background-color: $color-info; +}
--- a/client/src/components/map/layers.js Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/components/map/layers.js Thu Jun 13 11:20:17 2019 +0200 @@ -237,9 +237,9 @@ visible: true, source: new ImageSource({ preload: 1, - url: "https://service.d4d-portal.info/wms/", + url: store.state.application.config.ecdis_wms_url, crossOrigin: "anonymous", - params: { LAYERS: "d4d", VERSION: "1.1.1", TILED: true } + params: JSON.parse(store.state.application.config.ecdis_wms_params) }) }), new ImageLayer({
--- a/client/src/components/systemconfiguration/DataAccuracy.vue Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/components/systemconfiguration/DataAccuracy.vue Thu Jun 13 11:20:17 2019 +0200 @@ -330,7 +330,17 @@ }, methods: { submit() { - this.$store.commit("application/config", this.config); + this.$store.dispatch("application/saveConfig", { + bn_revtime_multiplier: this.config.bn_revtime_multiplier, + gm_latest_hours: this.config.gm_latest_hours, + gm_min_values_14d: this.config.gm_min_values_14d, + gm_forecast_offset_24h: this.config.gm_forecast_offset_24h, + gm_forecast_offset_72h: this.config.gm_forecast_offset_72h, + gm_forecast_vs_reality_nsc_24h: this.config + .gm_forecast_vs_reality_nsc_24h, + gm_forecast_vs_reality_nsc_72h: this.config + .gm_forecast_vs_reality_nsc_72h + }); } } };
--- a/client/src/components/systemconfiguration/MapLayers.vue Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/components/systemconfiguration/MapLayers.vue Thu Jun 13 11:20:17 2019 +0200 @@ -5,14 +5,50 @@ <div class="row"> <div class="col-sm-6"> <div class="form-group"> - <label for="ecdis-url" class="font-weight-bold">ECDIS URL</label> + <label for="ecdis-url" class="font-weight-bold"> + ECDIS WMS URL + </label> <input type="url" class="form-control" placeholder="https://..." - v-model="config.ecdis_url" + @input="lookupWMSCapabilities()" + v-model="config.ecdis_wms_url" /> </div> + <label for="ecdis-layers"> + <translate>Layers</translate> + <transition name="fade" + ><font-awesome-icon + icon="spinner" + spin + v-if="availableWMSLayersLoading" + class="ml-2" + /></transition> + </label> + <div class="container-fluid"> + <div class="row"> + <div + class="custom-control custom-checkbox col-sm-4" + v-for="layer in availableWMSLayers" + :key="'layer-' + layer" + > + <input + type="checkbox" + class="custom-control-input" + v-model="selectedWMSLayers" + :id="'layer-' + layer" + :value="layer" + /> + <label + class="custom-control-label text-break" + :for="'layer-' + layer" + > + {{ layer }} + </label> + </div> + </div> + </div> </div> </div> </div> @@ -39,15 +75,84 @@ * Markus Kottländer <markus@intevation.de> */ import { mapState } from "vuex"; +import WMSCapabilities from "ol/format/WMSCapabilities"; +import { HTTP } from "@/lib/http"; + +const WMSCapabilitiesParser = new WMSCapabilities(); export default { + data() { + return { + selectedWMSLayers: [], + availableWMSLayers: [], + availableWMSLayersLoading: false, + lookupWMSCapabilitiesTimeout: null, + wmsVersion: "" + }; + }, computed: { ...mapState("application", ["config"]) }, methods: { + lookupWMSCapabilities() { + if (this.lookupWMSCapabilitiesTimeout) { + clearTimeout(this.lookupWMSCapabilitiesTimeout); + } + this.lookupWMSCapabilitiesTimeout = setTimeout(() => { + let url; + try { + let urlParts = new URL(this.config.ecdis_wms_url); + url = + urlParts.protocol + + "//" + + urlParts.host + + urlParts.pathname.trim("/") + + "/"; + } catch (e) { + url = this.config.ecdis_wms_url; + } + this.availableWMSLayersLoading = true; + HTTP.get(url + "?request=GetCapabilities&service=WMS") + .then(response => { + let capabilities = WMSCapabilitiesParser.read(response.data); + this.wmsVersion = capabilities.version; + this.availableWMSLayers = []; + this.getLayersRecursive(capabilities.Capability.Layer.Layer); + }) + .catch(() => { + this.availableWMSLayers = []; + }) + .finally(() => (this.availableWMSLayersLoading = false)); + }, 500); + }, + getLayersRecursive(layers) { + layers.forEach(l => { + if (l.hasOwnProperty("Layer")) { + this.getLayersRecursive(l.Layer); + } else { + this.availableWMSLayers.push(l.Name); + } + }); + }, submit() { - this.$store.commit("application/config", this.config); + this.$store.dispatch("application/saveConfig", { + ecdis_wms_url: this.config.ecdis_wms_url, + ecdis_wms_params: JSON.stringify({ + LAYERS: this.selectedWMSLayers + .filter(l => this.availableWMSLayers.find(al => al === l)) + .join(","), + VERSION: this.wmsVersion, + TILED: true + }) + }); } + }, + mounted() { + let ecdisWmsParams = JSON.parse(this.config.ecdis_wms_params); + if (ecdisWmsParams.LAYERS) { + this.selectedWMSLayers = ecdisWmsParams.LAYERS.split(","); + } + this.lookupWMSCapabilities(); } }; </script>
--- a/client/src/components/systemconfiguration/MorphologyClassbreaks.vue Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/components/systemconfiguration/MorphologyClassbreaks.vue Thu Jun 13 11:20:17 2019 +0200 @@ -8,11 +8,11 @@ <div class="d-flex flex-wrap"> <div class="input-group mb-3 mr-2 classbreak" - v-for="(value, i) in config.morphology_classbreaks" + v-for="(value, i) in morphologyClassbreaks" :key="i" > <input - v-model="config.morphology_classbreaks[i]" + v-model="morphologyClassbreaks[i]" type="number" min="0" step="0.1" @@ -22,7 +22,7 @@ <button class="btn btn-sm btn-outline-secondary" type="button" - @click="config.morphology_classbreaks.splice(i, 1)" + @click="morphologyClassbreaks.splice(i, 1)" > <font-awesome-icon icon="times" /> </button> @@ -31,11 +31,9 @@ <button class="btn btn-sm btn-success mb-3" @click=" - config.morphology_classbreaks.push( - config.morphology_classbreaks.length - ? config.morphology_classbreaks[ - config.morphology_classbreaks.length - 1 - ] + morphologyClassbreaks.push( + morphologyClassbreaks.length + ? morphologyClassbreaks[morphologyClassbreaks.length - 1] : 1 ) " @@ -51,11 +49,11 @@ <div class="d-flex flex-wrap"> <div class="input-group mb-3 mr-2 classbreak" - v-for="(value, i) in config.morphology_classbreaks_compare" + v-for="(value, i) in morphologyClassbreaksCompare" :key="i" > <input - v-model="config.morphology_classbreaks_compare[i]" + v-model="morphologyClassbreaksCompare[i]" type="number" step="0.1" class="form-control form-control-sm" @@ -64,7 +62,7 @@ <button class="btn btn-sm btn-outline-secondary" type="button" - @click="config.morphology_classbreaks_compare.splice(i, 1)" + @click="morphologyClassbreaksCompare.splice(i, 1)" > <font-awesome-icon icon="times" /> </button> @@ -73,10 +71,10 @@ <button class="btn btn-sm btn-success mb-3" @click=" - config.morphology_classbreaks_compare.push( - config.morphology_classbreaks_compare.length - ? config.morphology_classbreaks_compare[ - config.morphology_classbreaks_compare.length - 1 + morphologyClassbreaksCompare.push( + morphologyClassbreaksCompare.length + ? morphologyClassbreaksCompare[ + morphologyClassbreaksCompare.length - 1 ] : 1 ) @@ -122,13 +120,32 @@ import { mapState } from "vuex"; export default { + data() { + return { + morphologyClassbreaks: [], + morphologyClassbreaksCompare: [] + }; + }, computed: { ...mapState("application", ["config"]) }, methods: { submit() { - this.$store.commit("application/config", this.config); + this.$store.dispatch("application/saveConfig", { + morphology_classbreaks: this.morphologyClassbreaks.join(","), + morphology_classbreaks_compare: this.morphologyClassbreaksCompare.join( + "," + ) + }); } + }, + mounted() { + this.morphologyClassbreaks = this.config.morphology_classbreaks + .split(",") + .map(n => Number(n)); + this.morphologyClassbreaksCompare = this.config.morphology_classbreaks_compare + .split(",") + .map(n => Number(n)); } }; </script>
--- a/client/src/components/systemconfiguration/Systemconfiguration.vue Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/components/systemconfiguration/Systemconfiguration.vue Thu Jun 13 11:20:17 2019 +0200 @@ -6,6 +6,9 @@ <div class="text-left flex-fill" style="overflow: auto"> <PDFTemplates /> <ColorSettings v-if="isSysAdmin" /> + <MapLayers v-if="isSysAdmin" /> + <DataAccuracy v-if="isSysAdmin" /> + <MorphologyClassbreaks v-if="isSysAdmin" /> </div> <!-- card-body --> </div> @@ -35,7 +38,10 @@ components: { Spacer: () => import("../Spacer"), PDFTemplates: () => import("./PDFTemplates"), - ColorSettings: () => import("./ColorSettings") + ColorSettings: () => import("./ColorSettings"), + MapLayers: () => import("./MapLayers"), + DataAccuracy: () => import("./DataAccuracy"), + MorphologyClassbreaks: () => import("./MorphologyClassbreaks") }, computed: { ...mapGetters("user", ["isSysAdmin"]),
--- a/client/src/store/application.js Wed Jun 12 18:26:26 2019 +0200 +++ b/client/src/store/application.js Thu Jun 13 11:20:17 2019 +0200 @@ -14,6 +14,8 @@ * Bernhard E. Reiter <bernhard.reiter@intevation.de> */ +import { HTTP } from "@/lib/http"; +import { displayError, displayInfo } from "@/lib/errors"; import { version } from "../../package.json"; // initial state @@ -140,85 +142,32 @@ } }, actions: { - loadConfig({ commit, state }) { - if (!Object.keys(state.config).length) { - setTimeout(() => { - commit("config", { - ecdis_url: "https://service.d4d-portal.info/wms/", - bn_revtime_multiplier: 1.5, - gm_min_values_14d: 1124, - gm_latest_hours: 24, - gm_forecast_offset_24h: 15, - gm_forecast_offset_72h: 15, - gm_forecast_vs_reality_nsc_24h: -12.5, - gm_forecast_vs_reality_nsc_72h: -12.5, - morphology_classbreaks: [ - 1, - 1.5, - 1.7, - 1.9, - 2.1, - 2.3, - 2.5, - 2.7, - 2.9, - 3.1, - 3.3, - 3.5, - 4.0, - 4.5, - 5, - 5.5, - 6, - 6.5, - 7 - ], - morphology_classbreaks_compare: [ - -2, - -1.9, - -1.8, - -1.7, - -1.6, - -1.5, - -1.4, - -1.3, - -1.2, - -1.1, - -1, - -0.9, - -0.8, - -0.7, - -0.6, - -0.5, - -0.4, - -0.3, - -0.2, - -0.1, - 0, - 0.1, - 0.2, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - 0.8, - 0.9, - 1, - 1.1, - 1.2, - 1.3, - 1.4, - 1.5, - 1.6, - 1.7, - 1.8, - 1.9, - 2 - ] + loadConfig({ commit }) { + HTTP.get("/system/settings", { + headers: { "X-Gemma-Auth": localStorage.getItem("token") } + }).then(response => { + commit("config", response.data); + }); + }, + saveConfig(context, config) { + HTTP.put("/system/settings", config, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "application/json" + } + }) + .then(() => { + displayInfo({ + message: "Configuration saved!" }); - }, 1000); - } + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); } } };
--- a/pkg/controllers/routes.go Wed Jun 12 18:26:26 2019 +0200 +++ b/pkg/controllers/routes.go Thu Jun 13 11:20:17 2019 +0200 @@ -79,6 +79,15 @@ NoConn: true, })).Methods(http.MethodGet) + api.Handle("/system/settings", any(&JSONHandler{ + Handle: getSystemSettings, + })).Methods(http.MethodGet) + + api.Handle("/system/settings", any(&JSONHandler{ + Input: func(*http.Request) interface{} { return &map[string]string{} }, + Handle: setSystemSettings, + })).Methods(http.MethodPut) + api.Handle("/system/style/{feature}/{attr}", any(&JSONHandler{ Handle: getFeatureStyle, })).Methods(http.MethodGet)
--- a/pkg/controllers/system.go Wed Jun 12 18:26:26 2019 +0200 +++ b/pkg/controllers/system.go Thu Jun 13 11:20:17 2019 +0200 @@ -21,9 +21,10 @@ "net/http" "strings" + "github.com/gorilla/mux" + "gemma.intevation.de/gemma/pkg/config" "gemma.intevation.de/gemma/pkg/models" - "github.com/gorilla/mux" ) const ( @@ -33,6 +34,15 @@ setFeatureColourSQL = `UPDATE systemconf.feature_colours SET (r, g, b, a) = ($3, $4, $5, $6) WHERE feature_name = $1 AND style_attr = $2` + + getSettingsSQL = ` +SELECT config_key, config_val +FROM sys_admin.system_config` + + updateSettingSQL = ` +INSERT INTO sys_admin.system_config (config_key, config_val) +VALUES ($1, $2) +ON CONFLICT (config_key) DO UPDATE SET config_val = $2` ) // System status end points @@ -100,6 +110,75 @@ return } +func getSystemSettings( + _ interface{}, + req *http.Request, + conn *sql.Conn, +) (jr JSONResult, err error) { + + var rows *sql.Rows + if rows, err = conn.QueryContext(req.Context(), getSettingsSQL); err != nil { + return + } + defer rows.Close() + + settings := map[string]string{} + + for rows.Next() { + var key, val string + if err = rows.Scan(&key, &val); err != nil { + return + } + settings[key] = val + } + if err = rows.Err(); err != nil { + return + } + + jr = JSONResult{Result: settings} + return +} + +func setSystemSettings( + input interface{}, + req *http.Request, + conn *sql.Conn, +) (jr JSONResult, err error) { + + settings := input.(*map[string]string) + + ctx := req.Context() + var tx *sql.Tx + if tx, err = conn.BeginTx(ctx, nil); err != nil { + return + } + defer tx.Rollback() + + var setStmt *sql.Stmt + if setStmt, err = tx.PrepareContext(ctx, updateSettingSQL); err != nil { + return + } + defer setStmt.Close() + + for key, value := range *settings { + if _, err = setStmt.ExecContext(ctx, key, value); err != nil { + return + } + } + + if err = tx.Commit(); err != nil { + return + } + + jr = JSONResult{ + Code: http.StatusCreated, + Result: struct { + Result string `json:"result"` + }{"success"}, + } + return +} + // Map/Feature style end points func getFeatureStyle(
--- a/schema/auth.sql Wed Jun 12 18:26:26 2019 +0200 +++ b/schema/auth.sql Thu Jun 13 11:20:17 2019 +0200 @@ -25,10 +25,11 @@ -- -- Privileges for waterway_user -- -GRANT USAGE ON SCHEMA public, users, waterway, systemconf, caching TO waterway_user; +GRANT USAGE ON SCHEMA public, users, waterway, systemconf, sys_admin, caching TO waterway_user; GRANT SELECT ON ALL TABLES IN SCHEMA public, users, waterway TO waterway_user; GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA caching TO waterway_user; GRANT SELECT ON systemconf.feature_colours TO waterway_user; +GRANT SELECT ON sys_admin.system_config TO waterway_user; GRANT UPDATE (pw, map_extent, email_address) ON users.list_users TO waterway_user; @@ -65,7 +66,7 @@ ON users.list_users, users.responsibility_areas TO sys_admin; GRANT USAGE ON SCHEMA sys_admin TO sys_admin; GRANT SELECT ON ALL TABLES IN SCHEMA sys_admin TO sys_admin; -GRANT UPDATE ON sys_admin.system_config TO sys_admin; +GRANT INSERT, UPDATE ON sys_admin.system_config TO sys_admin; GRANT UPDATE ON systemconf.feature_colours TO sys_admin; GRANT UPDATE ON sys_admin.published_services TO sys_admin; GRANT INSERT, DELETE ON sys_admin.password_reset_requests TO sys_admin;
--- a/schema/default_sysconfig.sql Wed Jun 12 18:26:26 2019 +0200 +++ b/schema/default_sysconfig.sql Thu Jun 13 11:20:17 2019 +0200 @@ -23,4 +23,19 @@ INSERT INTO systemconf.feature_colours VALUES ('Bottlenecks', 'stroke', 250, 40, 255, 1); INSERT INTO systemconf.feature_colours VALUES ('Bottlenecks', 'fill', 255, 37, 196, 0.14); +-- +-- Settings +-- +INSERT INTO sys_admin.system_config VALUES ('ecdis_wms_url', 'https://service.d4d-portal.info/wms/'); +INSERT INTO sys_admin.system_config VALUES ('ecdis_wms_params', '{"LAYERS": "d4d", "VERSION": "1.1.1", "TILED": true}'); +INSERT INTO sys_admin.system_config VALUES ('bn_revtime_multiplier', 1.5); +INSERT INTO sys_admin.system_config VALUES ('gm_min_values_14d', 1224); +INSERT INTO sys_admin.system_config VALUES ('gm_latest_hours', 24); +INSERT INTO sys_admin.system_config VALUES ('gm_forecast_offset_24h', 15); +INSERT INTO sys_admin.system_config VALUES ('gm_forecast_offset_72h', 15); +INSERT INTO sys_admin.system_config VALUES ('gm_forecast_vs_reality_nsc_24h', -12.5); +INSERT INTO sys_admin.system_config VALUES ('gm_forecast_vs_reality_nsc_72h', -12.5); +INSERT INTO sys_admin.system_config VALUES ('morphology_classbreaks', '1,1.5,1.7,1.9,2.1,2.3,2.5,2.7,2.9,3.1,3.3,3.5,4.0,4.5,5,5.5,6,6.5,7'); +INSERT INTO sys_admin.system_config VALUES ('morphology_classbreaks_compare', '-2,-1.9,-1.8,-1.7,-1.6,-1.5,-1.4,-1.3,-1.2,-1.1,-1,-0.9,-0.8,-0.7,-0.6,-0.5,-0.4,-0.3,-0.2,-0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2'); + COMMIT;