Mercurial > gemma
changeset 1879:9a2fbeaabd52 dev-pdf-generation
merging in from branch default
author | Bernhard Reiter <bernhard@intevation.de> |
---|---|
date | Tue, 15 Jan 2019 10:07:10 +0100 |
parents | f030182f82f1 (current diff) 70573eea890e (diff) |
children | 2b4de58a9031 |
files | client/package.json client/src/components/Pdftool.vue client/src/locale/bg_BG/LC_MESSAGES/app.po client/src/locale/de_AT/LC_MESSAGES/app.po client/src/locale/en_GB/LC_MESSAGES/app.po client/src/locale/hr_HR/LC_MESSAGES/app.po client/src/locale/hu_HU/LC_MESSAGES/app.po client/src/locale/ro_RO/LC_MESSAGES/app.po client/src/locale/sk_SK/LC_MESSAGES/app.po client/src/locale/translations.json client/src/store/map.js pkg/controllers/bnimports.go pkg/controllers/gmimports.go pkg/imports/fwsched.go pkg/imports/polygon.go |
diffstat | 95 files changed, 11062 insertions(+), 2025 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgchurn Tue Jan 15 09:54:46 2019 +0100 +++ b/.hgchurn Tue Jan 15 10:07:10 2019 +0100 @@ -1,1 +1,2 @@ teichmann@intevation.de = sascha.teichmann@intevation.de +bernhard.reiter@intevation.de = bernhard@intevation.de
--- a/client/Makefile Tue Jan 15 09:54:46 2019 +0100 +++ b/client/Makefile Tue Jan 15 10:07:10 2019 +0100 @@ -1,20 +1,20 @@ -# This is Free Software under GNU Affero General Public License v >= 3.0 -# without warranty, see README.md and license for details. -# -# SPDX-License-Identifier: AGPL-3.0-or-later -# License-Filename: LICENSES/AGPL-3.0.txt +# This is free software under MIT license taken from: +# https://github.com/Polyconseil/vue-gettext/blob/master/Makefile # -# Copyright (C) 2018 by via donau -# – Österreichische Wasserstraßen-Gesellschaft mbH -# Software engineering by Intevation GmbH -# -# Author(s): -# * Thomas Junk <thomas.junk@intevation.de> +# following changes were made due to requirments: +# * Add additional locales for the app +# * Add ability to run the translations Makefile with several users on a multi-user system +# * prevent clean from being called accidently if ´make´ is executed without parameters # On OSX the PATH variable isn't exported unless "SHELL" is also set, see: http://stackoverflow.com/a/25506676 SHELL = /bin/bash NODE_BINDIR = ./node_modules/.bin export PATH := $(NODE_BINDIR):$(PATH) +LOGNAME := $(shell logname) + +# adding the name of the user's login name to the template file, so that +# on a multi-user system several users can run this without interference +TEMPLATE_POT := /tmp/template-$(LOGNAME).pot # Where to write the files generated by this makefile. OUTPUT_DIR = src @@ -35,15 +35,15 @@ @echo choose a target from: clean makemessages translations clean: - rm -f /tmp/template.pot $(OUTPUT_DIR)/locale/translations.json + rm -f $(TEMPLATE_POT) $(OUTPUT_DIR)/locale/translations.json -makemessages: /tmp/template.pot +makemessages: $(TEMPLATE_POT) translations: ./$(OUTPUT_DIR)/locale/translations.json # Create a main .pot template, then generate .po files for each available language. # Thanx to Systematic: https://github.com/Polyconseil/systematic/blob/866d5a/mk/main.mk#L167-L183 -/tmp/template.pot: $(GETTEXT_HTML_SOURCES) +$(TEMPLATE_POT): $(GETTEXT_HTML_SOURCES) # `dir` is a Makefile built-in expansion function which extracts the directory-part of `$@`. # `$@` is a Makefile automatic variable: the file name of the target of the rule. # => `mkdir -p /tmp/` @@ -66,6 +66,6 @@ msgattrib --no-wrap --no-obsolete -o $$PO_FILE $$PO_FILE; \ done; -$(OUTPUT_DIR)/locale/translations.json: clean /tmp/template.pot +$(OUTPUT_DIR)/locale/translations.json: clean $(TEMPLATE_POT) mkdir -p $(OUTPUT_DIR) gettext-compile --output $@ $(LOCALE_FILES)
--- a/client/README.md Tue Jan 15 09:54:46 2019 +0100 +++ b/client/README.md Tue Jan 15 10:07:10 2019 +0100 @@ -3,6 +3,8 @@ * Install dependencies + * Install (`xgettext`) tool (e.g. for Debian xgettext (GNU gettext-tools) 0.19.8.1) + * Javascript `yarn install` @@ -21,10 +23,6 @@ Run webpack-dev-server with `yarn serve` - * If you also want to start the backend, try - - `yarn run:both` - * Build `yarn build` builds the production ready assets to `web` folder. @@ -34,7 +32,7 @@ * Translation * Extract Messages via `make makemessages` - * Translations are converted via `make translations` + * Translations are converted via `make translations` which is executed automatically with yarn serve/build For more information see [developer documentation](./docs/developer.md)
--- a/client/docs/developer.md Tue Jan 15 09:54:46 2019 +0100 +++ b/client/docs/developer.md Tue Jan 15 10:07:10 2019 +0100 @@ -75,11 +75,8 @@ - SK - HU - HR -- RS -- BiH - BG - RO -- UA ## Our preferred translation cycle is as follows: @@ -113,7 +110,8 @@ In order to extract the messages from the templates call `make makemessages`. After that you have the `.po`-file which could be translated. -After translation, you have to call `make translations` to generate the `translations.json` + +After translation, `make translations` has to be called to generate the `translations.json`, that is being done automatically via yarn serve/build. which is consumed by `vue-gettext`. The workflow is as follows:
--- a/client/package.json Tue Jan 15 09:54:46 2019 +0100 +++ b/client/package.json Tue Jan 15 10:07:10 2019 +0100 @@ -8,9 +8,9 @@ }, "private": true, "scripts": { - "run:both": "concurrently \"../cmd/gemma/gemma\" \"vue-cli-service serve\"", - "serve": "VUE_APP_HGREV=$(hg log -r . --template \"{data|shortdate}-{node|short}\") vue-cli-service serve", - "build": "VUE_APP_HGREV=$(hg log -r . --template \"{data|shortdate}-{node|short}\") vue-cli-service build", + "serve": "make translations && VUE_APP_HGREV=$(hg log -r . --template \"{data|shortdate}-{node|short}\") vue-cli-service serve", + "build": "make translations && VUE_APP_HGREV=$(hg log -r . --template \"{data|shortdate}-{node|short}\") vue-cli-service build", + "analyze": "ANALYZE=true vue-cli-service serve", "lint": "vue-cli-service lint", "test:unit": "vue-cli-service test:unit", "test:e2e": "vue-cli-service test:e2e" @@ -65,7 +65,7 @@ "node-sass": "^4.10.0", "pretty-quick": "^1.8.0", "sass-loader": "^7.0.1", - "vue-gettext": "^2.1.1", + "vue-gettext": "^2.1.2", "vue-template-compiler": "^2.5.17", "webpack-bundle-analyzer": "^3.0.3" },
--- a/client/src/assets/application.scss Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/assets/application.scss Tue Jan 15 10:07:10 2019 +0100 @@ -19,7 +19,7 @@ $icon-width: 2rem; $large-offset: 2rem; $offset: 1rem; -$sidebar-width: 20rem; +$sidebar-width: 21rem; $slight-transparent: 0.96; $small-offset: 0.5rem; $smaller: 0.9rem;
--- a/client/src/components/Contextbox.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Contextbox.vue Tue Jan 15 10:07:10 2019 +0100 @@ -5,6 +5,7 @@ </div> <Bottlenecks v-if="contextBoxContent === 'bottlenecks'"></Bottlenecks> <Staging v-if="contextBoxContent === 'staging'"></Staging> + <Stretches v-if="contextBoxContent === 'stretches'"></Stretches> </div> </template> @@ -28,7 +29,8 @@ name: "contextbox", components: { Bottlenecks: () => import("./Bottlenecks"), - Staging: () => import("./staging/Staging.vue") + Staging: () => import("./staging/Staging.vue"), + Stretches: () => import("./ImportStretches.vue") }, computed: { ...mapState("application", [
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/ImportApprovedGaugeMeasurement.vue Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,119 @@ +<template> + <div class="d-flex flex-row"> + <Spacer></Spacer> + <div class="card sysconfig mt-3 shadow-xs w-100 h-100 mr-3"> + <h6 + class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center" + > + <font-awesome-icon + icon="cloud-upload-alt" + class="mr-2" + ></font-awesome-icon> + <translate class="headline">Import approved gaugemeasurements</translate> + </h6> + <div class="card-body stretches-card"> + <div class="w-95 ml-auto mr-auto mt-4 mb-4"> + <div class="d-flex flex-row input-group mb-4"> + <div class="flex-column w-100"> + <div class="custom-file"> + <input + accept=".csv" + type="file" + @change="fileSelected" + class="custom-file-input" + id="uploadFile" + /> + <label class="custom-file-label" for="uploadFile"> + {{ uploadLabel }} + </label> + </div> + </div> + </div> + <div class="buttons text-right"> + <button + :disabled="disableUploadButton" + @click="submit" + class="btn btn-info mt-4" + type="button" + > + <font-awesome-icon + class="fa-fw mr-2" + fixed-width + icon="play" + ></font-awesome-icon> + <translate>Trigger import</translate> + </button> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> +/* This is Free Software under GNU Affero General Public License v >= 3.0 + * without warranty, see README.md and license for details. + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * License-Filename: LICENSES/AGPL-3.0.txt + * + * Copyright (C) 2018 by via donau + * – Österreichische Wasserstraßen-Gesellschaft mbH + * Software engineering by Intevation GmbH + * + * Author(s): + * Thomas Junk <thomas.junk@intevation.de> + */ + +import { HTTP } from "@/lib/http"; +import { displayError, displayInfo } from "@/lib/errors.js"; + +export default { + name: "importapprovedgaugemeasurements", + data() { + return { + disableUploadButton: false, + uploadLabel: this.$gettext("choose file to upload"), + uploadFile: null + }; + }, + methods: { + fileSelected(e) { + const files = e.target.files || e.dataTransfer.files; + if (!files) return; + this.uploadLabel = files[0].name; + this.uploadFile = files[0]; + }, + submit() { + if (!this.uploadFile) return; + let formData = new FormData(); + formData.append("approvedgm", this.uploadFile); + HTTP.post("/imports/approvedgm", formData, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-Type": "multipart/form-data" + } + }) + .then(() => { + displayInfo({ + title: this.$gettext("Import"), + message: this.$gettext("Starting import of Approved Gauge Measurements") + }); + this.initialState(); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + } + }, + components: { + Spacer: () => import("./Spacer") + } +}; +</script> + +<style lang="scss" scoped></style>
--- a/client/src/components/ImportStretches.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/ImportStretches.vue Tue Jan 15 10:07:10 2019 +0100 @@ -1,44 +1,9 @@ <template> - <div class="d-flex flex-row"> - <Spacer></Spacer> - <div class="card sysconfig mt-3 shadow-xs w-100 h-100 mr-3"> - <h6 - class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center" - > - <font-awesome-icon - icon="cloud-upload-alt" - class="mr-2" - ></font-awesome-icon> - <translate class="headline">Import streches</translate> - </h6> - <div class="card-body stretches-card"> - <div class="w-95 ml-auto mr-auto mt-4 mb-4"> - <div class="d-flex flex-row input-group mb-4"> - <div class="flex-column w-100"> - <div class="flex-row text-left"> - <small class="text-muted"> <translate>URL</translate> </small> - </div> - <div><input class="form-control" type="url" /></div> - </div> - </div> - <div class="buttons text-right"> - <button - :disabled="disableUploadButton" - @click="submit" - class="btn btn-info mt-4" - type="button" - > - <font-awesome-icon - class="fa-fw mr-2" - fixed-width - icon="play" - ></font-awesome-icon> - <translate>Trigger import</translate> - </button> - </div> - </div> - </div> - </div> + <div> + <h6 class="mb-0 py-2 px-3 border-bottom d-flex align-items-center"> + <font-awesome-icon icon="road" class="mr-2"></font-awesome-icon> + <translate>Define section and stretches</translate> + </h6> </div> </template> @@ -57,29 +22,12 @@ * Thomas Junk <thomas.junk@intevation.de> */ -import Spacer from "./Spacer"; -import { displayInfo } from "@/lib/errors.js"; - export default { name: "importstretches", data() { - return { - disableUploadButton: false, - uploadLabel: "", - uploadFile: null - }; + return {}; }, - methods: { - submit() { - displayInfo({ - title: this.$gettext("Import stretches"), - message: this.$gettext("under construction") - }); - } - }, - components: { - Spacer - } + methods: {} }; </script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/ImportWaterwayProfiles.vue Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,99 @@ +<template> + <div class="d-flex flex-row"> + <Spacer></Spacer> + <div class="card sysconfig mt-3 shadow-xs w-100 h-100 mr-3"> + <h6 + class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center" + > + <font-awesome-icon + icon="cloud-upload-alt" + class="mr-2" + ></font-awesome-icon> + <translate class="headline">Import Waterwayprofiles</translate> + </h6> + <div class="card-body stretches-card"> + <div class="w-95 ml-auto mr-auto mt-4 mb-4"> + <div class="d-flex flex-row input-group mb-4"> + <div class="flex-column w-100"> + <div class="custom-file"> + <input + accept=".zip" + type="file" + @change="fileSelected" + class="custom-file-input" + id="uploadFile" + /> + <label class="custom-file-label" for="uploadFile"> + {{ uploadLabel }} + </label> + </div> + </div> + </div> + <div class="buttons text-right"> + <button + :disabled="disableUploadButton" + @click="submit" + class="btn btn-info mt-4" + type="button" + > + <font-awesome-icon + class="fa-fw mr-2" + fixed-width + icon="play" + ></font-awesome-icon> + <translate>Trigger import</translate> + </button> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> +/* This is Free Software under GNU Affero General Public License v >= 3.0 + * without warranty, see README.md and license for details. + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * License-Filename: LICENSES/AGPL-3.0.txt + * + * Copyright (C) 2018 by via donau + * – Österreichische Wasserstraßen-Gesellschaft mbH + * Software engineering by Intevation GmbH + * + * Author(s): + * Thomas Junk <thomas.junk@intevation.de> + */ + +import { displayInfo } from "@/lib/errors.js"; + +export default { + name: "importwaterwayprofiles", + data() { + return { + disableUploadButton: false, + uploadLabel: this.$gettext("choose file to upload"), + uploadFile: null + }; + }, + methods: { + fileSelected(e) { + const files = e.target.files || e.dataTransfer.files; + if (!files) return; + this.uploadLabel = files[0].name; + this.uploadFile = files[0]; + }, + submit() { + displayInfo({ + title: this.$gettext("Import"), + message: this.$gettext("under construction") + }); + } + }, + components: { + Spacer: () => import("./Spacer") + } +}; +</script> + +<style lang="scss" scoped></style>
--- a/client/src/components/Logs.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Logs.vue Tue Jan 15 10:07:10 2019 +0100 @@ -120,7 +120,6 @@ import { mapState } from "vuex"; import { HTTP } from "@/lib/http.js"; import "../../node_modules/highlight.js/styles/paraiso-dark.css"; -import Spacer from "./Spacer"; import Vue from "vue"; import VueHighlightJS from "vue-highlightjs"; Vue.use(VueHighlightJS); @@ -131,7 +130,7 @@ export default { name: "logs", components: { - Spacer + Spacer: () => import("./Spacer") }, mounted() { this.fetch("system/log/apache2/access.log", ACCESSLOG);
--- a/client/src/components/Main.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Main.vue Tue Jan 15 10:07:10 2019 +0100 @@ -20,14 +20,11 @@ * Thomas Junk <thomas.junk@intevation.de> */ -import Maplayer from "./Maplayer"; -import FairwayProfile from "./fairway/Fairwayprofile"; - export default { name: "mainview", components: { - Maplayer, - FairwayProfile + Maplayer: () => import("./Maplayer"), + FairwayProfile: () => import("./fairway/Fairwayprofile") } }; </script>
--- a/client/src/components/Maplayer.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Maplayer.vue Tue Jan 15 10:07:10 2019 +0100 @@ -262,11 +262,12 @@ layer.data.getSource().setLoader( this.buildVectorLoader( { - featurePrefix: "ws-wamos", - featureTypes: ["ienc_wtware"], - geometryName: "geom" + featureNS: "gemma", + featurePrefix: "gemma", + featureTypes: ["waterway_area"], + geometryName: "area" }, - "/external/d4d", + "/internal/wfs", layer.data.getSource() ) ); @@ -275,11 +276,12 @@ layer.data.getSource().setLoader( this.buildVectorLoader( { - featurePrefix: "ws-wamos", - featureTypes: ["ienc_wtwaxs"], - geometryName: "geom" + featureNS: "gemma", + featurePrefix: "gemma", + featureTypes: ["waterway_axis"], + geometryName: "wtwaxs" }, - "/external/d4d", + "/internal/wfs", layer.data.getSource() ) );
--- a/client/src/components/Search.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Search.vue Tue Jan 15 10:07:10 2019 +0100 @@ -12,7 +12,9 @@ 'searchgroup-collapsed': !showSearchbar, big: showContextBox && - ['bottlenecks', 'staging'].indexOf(contextBoxContent) !== -1 + ['bottlenecks', 'staging', 'stretches'].indexOf( + contextBoxContent + ) !== -1 } ]" > @@ -248,7 +250,6 @@ } ) .then(response => { - // console.log("got:", response.data); this.searchResults = response.data; }) .catch(error => { @@ -263,7 +264,6 @@ this.searchQueryIsDirty = false; }, moveToSearchResult(resultEntry) { - // DEBUG console.log("Moving to", resultEntry); if (resultEntry.geom.type == "Point") { let zoom = 11; if (resultEntry.type === "bottleneck") zoom = 17;
--- a/client/src/components/Sidebar.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Sidebar.vue Tue Jan 15 10:07:10 2019 +0100 @@ -40,6 +40,20 @@ ></font-awesome-icon> <span class="fix-trans-space" v-translate>Staging area</span> </a> + <a + :class="['secondary', { active: isActive('stretches') }]" + @click="toggleContextBox('stretches')" + href="#" + > + <font-awesome-icon + class="fa-fw mr-2" + fixed-width + icon="road" + ></font-awesome-icon> + <span class="fix-trans-space" v-translate + >Define sections and stretches</span + > + </a> <small class="text-muted pl-3"> <translate>Import</translate> </small> <hr class="m-0" /> <router-link to="/importsoundingresults"> @@ -52,15 +66,27 @@ >Import soundingresults</span > </router-link> - <router-link to="/importstretches" v-if="this.$options.IMPORTSTRETCHES"> + <router-link to="/importapprovedgaugemeasurement"> <font-awesome-icon class="fa-fw mr-2" fixed-width - icon="cloud-upload-alt" + icon="upload" ></font-awesome-icon> - <span class="fix-trans-space" v-translate>Import stretches</span> + <span class="fix-trans-space" v-translate + >Import approved gaugemeasurements</span + > </router-link> - <router-link to="importschedule" v-if="this.$options.IMPORTSCHEDULE"> + <router-link to="/importwaterwayprofiles"> + <font-awesome-icon + class="fa-fw mr-2" + fixed-width + icon="upload" + ></font-awesome-icon> + <span class="fix-trans-space" v-translate + >Import waterway profiles</span + > + </router-link> + <router-link to="importschedule"> <font-awesome-icon class="fa-fw mr-2" fixed-width @@ -72,6 +98,8 @@ <translate>Systemadministration</translate> </small> <hr class="m-0" /> + </div> + <div v-if="isSysAdmin"> <router-link to="usermanagement"> <font-awesome-icon class="fa-fw mr-2" @@ -80,8 +108,6 @@ ></font-awesome-icon> <span class="fix-trans-space" v-translate>Users</span> </router-link> - </div> - <div v-if="isSysAdmin"> <router-link to="systemconfiguration"> <font-awesome-icon class="fa-fw mr-2" @@ -98,6 +124,8 @@ ></font-awesome-icon> <span class="fix-trans-space" v-translate>Logs</span> </router-link> + </div> + <div v-if="isWaterwayAdmin"> <router-link to="importqueue"> <font-awesome-icon class="fa-fw mr-2" @@ -136,11 +164,25 @@ * Markus Kottländer <markus.kottlaender@intevation.de> */ import { mapGetters, mapState } from "vuex"; -import app from "@/main"; +import { logOff } from "@/lib/session.js"; export default { name: "sidebar", props: ["routeName"], + watch: { + $route() { + const { review, importlog } = this.$route.query; + if (review) { + this.toggleContextBox("staging"); + this.$store.commit("imports/setImportToReview", review); + } else { + this.$store.commit("imports/setImportToReview", -99); + } + if (importlog) { + this.$router.push("/importqueue/" + importlog); + } + } + }, computed: { ...mapGetters("user", ["isSysAdmin", "isWaterwayAdmin"]), ...mapState("user", ["user"]), @@ -160,17 +202,12 @@ ]; } }, - IMPORTSCHEDULE: process.env.VUE_APP_FEATURE_IMPORTSCHEDULE, - IMPORTSTRETCHES: process.env.VUE_APP_FEATURE_IMPORTSTRETCHES, methods: { logoff() { - app.$snotify.clear(); - this.$store.commit("reset"); - this.$store.commit("user/clearAuth"); - this.$router.push("/login"); + logOff(); }, toggleContextBox(context) { - this.$router.push("/"); + if (this.$route.path !== "/") this.$router.push("/"); this.$store.commit("application/showContextBox", true); this.$store.commit("application/contextBoxContent", context); this.$store.commit("application/showSearchbar", true); @@ -243,7 +280,6 @@ } .sidebarextended { - max-height: 35rem; max-width: $sidebar-width; min-width: $sidebar-width; }
--- a/client/src/components/Systemconfiguration.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/Systemconfiguration.vue Tue Jan 15 10:07:10 2019 +0100 @@ -80,7 +80,6 @@ import { HTTP } from "@/lib/http"; import { displayError } from "@/lib/errors.js"; import { mapState } from "vuex"; -import Spacer from "./Spacer"; export default { name: "systemconfiguration", @@ -95,7 +94,7 @@ components: { "chrome-picker": Chrome, "compact-picker": Compact, - Spacer + Spacer: () => import("./Spacer") }, computed: { ...mapState("application", ["showSidebar"])
--- a/client/src/components/importqueue/Importqueue.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/importqueue/Importqueue.vue Tue Jan 15 10:07:10 2019 +0100 @@ -109,14 +109,12 @@ import { displayError } from "@/lib/errors.js"; import { mapState } from "vuex"; import { HTTP } from "@/lib/http.js"; -import Importqueuedetail from "./Importqueuedetail"; -import Spacer from "@/components/Spacer"; export default { name: "importqueue", components: { - Importqueuedetail, - Spacer + Importqueuedetail: () => import("./Importqueuedetail"), + Spacer: () => import("@/components/Spacer") }, data() { return {
--- a/client/src/components/importqueue/Importqueuedetail.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/importqueue/Importqueuedetail.vue Tue Jan 15 10:07:10 2019 +0100 @@ -113,7 +113,13 @@ sortAsc: true }; }, + mounted() { + this.openSpecificDetail(); + }, watch: { + $route() { + this.openSpecificDetail(); + }, reload() { if (this.reload) { this.entries = []; @@ -122,6 +128,14 @@ } }, methods: { + openSpecificDetail() { + const { id } = this.$route.params; + if (id == this.job.id) { + this.showDetails(id); + } else { + this.show = false; + } + }, formatDate(date) { return date ? new Date(date).toLocaleDateString(locale2, {
--- a/client/src/components/importschedule/Importschedule.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/importschedule/Importschedule.vue Tue Jan 15 10:07:10 2019 +0100 @@ -11,7 +11,7 @@ </h6> <div class="card-body schedulecardbody"> <div class="card-body schedulecardbody"> - <div class="searchandfilter w-50 d-flex flex-row"> + <div class="searchandfilter mb-3 w-50 d-flex flex-row"> <div class="searchgroup input-group"> <div class="input-group-prepend"> <span class="input-group-text" id="search"> @@ -38,40 +38,62 @@ <th><translate>Email</translate></th> <th> </th> <th> </th> + <th> </th> </tr> </thead> <tbody> - <tr v-for="(schedule, index) in schedules" :key="index"> - <td></td> - <td></td> - <td></td> - <td></td> - <td></td> + <tr v-for="schedule in schedules" :key="schedule.id"> + <td>{{ schedule.id }}</td> + <td>{{ schedule.kind }}</td> + <td>{{ schedule.user }}</td> + <td>{{ schedule.cron }}</td> <td> <font-awesome-icon + v-if="schedule['send-email']" + class="fa-fw mr-2" + fixed-width + icon="check" + ></font-awesome-icon> + </td> + <td> + <font-awesome-icon + :style="activeStyle" + @click="editSchedule(schedule.id)" icon="pencil-alt" fixed-width ></font-awesome-icon> </td> <td> <font-awesome-icon - @click="deleteSchedule" + :style="activeStyle" + @click="deleteSchedule(schedule.id)" icon="trash" fixed-width ></font-awesome-icon> </td> + <td> + <font-awesome-icon + @click="triggerManualImport(schedule.id)" + class="fa-fw mr-2" + fixed-width + icon="play" + ></font-awesome-icon> + </td> </tr> </tbody> </table> <div v-else class="mt-4 small text-center py-3"> - <translate>No schedules</translate> + <translate>No scheduled imports</translate> </div> - <button - @click="newImport" - class="btn btn-info position-absolute newbutton" - > - <translate>New Import</translate> - </button> + <div class="text-right"> + <button + :disabled="importScheduleDetailVisible" + @click="newImport" + class="btn btn-info newbutton" + > + <translate>New Import</translate> + </button> + </div> </div> </div> </div> @@ -96,31 +118,96 @@ */ import { mapState } from "vuex"; -import Importscheduledetail from "./Importscheduledetail"; -import Spacer from "@/components/Spacer"; +import { HTTP } from "@/lib/http"; +import { displayInfo, displayError } from "@/lib/errors.js"; export default { name: "importschedule", components: { - Importscheduledetail, - Spacer + Importscheduledetail: () => import("./Importscheduledetail"), + Spacer: () => import("@/components/Spacer") }, data() { return { searchQuery: "" }; }, + mounted() { + this.getSchedules(); + }, methods: { + editSchedule(id) { + this.$store + .dispatch("imports/loadSchedule", id) + .then(() => { + this.$store.commit("imports/setImportScheduleDetailVisible"); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }, + triggerManualImport(id) { + HTTP.get("/imports/config/" + id + "/run", { + headers: { "X-Gemma-Auth": localStorage.getItem("token") } + }) + .then(response => { + const { id } = response.data; + displayInfo({ + title: this.$gettext("Imports"), + message: this.$gettext("Manually triggered import: #") + id + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }, + getSchedules() { + this.$store.dispatch("imports/loadSchedules").catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }, newImport() { this.$store.commit("imports/setImportScheduleDetailVisible"); }, deleteSchedule(index) { - this.$store.commit("imports/deleteSchedule", index); + if (this.importScheduleDetailVisible) return; + this.$store + .dispatch("imports/deleteSchedule", index) + .then(() => { + this.getSchedules(); + displayInfo({ + title: this.$gettext("Imports"), + message: this.$gettext("Deleted import: #") + index + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); } }, computed: { ...mapState("application", ["showSidebar"]), - ...mapState("imports", ["schedules"]), + ...mapState("imports", ["schedules", "importScheduleDetailVisible"]), + activeStyle() { + const color = this.importScheduleDetailVisible ? "#aeaeae" : "#000000"; + return { color: color }; + }, spacerStyle() { return [ "spacer ml-3", @@ -135,6 +222,14 @@ </script> <style lang="scss" scoped> +th { + border-top: 0px; +} + +.card-body { + padding-bottom: $small-offset; +} + .schedulecard { margin-right: $offset; min-height: 20rem; @@ -145,10 +240,4 @@ margin-left: auto; margin-right: auto; } - -.newbutton { - position: absolute; - bottom: $offset; - right: $offset; -} </style>
--- a/client/src/components/importschedule/Importscheduledetail.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/importschedule/Importscheduledetail.vue Tue Jan 15 10:07:10 2019 +0100 @@ -7,14 +7,14 @@ <h6 class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center" > - <translate>New import</translate> + {{ dialogLabel }} <span @click="closeDetailview" class="closebutton"> <font-awesome-icon icon="times"></font-awesome-icon> </span> </h6> <div class="card-body"> <form @submit.prevent="save" class="ml-2 mr-2"> - <div class="d-flex flex-row w-100"> + <div class="d-flex flex-row"> <div class="flex-column w-50 mr-3"> <div class="flex-row text-left"> <small class="text-muted"> @@ -25,24 +25,45 @@ <option :value="$options.IMPORTTYPES.BOTTLENECK" ><translate>Bottlenecks</translate></option > + <option :value="$options.IMPORTTYPES.WATERWAYAXIS" + ><translate>Waterway axis</translate></option + > + <option :value="$options.IMPORTTYPES.GAUGEMEASUREMENT" + ><translate>Gauge measurement</translate></option + > + <option :value="$options.IMPORTTYPES.FAIRWAYAVAILABILITY" + ><translate>Available Fairway Depths</translate></option + > + <option :value="$options.IMPORTTYPES.WATERWAYAREA" + ><translate>Waterwayarea</translate></option + > + <option :value="$options.IMPORTTYPES.FAIRWAYDIMENSIONS" + ><translate>Fairwaydimensions</translate></option + > + <option :value="$options.IMPORTTYPES.WATERWAYGAUGES" + ><translate>Waterway gauges</translate></option + > </select> </div> - <div v-if="import_" class="flex-column w-50"> + <div class="flex-column ml-4"> <div class="flex-row text-left"> <small class="text-muted"> - <translate>Importtype</translate> + <translate>Email Notification</translate> </small> </div> - <select - :disabled="fixedSource" - v-model="importSource" - class="custom-select" - id="importsource" - > - <option :value="this.$options.IMPORTSOURCES.SOAP" - ><translate>SOAP</translate></option - > - </select> + <div class="flex-flex-row text-left"> + <toggle-button + v-model="eMailNotification" + class="mt-2" + :speed="100" + :labels="{ + checked: this.$options.on, + unchecked: this.$options.off + }" + :width="60" + :height="30" + /> + </div> </div> </div> <div v-if="isURLRequired"> @@ -55,7 +76,7 @@ <input v-model="url" class="form-control" type="url" /> </div> </div> - <div class="flex-column mt-3 text-left"> + <div v-if="false" class="flex-column mt-3 text-left"> <div class="d-flex flex-row"> <small class="text-muted mr-2" ><translate>Insecure</translate> @@ -81,25 +102,135 @@ </div> </div> </div> + <div v-if="!url" class="d-flex flex-row"> + <small + ><translate class="text-danger" + >Please enter a URL</translate + ></small + > + </div> </div> - <div class="flex-column mt-3 w-100 mr-2"> - <div class="flex-row text-left"> - <small class="text-muted"> - <translate>Simple Schedule</translate> - </small> + <div v-if="isCredentialsRequired"> + <div class="d-flex flex-row"> + <div class="flex-column mt-3 mr-3 w-50"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>Username</translate> + </small> + </div> + <div class="w-100"> + <input v-model="username" class="form-control" type="text" /> + </div> + <div v-if="!username" class="d-flex flex-row"> + <small + ><translate class="text-danger" + >Please enter a Username</translate + ></small + > + </div> + </div> + <div class="flex-column mt-3 w-50"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>Password</translate> + </small> + </div> + <div class="w-100"> + <input v-model="password" class="form-control" type="text" /> + </div> + <div v-if="!password" class="d-flex flex-row"> + <small + ><translate class="text-danger" + >Please enter a Password</translate + ></small + > + </div> + </div> </div> - <div class="flex-flex-row text-left"> - <toggle-button - v-model="easyCron" - class="mt-2" - :speed="100" - :labels="{ - checked: this.$options.on, - unchecked: this.$options.off - }" - :width="60" - :height="30" - /> + </div> + <div v-if="isFeatureTypeRequired"> + <div class="d-flex flex-row"> + <div class="flex-column mt-3 mr-3 w-50"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>Featuretype</translate> + </small> + </div> + <div class="w-100"> + <input + v-model="featureType" + class="form-control" + type="text" + /> + </div> + <div v-if="!featureType" class="d-flex flex-row"> + <small + ><translate class="text-danger" + >Please enter a Featuretype</translate + ></small + > + </div> + </div> + <div class="flex-column mt-3 w-50"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>SortBy</translate> + </small> + </div> + <div class="w-100"> + <input v-model="sortBy" class="form-control" type="text" /> + </div> + <div v-if="!sortBy" class="d-flex flex-row"> + <small + ><translate class="text-danger" + >Please enter SortBy</translate + ></small + > + </div> + </div> + </div> + </div> + <div class="d-flex flex-row"> + <div class="flex-column mt-3 mr-4"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>Scheduled</translate>? + </small> + </div> + <div class="flex-flex-row text-left"> + <toggle-button + v-model="scheduled" + class="mt-2" + :speed="100" + :labels="{ + checked: this.$options.on, + unchecked: this.$options.off + }" + :width="60" + :height="30" + /> + </div> + </div> + <div class="flex-column mt-3 mr-2"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>Simple Schedule</translate> + </small> + </div> + <div class="flex-flex-row text-left"> + <toggle-button + :disabled="!scheduled" + v-model="easyCron" + class="mt-2" + :speed="100" + :labels="{ + checked: this.$options.on, + unchecked: this.$options.off + }" + :width="60" + :height="30" + /> + </div> </div> </div> <div class="flex-column w-100 mr-2"> @@ -109,7 +240,10 @@ </small> </div> <div v-if="easyCron" class="text-left w-50"> - <select v-model="simple" class="form-control" + <select + :disabled="!scheduled" + v-model="simple" + class="form-control" ><option value="weekly"><translate>Weekly</translate></option> <option value="monthly"><translate>Monthly</translate></option> </select> @@ -118,6 +252,7 @@ <div class="d-flex flex-row"> <h4 class="mt-auto mb-auto mr-2">{{ $options.EVERY }}</h4> <select + :disabled="!scheduled" style="width: 130px;" v-model="cronMode" class="form-control" @@ -133,6 +268,7 @@ <div v-if="cronMode == 'hour'" class="ml-1 d-flex flex-row"> <h4 class="mt-auto mb-auto">{{ $options.ON }}</h4> <input + :disabled="!scheduled" v-model="minutes" class="cronfield ml-1 mr-1 form-control" type="number" @@ -142,11 +278,13 @@ <div v-if="cronMode == 'day'" class="ml-1 d-flex flex-row"> <h4 class="mt-auto mb-auto">{{ $options.AT }}</h4> <input + :disabled="!scheduled" v-model="hour" class="cronfield ml-1 mr-1 form-control" type="number" /> <input + :disabled="!scheduled" v-model="minutes" class="cronfield ml-1 mr-1 form-control" type="number" @@ -155,7 +293,11 @@ </div> <div v-if="cronMode == 'week'" class="ml-1 d-flex flex-row"> <h4 class="ml-1 mr-1 mt-auto mb-auto">{{ $options.ON }}</h4> - <select v-model="day" class="form-control"> + <select + :disabled="!scheduled" + v-model="day" + class="form-control" + > <option v-for="(option, key) in $options.DAYSOFWEEK" :key="key" @@ -165,11 +307,13 @@ </select> <h4 class="ml-1 mt-auto mb-auto">{{ $options.AT }}</h4> <input + :disabled="!scheduled" v-model="hour" class="cronfield ml-1 mr-1 form-control" type="number" /> <input + :disabled="!scheduled" v-model="minutes" class="cronfield ml-1 mr-1 form-control" type="number" @@ -178,17 +322,20 @@ <div v-if="cronMode == 'month'" class="ml-1 d-flex flex-row"> <h4 class="ml-1 mt-auto mb-auto">{{ $options.ON }}</h4> <input + :disabled="!scheduled" v-model="dayOfMonth" class="cronfield ml-1 mr-1 form-control" type="number" /> <h4 class="mt-auto mb-auto">{{ $options.AT }}</h4> <input + :disabled="!scheduled" v-model="hour" class="cronfield ml-1 mr-2 form-control" type="number" /> <input + :disabled="!scheduled" v-model="minutes" class="cronfield ml-1 mr-2 form-control" type="number" @@ -198,12 +345,17 @@ <div v-if="cronMode == 'year'" class="ml-1 d-flex flex-row"> <h4 class="ml-1 mt-auto mb-auto">{{ $options.ON }}</h4> <input + :disabled="!scheduled" v-model="dayOfMonth" class="cronfield ml-1 mr-1 form-control" type="number" /> <h4 class="mt-auto mb-auto">{{ $options.OF }}</h4> - <select v-model="month" class="ml-1 mr-1 form-control"> + <select + :disabled="!scheduled" + v-model="month" + class="ml-1 mr-1 form-control" + > <option v-for="(option, key) in $options.MONTHS" :value="key" @@ -213,11 +365,13 @@ </select> <h4 class="mt-auto mb-auto">{{ $options.ON }}</h4> <input + :disabled="!scheduled" v-model="hour" class="cronfield ml-1 mr-1 form-control" type="number" /> <input + :disabled="!scheduled" v-model="minutes" class="cronfield ml-1 mr-1 form-control" type="number" @@ -228,40 +382,15 @@ <h5 class="mt-auto mb-auto mr-2"> <translate>Cronstring</translate> </h5> - <input class="form-control" :value="cronString" type="text" /> + <input + :disabled="!scheduled" + class="form-control" + v-model="cronString" + type="text" + /> </div> </div> </div> - <div class="flex-column mt-3 w-100 mr-2"> - <div class="flex-row text-left"> - <small class="text-muted"> - <translate>Email Notification</translate> - </small> - </div> - <div class="flex-flex-row text-left"> - <toggle-button - v-model="eMailNotification" - class="mt-2" - :speed="100" - :labels="{ - checked: this.$options.on, - unchecked: this.$options.off - }" - :width="60" - :height="30" - /> - </div> - </div> - <div class="flex-column w-100 mr-2"> - <div class="flex-row text-left"> - <small class="text-muted"><translate>Email</translate> </small> - </div> - <input - :disabled="!eMailNotification" - class="form-control" - type="email" - /> - </div> <button type="submit" class="shadow-sm btn btn-info submit-button"> <translate>Submit</translate> </button> @@ -285,39 +414,34 @@ </template> <script> +/* This is Free Software under GNU Affero General Public License v >= 3.0 + * without warranty, see README.md and license for details. + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * License-Filename: LICENSES/AGPL-3.0.txt + * + * Copyright (C) 2018 by via donau + * – Österreichische Wasserstraßen-Gesellschaft mbH + * Software engineering by Intevation GmbH + * + * Author(s): + * Thomas Junk <thomas.junk@intevation.de> + */ +import { + IMPORTTYPES, + IMPORTTYPEKIND, + initializeCurrentSchedule +} from "@/store/imports.js"; import { mapState } from "vuex"; import { displayInfo, displayError } from "@/lib/errors.js"; import app from "@/main.js"; -import { HTTP } from "@/lib/http.js"; export default { name: "importscheduledetail", data() { - return { - importType: null, - schedule: null, - import_: null, - importSource: null, - eMailNotification: false, - easyCron: true, - cronMode: "", - minutes: null, - month: null, - hour: null, - day: null, - dayOfMonth: null, - simple: null, - url: null, - insecure: false, - triggerActive: true - }; + return initializeCurrentSchedule(); }, - IMPORTTYPES: { - BOTTLENECK: "BOTTLENECK" - }, - IMPORTSOURCES: { - SOAP: "SOAP" - }, + IMPORTTYPES: IMPORTTYPES, EVERY: app.$gettext("Every"), MINUTESPAST: app.$gettext("minutes past"), ON: app.$gettext("on"), @@ -355,7 +479,31 @@ 11: app.$gettext("November"), 12: app.$gettext("December") }, + mounted() { + this.initialize(); + }, watch: { + cronMode() { + this.cronString = this.calcCronString(); + }, + minutes() { + this.cronString = this.calcCronString(); + }, + hour() { + this.cronString = this.calcCronString(); + }, + month() { + this.cronString = this.calcCronString(); + }, + day() { + this.cronString = this.calcCronString(); + }, + dayOfMonth() { + this.cronString = this.calcCronString(); + }, + importScheduleDetailVisible() { + this.initialize(); + }, cronString() { if (this.isWeekly(this.cronString)) { this.simple = "weekly"; @@ -363,31 +511,53 @@ if (this.isMonthly(this.cronString)) { this.simple = "monthly"; } - }, - import_() { - if (this.import_ === this.$options.IMPORTTYPES.BOTTLENECK) { - this.importSource = this.$options.IMPORTSOURCES.SOAP; - } } }, computed: { - ...mapState("imports", ["importScheduleDetailVisible"]), - isURLRequired() { - if (this.import_ === this.$options.IMPORTTYPES.BOTTLENECK) return true; - return false; + ...mapState("imports", ["importScheduleDetailVisible", "currentSchedule"]), + dialogLabel() { + if (this.id) return this.$gettext("Import") + " " + this.id; + return this.$gettext("New Import"); + }, + isCredentialsRequired() { + switch (this.import_) { + case this.$options.IMPORTTYPES.WATERWAYGAUGES: + return true; + default: + return false; + } }, - cronString: { - get() { - let getValue = value => { - return this[value] ? this[value] : "*"; - }; - if (this.cronMode === "15minutes") return "*/15 * * * *"; - const min = getValue("minutes"); - const h = getValue("hour"); - const dm = getValue("dayOfMonth"); - const m = getValue("month"); - const wd = getValue("day"); - return `${min} ${h} ${dm} ${m} ${wd}`; + isURLRequired() { + switch (this.import_) { + case this.$options.IMPORTTYPES.BOTTLENECK: + case this.$options.IMPORTTYPES.WATERWAYAXIS: + case this.$options.IMPORTTYPES.GAUGEMEASUREMENT: + case this.$options.IMPORTTYPES.FAIRWAYAVAILABILITY: + case this.$options.IMPORTTYPES.WATERWAYAREA: + case this.$options.IMPORTTYPES.FAIRWAYDIMENSIONS: + case this.$options.IMPORTTYPES.WATERWAYGAUGES: + return true; + default: + return false; + } + }, + isFeatureTypeRequired() { + switch (this.import_) { + case this.$options.IMPORTTYPES.WATERWAYAXIS: + case this.$options.IMPORTTYPES.WATERWAYAREA: + case this.$options.IMPORTTYPES.FAIRWAYDIMENSIONS: + return true; + default: + return false; + } + }, + isSortbyRequired() { + switch (this.import_) { + case this.$options.IMPORTTYPES.WATERWAYAXIS: + case this.$options.IMPORTTYPES.WATERWAYAREA: + return true; + default: + return false; } }, fixedSource() { @@ -396,28 +566,50 @@ } }, methods: { - isWeekly(cron) { - return /\d{1,2} \d{1,2} \* \* \d{1}/.test(cron); + calcCronString() { + let getValue = value => { + return this[value] ? this[value] : "*"; + }; + if (this.cronMode === "15minutes") return "0 */15 * * * *"; + const min = getValue("minutes"); + const h = getValue("hour"); + const dm = getValue("dayOfMonth"); + const m = getValue("month"); + const wd = getValue("day"); + return `0 ${min} ${h} ${dm} ${m} ${wd}`; }, - isMonthly(cron) { - return /\d{1,2} \d{1,2} \d{1,2} \* \*/.test(cron); + validateBottleneckfields() { + return !!this.url; }, initialize() { - this.importType = null; - this.schedule = null; - this.import_ = null; - this.importSource = null; - this.eMailNotification = false; - this.easyCron = true; - this.cronMode = ""; - this.minutes = null; - this.month = null; - this.hour = null; - this.day = null; - this.dayOfMonth = null; - this.simple = null; - this.url = null; - this.insecure = false; + this.id = this.currentSchedule.id; + this.importType = this.currentSchedule.importType; + this.schedule = this.currentSchedule.schedule; + this.scheduled = this.currentSchedule.scheduled; + this.import_ = this.currentSchedule.import_; + this.importSource = this.currentSchedule.importSource; + this.eMailNotification = this.currentSchedule.eMailNotification; + this.easyCron = this.currentSchedule.easyCron; + this.cronMode = this.currentSchedule.cronMode; + this.minutes = this.currentSchedule.minutes; + this.month = this.currentSchedule.month; + this.hour = this.currentSchedule.hour; + this.day = this.currentSchedule.day; + this.dayOfMonth = this.currentSchedule.dayOfMonth; + this.simple = this.currentSchedule.simple; + this.url = this.currentSchedule.url; + this.insecure = this.currentSchedule.insecure; + this.cronString = this.currentSchedule.cronString; + this.featureType = this.currentSchedule.featureType; + this.sortBy = this.currentSchedule.sortBy; + this.username = this.currentSchedule.username; + this.password = this.currentSchedule.password; + }, + isWeekly(cron) { + return /0 \d{1,2} \d{1,2} \* \* \d{1}/.test(cron); + }, + isMonthly(cron) { + return /0 \d{1,2} \d{1,2} \d{1,2} \* \*/.test(cron); }, clearInputs() { this.minutes = null; @@ -428,21 +620,29 @@ }, triggerManualImport() { if (!this.triggerActive) return; + if (!this.import_) return; let data = {}; - if (this.import_ === this.$options.IMPORTTYPES.BOTTLENECK) { + if (this.isURLRequired) { if (!this.url) return; data["url"] = this.url; data["insecure"] = this.insecure; } - const importTypes = { - BOTTLENECK: "bottleneck" - }; + if (this.isFeatureTypeRequired) { + if (!this.featureType) return; + data["feature-type"] = this.featureType; + } + if (this.isSortbyRequired) { + if (!this.sortBy) return; + data["sort-by"] = this.sortBy; + } + if (this.isCredentialsRequired) { + if (!this.username || !this.password) return; + data["username"] = this.username; + data["password"] = this.password; + } this.triggerActive = false; - HTTP.post("imports/" + importTypes[this.import_], data, { - headers: { - "X-Gemma-Auth": localStorage.getItem("token") - } - }) + this.$store + .dispatch("imports/triggerImport", { type: this.import_, data }) .then(response => { const { id } = response.data; displayInfo({ @@ -453,7 +653,7 @@ .catch(error => { const { status, data } = error.response; displayError({ - title: this.gettext("Backend Error"), + title: this.$gettext("Backend Error"), message: `${status}: ${data.message || data}` }); }) @@ -462,13 +662,101 @@ }); }, save() { - displayInfo({ - title: this.$gettext("Import"), - message: this.$gettext("under construction") - }); + const addAttribute = (data, attribute) => { + if (!data["attributes"]) data.attributes = {}; + data["attributes"] = { ...data["attributes"], ...attribute }; + }; + if (!this.import_) return; + let cron = this.cronString; + if (this.easyCron) { + if (this.simple === "weekly") cron = "0 0 0 * * 0"; + if (this.simple === "monthly") cron = "0 0 0 1 * *"; + } + let data = {}; + if (this.isURLRequired) { + if (!this.url) return; + data["url"] = this.url; + addAttribute(data, { + insecure: this.insecure + "" + }); + } + if (this.isSortbyRequired) { + if (!this.sortBy) return; + addAttribute(data, { + "sort-by": this.sortBy + }); + } + if (this.isFeatureTypeRequired) { + if (!this.featureType) return; + addAttribute(data, { + "feature-type": this.featureType + }); + } + if (this.isCredentialsRequired) { + if (!this.username || !this.password) return; + addAttribute(data, { username: this.username }); + addAttribute(data, { password: this.password }); + } + if (this.scheduled) data["cron"] = cron; + data["kind"] = IMPORTTYPEKIND[this.import_]; + data["send-email"] = this.eMailNotification; + if (!this.id) { + this.$store + .dispatch("imports/saveCurrentSchedule", data) + .then(response => { + const { id } = response.data; + displayInfo({ + title: this.$gettext("Import"), + message: this.$gettext("Saved import: #") + id + }); + this.closeDetailview(); + this.$store.dispatch("imports/loadSchedules").catch(error => { + const { status, data } = error.response; + displayError({ + title: this.gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + } else { + this.$store + .dispatch("imports/updateCurrentSchedule", { + data: data, + id: this.id + }) + .then(response => { + const { id } = response.data; + displayInfo({ + title: this.$gettext("Import"), + message: this.$gettext("update import: #") + id + }); + this.closeDetailview(); + this.$store.dispatch("imports/loadSchedules").catch(error => { + const { status, data } = error.response; + displayError({ + title: this.gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + } }, closeDetailview() { - this.initialize(); + this.$store.commit("imports/clearCurrentSchedule"); this.$store.commit("imports/setImportScheduleDetailInvisible"); } },
--- a/client/src/components/layers/Layers.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/layers/Layers.vue Tue Jan 15 10:07:10 2019 +0100 @@ -44,12 +44,11 @@ * Thomas Junk <thomas.junk@intevation.de> * Markus Kottländer <markus.kottlaender@intevation.de> */ -import Layerselect from "./Layerselect"; import { mapGetters, mapState } from "vuex"; export default { name: "layers", components: { - Layerselect + Layerselect: () => import("./Layerselect") }, computed: { ...mapGetters("map", ["layersForLegend"]),
--- a/client/src/components/layers/Layerselect.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/layers/Layerselect.vue Tue Jan 15 10:07:10 2019 +0100 @@ -46,7 +46,6 @@ * Thomas Junk <thomas.junk@intevation.de> */ import { HTTP } from "@/lib/http"; -import LegendElement from "./LegendElement.vue"; export default { props: ["layername", "layerindex", "isVisible"], name: "layerselect", @@ -56,7 +55,7 @@ }; }, components: { - LegendElement + LegendElement: () => import("./LegendElement.vue") }, methods: { visibilityToggled() {
--- a/client/src/components/staging/Staging.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/staging/Staging.vue Tue Jan 15 10:07:10 2019 +0100 @@ -59,7 +59,6 @@ import { mapState } from "vuex"; import { HTTP } from "@/lib/http.js"; import { displayError, displayInfo } from "@/lib/errors.js"; -import StagingDetail from "./StagingDetail"; import { STATES } from "@/store/imports.js"; export default { @@ -67,7 +66,7 @@ return {}; }, components: { - StagingDetail + StagingDetail: () => import("./StagingDetail") }, mounted() { this.loadData();
--- a/client/src/components/staging/StagingDetail.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/staging/StagingDetail.vue Tue Jan 15 10:07:10 2019 +0100 @@ -1,5 +1,5 @@ <template> - <div class="pb-2 d-flex flex-column w-100"> + <div :class="detail"> <div class="d-flex flex-row"> <div class="mt-auto d-flex flex-row mb-auto small name text-left"> <a @@ -149,6 +149,7 @@ import { WFS } from "ol/format.js"; import { or as orFilter, equalTo as equalToFilter } from "ol/format/filter.js"; import { displayError } from "@/lib/errors.js"; +import { mapState } from "vuex"; import center from "@turf/center"; export default { @@ -164,9 +165,37 @@ }, mounted() { this.bottlenecks = []; + if (this.open) this.showDetails(); + }, + computed: { + ...mapState("imports", ["importToReview"]), + open() { + return this.importToReview == this.data.id; + }, + detail() { + return [ + "pb-2", + "pt-2", + "d-flex", + "flex-column", + "w-100", + { + highlight: this.open && this.needsApproval(this.data) + } + ]; + } + }, + watch: { + open() { + const { review } = this.$route.query; + if (review) { + this.showDetails(); + } + } }, methods: { showDetails() { + if (this.data.kind.toUpperCase() !== "BN") return; if (this.show) { this.show = false; return; @@ -267,6 +296,10 @@ </script> <style lang="scss" scoped> +.highlight { + background-color: #f9f9f9; +} + .condensed { font-stretch: condensed; }
--- a/client/src/components/usermanagement/Userdetail.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/usermanagement/Userdetail.vue Tue Jan 15 10:07:10 2019 +0100 @@ -191,7 +191,6 @@ import { HTTP } from "@/lib/http"; import { displayError } from "@/lib/errors.js"; import { mapState } from "vuex"; -import PasswordField from "./Passwordfield"; const emptyErrormessages = () => { return { @@ -228,7 +227,7 @@ export default { name: "userdetail", components: { - PasswordField + PasswordField: () => import("./Passwordfield") }, data() { return {
--- a/client/src/components/usermanagement/Usermanagement.vue Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/components/usermanagement/Usermanagement.vue Tue Jan 15 10:07:10 2019 +0100 @@ -68,6 +68,7 @@ <td>{{ user.email }}</td> <td> <font-awesome-icon + v-tooltip="roleLabel(user.role)" :icon="roleIcon(user.role)" @click="deleteUser(user.user)" ></font-awesome-icon> @@ -115,7 +116,7 @@ </div> </template> -<style scoped lang="scss"> +<style lang="scss"> @import "@/assets/tooltip.scss"; .addbutton { @@ -194,11 +195,15 @@ * Author(s): * Thomas Junk <thomas.junk@intevation.de> */ -import Userdetail from "./Userdetail"; import store from "@/store"; import { mapGetters, mapState } from "vuex"; import { displayError } from "@/lib/errors.js"; -import Spacer from "@/components/Spacer"; +import Vue from "vue"; +import { VTooltip, VPopover, VClosePopover } from "v-tooltip"; + +Vue.directive("tooltip", VTooltip); +Vue.directive("close-popover", VClosePopover); +Vue.component("v-popover", VPopover); export default { name: "userview", @@ -210,8 +215,8 @@ }; }, components: { - Userdetail, - Spacer + Userdetail: () => import("./Userdetail"), + Spacer: () => import("@/components/Spacer") }, computed: { ...mapGetters("usermanagement", ["isUserDetailsVisible"]), @@ -315,6 +320,14 @@ if (role === "sys_admin") return "star"; if (role === "waterway_admin") return ["fab", "adn"]; return "user"; + }, + roleLabel(role) { + const labels = { + sys_admin: this.$gettext("System-Administrator"), + waterway_admin: this.$gettext("Waterway Admin"), + waterway_user: this.$gettext("Waterway User") + }; + return labels[role]; } }, beforeRouteEnter(to, from, next) {
--- a/client/src/lib/errors.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/lib/errors.js Tue Jan 15 10:07:10 2019 +0100 @@ -26,8 +26,7 @@ const displayError = ({ title, message, options }) => { if (!options) options = {}; const mergedOptions = { ...displayOptions, ...options }; - app.$snotify.info(message, title, mergedOptions); - app.$snotify.error(message, title, displayOptions); + app.$snotify.error(message, title, mergedOptions); }; const displayInfo = ({ title, message, options }) => {
--- a/client/src/lib/http.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/lib/http.js Tue Jan 15 10:07:10 2019 +0100 @@ -12,11 +12,28 @@ * Thomas Junk <thomas.junk@intevation.de> */ +import { logOff } from "@/lib/session.js"; + import axios from "axios"; -export const HTTP = axios.create({ +const UNAUTHORIZED = 401; + +const HTTP = axios.create({ baseURL: process.env.VUE_APP_API_URL || "/api" /* headers: { Authorization: 'Bearer {token}' }*/ }); + +HTTP.interceptors.response.use( + response => response, + error => { + const { status } = error.response; + if (status === UNAUTHORIZED) { + logOff(); + } + return Promise.reject(error); + } +); + +export { HTTP };
--- a/client/src/lib/session.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/lib/session.js Tue Jan 15 10:07:10 2019 +0100 @@ -12,6 +12,15 @@ * Thomas Junk <thomas.junk@intevation.de> */ +import app from "@/main"; + +const logOff = () => { + app.$snotify.clear(); + app.$store.commit("reset"); + app.$store.commit("user/clearAuth"); + app.$router.push("/login"); +}; + /** * Compares whether session is current * based on the expiry information and the @@ -34,4 +43,4 @@ return timestring * 1000; } -export { sessionStillActive, toMillisFromString }; +export { logOff, sessionStillActive, toMillisFromString };
--- a/client/src/locale/bg_BG/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/bg_BG/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: gemmajs 1.99.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-12-05 12:23+0100\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,138 +17,200 @@ "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +msgid "at" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" msgstr "" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/Pdftool.vue:18 +msgid "Choose format:" +msgstr "" + +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "" @@ -160,89 +222,160 @@ msgid "Enter username" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +msgid "Gauge measurement" +msgstr "" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +msgid "Import Waterwayprofiles" +msgstr "" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "" @@ -254,65 +387,114 @@ msgid "Login failed" msgstr "" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +msgid "May" +msgstr "" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +msgid "No scheduled imports" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" msgstr "" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "" @@ -320,82 +502,102 @@ msgid "Password reset requested!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +msgid "Please enter a Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:142 +msgid "Please enter a Password" +msgstr "" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +msgid "Please enter a URL" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:125 +msgid "Please enter a Username" +msgstr "" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +msgid "Please enter SortBy" +msgstr "" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "" @@ -403,148 +605,241 @@ msgid "Request password reset!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +msgid "Saved import: #" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +msgid "Scheduled" +msgstr "" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +msgid "Simple Schedule" +msgstr "" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "" -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +msgid "System-Administrator" +msgstr "" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +msgid "Trigger import" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +msgid "update import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +msgid "Waterway axis" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:43 +msgid "Waterway gauges" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +msgid "Waterwayarea" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "" -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr ""
--- a/client/src/locale/de_AT/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/de_AT/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -8,11 +8,10 @@ msgstr "" "Project-Id-Version: wamosjs 0.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-12-11 17:08+0000\n" "Last-Translator: Sascha L. Teichmann <sascha.teichmann@intevation.de>\n" -"Language-Team: Austrian German <https://hosted.weblate.org/projects/gemma/" -"client/de_AT/>\n" +"Language-Team: Austrian German <https://hosted.weblate.org/projects/gemma/client/de_AT/>\n" "Language: de_AT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,138 +19,202 @@ "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.4-dev\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "Akzeptiert" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "Zugriffs-Protokoll" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "Benutzer hinzufügen" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +#, fuzzy +msgid "at" +msgstr "Datum" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "Autor" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "zurück zur Anmeldung" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "Server-Fehler" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "Seichtstelle" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "Flächenfüllfarbe Seichtstelle" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "Flächenumrandungsfarbe Seichtstelle" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "Seichtstellen" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "Hochladen abbrechen" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "Stationierung" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "Wählen Sie eine .zip Datei" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" +msgstr "" + +#: src/components/Pdftool.vue:18 +#, fuzzy +msgid "Choose format:" msgstr "Format wählen:" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "Vergleiche mit" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "Konfiguration" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "Bestätigen" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "Koordinaten auf die Zwischenablage kopiert!" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "Land" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "Datum" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "Tiefenreferenz" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "Herunterladen" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "Meta.json Herunterladen" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "E-Mail" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "E-Mail Adresse" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "E-Mail Benachrichtigung" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "Hinzugefügt" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "Manuelle Koordinateneingabe" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "Namen für Profilschnitt eingeben" @@ -163,89 +226,162 @@ msgid "Enter username" msgstr "Benutzername eingeben" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "Während des Speicherns der Nutzerdaten trat ein Fehler auf" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "Fehlerprotokoll" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "Fehlgeschlagen" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "Passwort vergessen" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +#, fuzzy +msgid "Gauge measurement" +msgstr "Messung" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "PDF generieren" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "Identifiziert" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "Daten-Import" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "Seichtstellenmessungen importieren" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "Seichtstellenmessungen importieren" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +#, fuzzy +msgid "Import Waterwayprofiles" +msgstr "Art des Imports" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "Importiert" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "Import-Warteschlange" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "Daten-Import" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" -msgstr "Import-Zeitplan" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" +msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "Art des Imports" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "Ungültige E-Mail" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "Ungültige Eingabe" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "Art" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "Querformat" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "Letzter Abgleich:" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "Neuste" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "Ebenen" @@ -257,65 +393,116 @@ msgid "Login failed" msgstr "Login fehlgeschlagen" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "Abmelden" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "Protokolle" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "E-Mail wurde gesendet" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "Karte" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +#, fuzzy +msgid "May" +msgstr "Karte" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "Messung" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "Name" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "Neuer Import" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "Neuer Import" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "Keine Objekte identifiziert." -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "Keine Ergebnisse." -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +#, fuzzy +msgid "No scheduled imports" msgstr "Keine Pläne" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "In neuem Fenster öffnen" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "Passwort" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "Passwort" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "Noch einmal das Passwort" @@ -323,82 +510,107 @@ msgid "Password reset requested!" msgstr "Passwort Zurücksetzung angefragt!" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein, eine Zahlenziffer und ein Sonderzeichen wie etwa $ enthalten" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "Die Passwörter stimmen nicht überein!" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "Ausstehend" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "Bitte wählen Sie ein Land aus" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "Bitte wählen Sie eine Rolle aus" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "Bitte ein Datum eingeben" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +#, fuzzy +msgid "Please enter a Featuretype" +msgstr "Bitte ein Datum eingeben" + +#: src/components/importschedule/Importscheduledetail.vue:142 +#, fuzzy +msgid "Please enter a Password" +msgstr "Bitte ein Datum eingeben" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "Bitte eine Projektion eingeben" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "Bitte ein Höhenreferenzsystem eingeben" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +#, fuzzy +msgid "Please enter a URL" +msgstr "Bitte ein Datum eingeben" + +#: src/components/importschedule/Importscheduledetail.vue:125 +#, fuzzy +msgid "Please enter a Username" +msgstr "Bitte ein Datum eingeben" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "Bitte geben Sie die Koordinaten in folgendem Format an: Lat,Lon,Lat,Lon" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +#, fuzzy +msgid "Please enter SortBy" +msgstr "Bitte ein Datum eingeben" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "Bitte eine Seichtstelle wählen" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "Bitte auswählen" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "Hochformat" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "Profil gelöscht!" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "Profil gespeichert!" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "Profile" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "Projektion" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "Aktualisieren" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "Abgelehnt" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "Passwort erneut eingeben" @@ -406,148 +618,250 @@ msgid "Request password reset!" msgstr "Passwort-Zurücksetzung anfragen!" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "Rolle" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "Gespeicherte Profile" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +#, fuzzy +msgid "Saved import: #" +msgstr "Neuer Import" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "Zeitplan" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +#, fuzzy +msgid "Scheduled" +msgstr "Zeitplan" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "Wähle Seichtstelle" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "Absenden" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "Test-E-Mail versenden" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "Überprüft durch" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +#, fuzzy +msgid "Simple Schedule" +msgstr "Zeitplan" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "Seichtstellenvermessung" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "Quelltext" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "Import-Überprüfung" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "Import-Überprüfung" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "Start" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "Import gestartet " -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "Zustand" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "Abschicken" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "Erfolg" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "Erfolgreich" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "Sys-Admin" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +#, fuzzy +msgid "System-Administrator" +msgstr "System-Administration" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "System-Administration" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "System-Konfiguation" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +#, fuzzy +msgid "Trigger import" +msgstr "Neuer Import" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "Typ" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +#, fuzzy +msgid "update import: #" +msgstr "Neuer Import" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "Hochladen" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "Benutzer" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "Benutzername" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "Benutzer" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "Waterway-Admin" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +#, fuzzy +msgid "Waterway axis" +msgstr "Waterway-Benutzer" + +#: src/components/importschedule/Importscheduledetail.vue:43 +#, fuzzy +msgid "Waterway gauges" +msgstr "Waterway-Benutzer" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "Waterway-Benutzer" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +#, fuzzy +msgid "Waterwayarea" +msgstr "Waterway-Benutzer" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "Sie können diese Koordinaten aus dem \"Gespeicherte Profile\"-Menü auswählen, um diesen Profilschnitt wieder herzustellen." -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "Länge" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr "Fläche"
--- a/client/src/locale/en_GB/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/en_GB/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: wamosjs 0.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-07-03 17:18+0200\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,138 +17,200 @@ "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +msgid "at" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" msgstr "" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/Pdftool.vue:18 +msgid "Choose format:" +msgstr "" + +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "" @@ -160,89 +222,160 @@ msgid "Enter username" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +msgid "Gauge measurement" +msgstr "" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +msgid "Import Waterwayprofiles" +msgstr "" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "" @@ -254,65 +387,114 @@ msgid "Login failed" msgstr "" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +msgid "May" +msgstr "" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +msgid "No scheduled imports" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" msgstr "" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "" @@ -320,82 +502,102 @@ msgid "Password reset requested!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +msgid "Please enter a Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:142 +msgid "Please enter a Password" +msgstr "" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +msgid "Please enter a URL" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:125 +msgid "Please enter a Username" +msgstr "" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +msgid "Please enter SortBy" +msgstr "" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "" @@ -403,148 +605,241 @@ msgid "Request password reset!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +msgid "Saved import: #" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +msgid "Scheduled" +msgstr "" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +msgid "Simple Schedule" +msgstr "" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "" -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +msgid "System-Administrator" +msgstr "" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +msgid "Trigger import" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +msgid "update import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +msgid "Waterway axis" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:43 +msgid "Waterway gauges" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +msgid "Waterwayarea" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "" -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr ""
--- a/client/src/locale/hr_HR/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/hr_HR/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: gemmajs 1.99.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-12-05 12:23+0100\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,138 +17,200 @@ "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +msgid "at" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" msgstr "" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/Pdftool.vue:18 +msgid "Choose format:" +msgstr "" + +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "" @@ -160,89 +222,160 @@ msgid "Enter username" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +msgid "Gauge measurement" +msgstr "" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +msgid "Import Waterwayprofiles" +msgstr "" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "" @@ -254,65 +387,114 @@ msgid "Login failed" msgstr "" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +msgid "May" +msgstr "" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +msgid "No scheduled imports" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" msgstr "" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "" @@ -320,82 +502,102 @@ msgid "Password reset requested!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +msgid "Please enter a Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:142 +msgid "Please enter a Password" +msgstr "" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +msgid "Please enter a URL" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:125 +msgid "Please enter a Username" +msgstr "" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +msgid "Please enter SortBy" +msgstr "" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "" @@ -403,148 +605,241 @@ msgid "Request password reset!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +msgid "Saved import: #" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +msgid "Scheduled" +msgstr "" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +msgid "Simple Schedule" +msgstr "" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "" -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +msgid "System-Administrator" +msgstr "" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +msgid "Trigger import" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +msgid "update import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +msgid "Waterway axis" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:43 +msgid "Waterway gauges" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +msgid "Waterwayarea" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "" -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr ""
--- a/client/src/locale/hu_HU/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/hu_HU/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: gemmajs 1.99.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-12-05 12:23+0100\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,138 +17,200 @@ "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +msgid "at" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" msgstr "" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/Pdftool.vue:18 +msgid "Choose format:" +msgstr "" + +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "" @@ -160,89 +222,160 @@ msgid "Enter username" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +msgid "Gauge measurement" +msgstr "" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +msgid "Import Waterwayprofiles" +msgstr "" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "" @@ -254,65 +387,114 @@ msgid "Login failed" msgstr "" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +msgid "May" +msgstr "" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +msgid "No scheduled imports" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" msgstr "" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "" @@ -320,82 +502,102 @@ msgid "Password reset requested!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +msgid "Please enter a Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:142 +msgid "Please enter a Password" +msgstr "" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +msgid "Please enter a URL" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:125 +msgid "Please enter a Username" +msgstr "" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +msgid "Please enter SortBy" +msgstr "" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "" @@ -403,148 +605,241 @@ msgid "Request password reset!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +msgid "Saved import: #" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +msgid "Scheduled" +msgstr "" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +msgid "Simple Schedule" +msgstr "" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "" -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +msgid "System-Administrator" +msgstr "" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +msgid "Trigger import" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +msgid "update import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +msgid "Waterway axis" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:43 +msgid "Waterway gauges" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +msgid "Waterwayarea" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "" -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr ""
--- a/client/src/locale/ro_RO/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/ro_RO/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: gemmajs 1.99.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-12-05 12:23+0100\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,138 +17,200 @@ "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +msgid "at" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" msgstr "" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/Pdftool.vue:18 +msgid "Choose format:" +msgstr "" + +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "" @@ -160,89 +222,160 @@ msgid "Enter username" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +msgid "Gauge measurement" +msgstr "" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +msgid "Import Waterwayprofiles" +msgstr "" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "" @@ -254,65 +387,114 @@ msgid "Login failed" msgstr "" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +msgid "May" +msgstr "" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +msgid "No scheduled imports" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" msgstr "" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "" @@ -320,82 +502,102 @@ msgid "Password reset requested!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +msgid "Please enter a Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:142 +msgid "Please enter a Password" +msgstr "" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +msgid "Please enter a URL" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:125 +msgid "Please enter a Username" +msgstr "" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +msgid "Please enter SortBy" +msgstr "" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "" @@ -403,148 +605,241 @@ msgid "Request password reset!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +msgid "Saved import: #" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +msgid "Scheduled" +msgstr "" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +msgid "Simple Schedule" +msgstr "" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "" -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +msgid "System-Administrator" +msgstr "" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +msgid "Trigger import" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +msgid "update import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +msgid "Waterway axis" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:43 +msgid "Waterway gauges" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +msgid "Waterwayarea" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "" -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr ""
--- a/client/src/locale/sk_SK/LC_MESSAGES/app.po Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/sk_SK/LC_MESSAGES/app.po Tue Jan 15 10:07:10 2019 +0100 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: wamosjs 0.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-10 15:30+0100\n" +"POT-Creation-Date: 2019-01-15 10:04+0100\n" "PO-Revision-Date: 2018-07-03 17:18+0200\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,138 +17,200 @@ "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/components/admin/Importqueue.vue:46 +#: src/components/importschedule/Importscheduledetail.vue:452 +msgid "15 minutes" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:46 msgid "Accepted" msgstr "" -#: src/components/admin/Logs.vue:25 +#: src/components/Logs.vue:25 msgid "Accesslog" msgstr "" -#: src/components/admin/usermanagement/Usermanagement.vue:103 +#: src/components/usermanagement/Usermanagement.vue:104 msgid "Add User" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:35 +#: src/components/importschedule/Importscheduledetail.vue:472 +msgid "April" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:449 +msgid "at" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:476 +msgid "August" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:35 msgid "Author" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:34 +msgid "Available Fairway Depths" +msgstr "" + #: src/components/Login.vue:70 msgid "back to login" msgstr "" -#: src/components/admin/Importqueue.vue:136 -#: src/components/admin/Systemconfiguration.vue:134 -#: src/components/admin/Systemconfiguration.vue:149 -#: src/components/admin/Systemconfiguration.vue:168 -#: src/components/admin/Systemconfiguration.vue:185 -#: src/components/admin/usermanagement/Userdetail.vue:305 -#: src/components/admin/usermanagement/Userdetail.vue:377 -#: src/components/admin/usermanagement/Usermanagement.vue:313 -#: src/components/admin/usermanagement/Usermanagement.vue:321 -#: src/components/admin/usermanagement/Usermanagement.vue:347 -#: src/components/map/Search.vue:257 -#: src/components/map/contextbox/Bottlenecks.vue:275 -#: src/components/map/contextbox/ImportSoundingresults.vue:205 -#: src/components/map/contextbox/ImportSoundingresults.vue:244 -#: src/components/map/contextbox/ImportSoundingresults.vue:275 +#: src/components/Bottlenecks.vue:274 +#: src/components/ImportApprovedGaugeMeasurement.vue:107 +#: src/components/ImportSoundingresults.vue:216 +#: src/components/ImportSoundingresults.vue:255 +#: src/components/ImportSoundingresults.vue:286 src/components/Search.vue:258 +#: src/components/Systemconfiguration.vue:114 +#: src/components/Systemconfiguration.vue:129 +#: src/components/Systemconfiguration.vue:148 +#: src/components/Systemconfiguration.vue:165 +#: src/components/importqueue/Importqueue.vue:160 +#: src/components/importqueue/Importqueue.vue:180 +#: src/components/importqueue/Importqueuedetail.vue:182 +#: src/components/importschedule/Importschedule.vue:148 +#: src/components/importschedule/Importschedule.vue:167 +#: src/components/importschedule/Importschedule.vue:176 +#: src/components/importschedule/Importschedule.vue:198 +#: src/components/importschedule/Importscheduledetail.vue:656 +#: src/components/importschedule/Importscheduledetail.vue:724 +#: src/components/importschedule/Importscheduledetail.vue:752 +#: src/components/staging/StagingDetail.vue:246 +#: src/components/usermanagement/Userdetail.vue:304 +#: src/components/usermanagement/Userdetail.vue:376 +#: src/components/usermanagement/Usermanagement.vue:298 +#: src/components/usermanagement/Usermanagement.vue:306 +#: src/components/usermanagement/Usermanagement.vue:340 +#: src/components/importschedule/Importscheduledetail.vue:716 +#: src/components/importschedule/Importscheduledetail.vue:744 msgid "Backend Error" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:11 +#: src/components/ImportSoundingresults.vue:16 msgid "Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:19 +#: src/components/Systemconfiguration.vue:19 msgid "Bottleneck Areas fill-color" msgstr "" -#: src/components/admin/Systemconfiguration.vue:13 +#: src/components/Systemconfiguration.vue:13 msgid "Bottleneck Areas stroke-color" msgstr "" -#: src/components/Sidebar.vue:27 -#: src/components/map/contextbox/Bottlenecks.vue:4 +#: src/components/Bottlenecks.vue:4 src/components/Sidebar.vue:27 +#: src/components/importschedule/Importscheduledetail.vue:25 +#: src/components/staging/StagingDetail.vue:11 msgid "Bottlenecks" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:121 +#: src/components/ImportSoundingresults.vue:126 msgid "Cancel Upload" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:30 +#: src/components/Bottlenecks.vue:30 msgid "Chainage" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:167 -#: src/components/map/contextbox/ImportSoundingresults.vue:181 +#: src/components/ImportSoundingresults.vue:178 +#: src/components/ImportSoundingresults.vue:192 msgid "choose .zip- file" msgstr "" -#: src/components/map/Pdftool.vue:18 -msgid "Chose format:" +#: src/components/ImportApprovedGaugeMeasurement.vue:76 +#: src/components/ImportWaterwayProfiles.vue:75 +msgid "choose file to upload" msgstr "" -#: src/components/map/fairway/Profiles.vue:64 +#: src/components/Pdftool.vue:18 +msgid "Choose format:" +msgstr "" + +#: src/components/fairway/Profiles.vue:64 msgid "Compare with" msgstr "" -#: src/components/Sidebar.vue:76 +#: src/components/Sidebar.vue:116 msgid "Configuration" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:316 -#: src/components/map/contextbox/Staging.vue:70 +#: src/components/ImportSoundingresults.vue:327 +#: src/components/staging/Staging.vue:33 msgid "Confirm" msgstr "" -#: src/components/map/fairway/Profiles.vue:378 +#: src/components/fairway/Profiles.vue:382 msgid "Coordinates copied to clipboard!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:33 +#: src/components/usermanagement/Userdetail.vue:33 msgid "Country" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:67 -#: src/components/map/contextbox/Staging.vue:14 +#: src/components/importschedule/Importscheduledetail.vue:382 +msgid "Cronstring" +msgstr "" + +#: src/components/ImportSoundingresults.vue:72 +#: src/components/importqueue/Importqueuedetail.vue:49 +#: src/components/staging/Staging.vue:13 msgid "Date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:47 +#: src/components/importschedule/Importscheduledetail.vue:454 +msgid "day" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:480 +msgid "December" +msgstr "" + +#: src/components/ImportStretches.vue:4 +msgid "Define section and stretches" +msgstr "" + +#: src/components/Sidebar.vue:52 +msgid "Define sections and stretches" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:192 +msgid "Deleted import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:52 msgid "Depthreference" msgstr "" -#: src/components/map/Pdftool.vue:32 +#: src/components/Pdftool.vue:36 msgid "Download" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:113 +#: src/components/ImportSoundingresults.vue:118 msgid "Download Meta.json" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:37 -#: src/components/admin/importschedule/Importscheduledetail.vue:80 +#: src/components/importschedule/Importschedule.vue:37 msgid "Email" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:59 +#: src/components/usermanagement/Userdetail.vue:59 msgid "Email address" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:61 +#: src/components/importschedule/Importscheduledetail.vue:50 msgid "Email Notification" msgstr "" -#: src/components/admin/Importqueue.vue:53 +#: src/components/importqueue/Importqueue.vue:55 msgid "Enqueued" msgstr "" -#: src/components/map/fairway/Profiles.vue:123 +#: src/components/fairway/Profiles.vue:123 msgid "Enter coordinates manually" msgstr "" -#: src/components/map/fairway/Profiles.vue:185 +#: src/components/fairway/Profiles.vue:185 msgid "Enter label for cross profile" msgstr "" @@ -160,89 +222,160 @@ msgid "Enter username" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:386 +#: src/components/usermanagement/Userdetail.vue:385 msgid "Error while saving user" msgstr "" -#: src/components/admin/Logs.vue:34 +#: src/components/Logs.vue:34 msgid "Errorlog" msgstr "" -#: src/components/admin/Importqueue.vue:37 +#: src/components/importschedule/Importscheduledetail.vue:445 +msgid "Every" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:37 msgid "Failed" msgstr "" +#: src/components/importschedule/Importscheduledetail.vue:40 +msgid "Fairwaydimensions" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:155 +msgid "Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:470 +msgid "February" +msgstr "" + #: src/components/Login.vue:76 msgid "Forgot password" msgstr "" -#: src/components/map/Pdftool.vue:10 src/components/map/Pdftool.vue:49 +#: src/components/importschedule/Importscheduledetail.vue:464 +msgid "Friday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:31 +msgid "Gauge measurement" +msgstr "" + +#: src/components/Pdftool.vue:10 src/components/Pdftool.vue:53 msgid "Generate PDF" msgstr "" -#: src/components/map/Identify.vue:10 +#: src/components/importschedule/Importscheduledetail.vue:453 +msgid "hour" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:52 +msgid "Id" +msgstr "" + +#: src/components/Identify.vue:10 msgid "Identified" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:33 -#: src/components/map/contextbox/ImportSoundingresults.vue:267 +#: src/components/ImportApprovedGaugeMeasurement.vue:99 +#: src/components/ImportSoundingresults.vue:278 +#: src/components/ImportWaterwayProfiles.vue:88 src/components/Sidebar.vue:56 +#: src/components/importschedule/Importschedule.vue:33 +#: src/components/importschedule/Importscheduledetail.vue:519 +#: src/components/importschedule/Importscheduledetail.vue:649 +#: src/components/importschedule/Importscheduledetail.vue:709 +#: src/components/importschedule/Importscheduledetail.vue:737 msgid "Import" msgstr "" -#: src/components/Sidebar.vue:40 +#: src/components/ImportApprovedGaugeMeasurement.vue:11 +#: src/components/Sidebar.vue:74 +msgid "Import approved gaugemeasurements" +msgstr "" + +#: src/components/Sidebar.vue:64 msgid "Import soundingresults" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:4 +#: src/components/ImportSoundingresults.vue:9 msgid "Import Soundingresults" msgstr "" -#: src/components/map/contextbox/Staging.vue:15 +#: src/components/Sidebar.vue:84 +msgid "Import waterway profiles" +msgstr "" + +#: src/components/ImportWaterwayProfiles.vue:11 +msgid "Import Waterwayprofiles" +msgstr "" + +#: src/components/staging/Staging.vue:15 msgid "Imported" msgstr "" -#: src/components/Sidebar.vue:92 src/components/admin/Importqueue.vue:9 +#: src/components/Sidebar.vue:134 src/components/importqueue/Importqueue.vue:9 msgid "Importqueue" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:20 +#: src/components/Sidebar.vue:94 +#: src/components/importschedule/Importschedule.vue:9 +#: src/components/importschedule/Importschedule.vue:160 +#: src/components/importschedule/Importschedule.vue:191 +#: src/components/importschedule/Importscheduledetail.vue:20 msgid "Imports" msgstr "" -#: src/components/Sidebar.vue:100 -#: src/components/admin/importschedule/Importschedule.vue:9 -msgid "Importschedule" +#: src/components/importschedule/Importscheduledetail.vue:81 +msgid "Insecure" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:34 -msgid "Importtype" -msgstr "" - -#: src/components/admin/usermanagement/Userdetail.vue:354 +#: src/components/usermanagement/Userdetail.vue:353 msgid "invalid email" msgstr "" -#: src/components/map/fairway/Profiles.vue:412 +#: src/components/fairway/Profiles.vue:416 msgid "Invalid input" msgstr "" -#: src/components/admin/Importqueue.vue:54 +#: src/components/Pdftool.vue:24 +msgid "ISO A3" +msgstr "" + +#: src/components/Pdftool.vue:25 +msgid "ISO A4" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:469 +msgid "January" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:475 +msgid "July" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:474 +msgid "June" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:58 +#: src/components/importqueue/Importqueuedetail.vue:45 msgid "Kind" msgstr "" -#: src/components/map/Pdftool.vue:20 +#: src/components/Pdftool.vue:20 msgid "landscape" msgstr "" -#: src/components/admin/Logs.vue:41 +#: src/components/Logs.vue:41 msgid "Last refresh:" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:19 +#: src/components/Bottlenecks.vue:19 msgid "Latest" msgstr "" -#: src/components/map/layers/Layers.vue:10 +#: src/components/layers/Layers.vue:10 msgid "Layers" msgstr "" @@ -254,65 +387,114 @@ msgid "Login failed" msgstr "" -#: src/components/Sidebar.vue:110 +#: src/components/Sidebar.vue:144 msgid "Logout" msgstr "" -#: src/components/Sidebar.vue:84 src/components/admin/Logs.vue:9 +#: src/components/Logs.vue:9 src/components/Sidebar.vue:124 msgid "Logs" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:140 +#: src/components/usermanagement/Userdetail.vue:140 msgid "Mail was sent" msgstr "" +#: src/components/importschedule/Importschedule.vue:161 +#: src/components/importschedule/Importscheduledetail.vue:650 +msgid "Manually triggered import: #" +msgstr "" + #: src/components/Sidebar.vue:15 msgid "Map" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:20 +#: src/components/importschedule/Importscheduledetail.vue:471 +msgid "March" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:473 +msgid "May" +msgstr "" + +#: src/components/Bottlenecks.vue:20 msgid "Measurement" msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:9 -#: src/components/map/contextbox/Staging.vue:12 +#: src/components/importqueue/Importqueuedetail.vue:58 +msgid "Message" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:446 +msgid "minutes past" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:460 +msgid "Monday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:456 +msgid "month" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:247 +msgid "Monthly" +msgstr "" + +#: src/components/Bottlenecks.vue:9 src/components/staging/Staging.vue:11 msgid "Name" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:9 -msgid "New import" -msgstr "" - -#: src/components/admin/importschedule/Importschedule.vue:72 +#: src/components/importschedule/Importschedule.vue:93 +#: src/components/importschedule/Importscheduledetail.vue:520 msgid "New Import" msgstr "" -#: src/components/map/Identify.vue:47 +#: src/components/Identify.vue:47 msgid "No features identified." msgstr "" -#: src/components/map/contextbox/Bottlenecks.vue:116 -#: src/components/map/contextbox/Staging.vue:63 +#: src/components/Bottlenecks.vue:115 src/components/staging/Staging.vue:36 msgid "No results." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:66 -msgid "No schedules" +#: src/components/importschedule/Importschedule.vue:85 +msgid "No scheduled imports" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:479 +msgid "November" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:450 +msgid "o' clock" msgstr "" -#: src/components/map/Pdftool.vue:41 +#: src/components/importschedule/Importscheduledetail.vue:478 +msgid "October" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:448 +msgid "of" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:447 +msgid "on" +msgstr "" + +#: src/components/Pdftool.vue:45 msgid "Open in new window" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:238 +#: src/components/usermanagement/Userdetail.vue:237 msgid "password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:236 +#: src/components/importschedule/Importscheduledetail.vue:134 +#: src/components/usermanagement/Userdetail.vue:235 msgid "Password" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:239 +#: src/components/usermanagement/Userdetail.vue:238 msgid "password again" msgstr "" @@ -320,82 +502,102 @@ msgid "Password reset requested!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:347 -#: src/components/admin/usermanagement/Userdetail.vue:348 +#: src/components/usermanagement/Userdetail.vue:346 +#: src/components/usermanagement/Userdetail.vue:347 msgid "Password should at least be 8 char long including 1 digit and 1 special char like $" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:343 +#: src/components/usermanagement/Userdetail.vue:342 msgid "Passwords do not match!" msgstr "" -#: src/components/admin/Importqueue.vue:40 +#: src/components/importqueue/Importqueue.vue:40 msgid "Pending" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:332 +#: src/components/usermanagement/Userdetail.vue:331 msgid "Please choose a country" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:337 +#: src/components/usermanagement/Userdetail.vue:336 msgid "Please choose a role" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:79 +#: src/components/ImportSoundingresults.vue:84 msgid "Please enter a date" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:39 +#: src/components/importschedule/Importscheduledetail.vue:167 +msgid "Please enter a Featuretype" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:142 +msgid "Please enter a Password" +msgstr "" + +#: src/components/ImportSoundingresults.vue:44 msgid "Please enter a projection" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:62 +#: src/components/ImportSoundingresults.vue:67 msgid "Please enter a reference" msgstr "" -#: src/components/map/fairway/Profiles.vue:413 -#: src/components/map/fairway/Profiles.vue:414 +#: src/components/importschedule/Importscheduledetail.vue:106 +msgid "Please enter a URL" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:125 +msgid "Please enter a Username" +msgstr "" + +#: src/components/fairway/Profiles.vue:417 +#: src/components/fairway/Profiles.vue:418 msgid "Please enter correct coordinates in the format: Lat,Lon,Lat,Lon" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:22 +#: src/components/importschedule/Importscheduledetail.vue:184 +msgid "Please enter SortBy" +msgstr "" + +#: src/components/ImportSoundingresults.vue:27 msgid "Please select a bottleneck" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:40 -#: src/components/admin/usermanagement/Userdetail.vue:85 +#: src/components/usermanagement/Userdetail.vue:40 +#: src/components/usermanagement/Userdetail.vue:85 msgid "Please select one" msgstr "" -#: src/components/map/Pdftool.vue:21 +#: src/components/Pdftool.vue:21 msgid "portrait" msgstr "" -#: src/components/map/fairway/Profiles.vue:452 +#: src/components/fairway/Profiles.vue:456 msgid "Profile deleted!" msgstr "" -#: src/components/map/fairway/Profiles.vue:438 +#: src/components/fairway/Profiles.vue:442 msgid "Profile saved!" msgstr "" -#: src/components/map/fairway/Profiles.vue:10 +#: src/components/fairway/Profiles.vue:10 msgid "Profiles" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:28 +#: src/components/ImportSoundingresults.vue:33 msgid "Projection" msgstr "" -#: src/components/admin/Importqueue.vue:72 src/components/admin/Logs.vue:48 +#: src/components/Logs.vue:48 src/components/importqueue/Importqueue.vue:84 msgid "Refresh" msgstr "" -#: src/components/admin/Importqueue.vue:43 +#: src/components/importqueue/Importqueue.vue:43 msgid "Rejected" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:237 +#: src/components/usermanagement/Userdetail.vue:236 msgid "Repeat Password" msgstr "" @@ -403,148 +605,241 @@ msgid "Request password reset!" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:78 +#: src/components/usermanagement/Userdetail.vue:78 msgid "Role" msgstr "" -#: src/components/map/fairway/Profiles.vue:82 +#: src/components/importschedule/Importscheduledetail.vue:465 +msgid "Saturday" +msgstr "" + +#: src/components/fairway/Profiles.vue:82 msgid "Saved cross profiles" msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:36 -#: src/components/admin/importschedule/Importscheduledetail.vue:48 +#: src/components/importschedule/Importscheduledetail.vue:710 +msgid "Saved import: #" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:36 +#: src/components/importschedule/Importscheduledetail.vue:238 msgid "Schedule" msgstr "" -#: src/components/map/fairway/Profiles.vue:32 +#: src/components/importschedule/Importscheduledetail.vue:196 +msgid "Scheduled" +msgstr "" + +#: src/components/fairway/Profiles.vue:32 msgid "Select Bottleneck" msgstr "" -#: src/components/admin/Systemconfiguration.vue:25 +#: src/components/Systemconfiguration.vue:25 msgid "Send" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:138 +#: src/components/usermanagement/Userdetail.vue:138 msgid "Send testmail" msgstr "" -#: src/components/admin/Importqueue.vue:56 +#: src/components/importschedule/Importscheduledetail.vue:477 +msgid "September" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:64 msgid "Signer" msgstr "" -#: src/components/map/Identify.vue:60 +#: src/components/importschedule/Importscheduledetail.vue:216 +msgid "Simple Schedule" +msgstr "" + +#: src/components/Identify.vue:60 msgid "" "Some data ©\n" " <a href=\"https://www.openstreetmap.org/copyright\">%{ name }</a>\n" " contributors." msgstr "" -#: src/components/map/fairway/Profiles.vue:45 +#: src/components/importschedule/Importscheduledetail.vue:176 +msgid "SortBy" +msgstr "" + +#: src/components/fairway/Profiles.vue:45 msgid "Sounding Result" msgstr "" -#: src/components/map/Identify.vue:57 +#: src/components/Identify.vue:57 msgid "source-code" msgstr "" -#: src/components/Sidebar.vue:54 +#: src/components/Sidebar.vue:40 msgid "Staging area" msgstr "" -#: src/components/map/contextbox/Staging.vue:7 +#: src/components/staging/Staging.vue:7 msgid "Staging Area" msgstr "" -#: src/components/map/fairway/Profiles.vue:142 +#: src/components/fairway/Profiles.vue:142 msgid "Start" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:268 +#: src/components/ImportSoundingresults.vue:279 msgid "Starting import for " msgstr "" -#: src/components/admin/Importqueue.vue:57 +#: src/components/ImportApprovedGaugeMeasurement.vue:100 +msgid "Starting import of Approved Gauge Measurements" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:67 msgid "State" msgstr "" -#: src/components/admin/importschedule/Importscheduledetail.vue:85 -#: src/components/admin/usermanagement/Userdetail.vue:129 +#: src/components/importschedule/Importscheduledetail.vue:394 +#: src/components/usermanagement/Userdetail.vue:129 msgid "Submit" msgstr "" -#: src/components/map/fairway/Profiles.vue:377 +#: src/components/fairway/Profiles.vue:381 msgid "Success" msgstr "" -#: src/components/admin/Importqueue.vue:34 +#: src/components/importqueue/Importqueue.vue:34 msgid "Successful" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:88 +#: src/components/importschedule/Importscheduledetail.vue:466 +msgid "Sunday" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:88 msgid "Sysadmin" msgstr "" -#: src/components/Sidebar.vue:57 +#: src/components/usermanagement/Usermanagement.vue:326 +msgid "System-Administrator" +msgstr "" + +#: src/components/Sidebar.vue:97 msgid "Systemadministration" msgstr "" -#: src/components/admin/Systemconfiguration.vue:8 +#: src/components/Systemconfiguration.vue:8 msgid "Systemconfiguration" msgstr "" -#: src/components/map/Identify.vue:51 +#: src/components/Identify.vue:51 msgid "" "This app uses <i>gemma</i>, which is Free Software under <br/>\n" " %{ license } without warranty, see docs for details." msgstr "" -#: src/components/admin/importschedule/Importschedule.vue:34 -#: src/components/map/contextbox/Staging.vue:13 +#: src/components/importschedule/Importscheduledetail.vue:463 +msgid "Thursday" +msgstr "" + +#: src/components/ImportApprovedGaugeMeasurement.vue:43 +#: src/components/ImportWaterwayProfiles.vue:43 +#: src/components/importschedule/Importscheduledetail.vue:407 +msgid "Trigger import" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:461 +msgid "Tuesday" +msgstr "" + +#: src/components/importschedule/Importschedule.vue:34 +#: src/components/staging/Staging.vue:12 msgid "Type" msgstr "" -#: src/components/map/contextbox/ImportSoundingresults.vue:313 +#: src/components/ImportWaterwayProfiles.vue:89 +msgid "under construction" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:738 +msgid "update import: #" +msgstr "" + +#: src/components/ImportSoundingresults.vue:324 msgid "Upload" msgstr "" -#: src/components/admin/Importqueue.vue:55 +#: src/components/importschedule/Importscheduledetail.vue:72 +msgid "URL" +msgstr "" + +#: src/components/importqueue/Importqueue.vue:61 msgid "User" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:14 -#: src/components/map/contextbox/Staging.vue:16 +#: src/components/importschedule/Importscheduledetail.vue:117 +#: src/components/staging/Staging.vue:18 +#: src/components/usermanagement/Userdetail.vue:14 msgid "Username" msgstr "" -#: src/components/Sidebar.vue:66 -#: src/components/admin/usermanagement/Usermanagement.vue:14 +#: src/components/Sidebar.vue:108 +#: src/components/usermanagement/Usermanagement.vue:14 msgid "Users" msgstr "" -#: src/components/map/Identify.vue:65 +#: src/components/Identify.vue:65 msgid "" "Uses\n" " <a href=\"https://download.geonames.org/export/dump/readme.txt\">GeoNames</a>\n" " under %{ geoLicense }." msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Userdetail.vue:91 +#: src/components/usermanagement/Usermanagement.vue:327 msgid "Waterway Admin" msgstr "" -#: src/components/admin/usermanagement/Userdetail.vue:94 +#: src/components/importschedule/Importscheduledetail.vue:28 +msgid "Waterway axis" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:43 +msgid "Waterway gauges" +msgstr "" + +#: src/components/usermanagement/Userdetail.vue:94 +#: src/components/usermanagement/Usermanagement.vue:328 msgid "Waterway User" msgstr "" -#: src/components/map/fairway/Profiles.vue:439 -#: src/components/map/fairway/Profiles.vue:440 +#: src/components/importschedule/Importscheduledetail.vue:37 +msgid "Waterwayarea" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:462 +msgid "Wednesday" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:455 +msgid "week" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:246 +msgid "Weekly" +msgstr "" + +#: src/components/importschedule/Importscheduledetail.vue:457 +msgid "year" +msgstr "" + +#: src/components/fairway/Profiles.vue:443 +#: src/components/fairway/Profiles.vue:444 msgid "You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile." msgstr "" -#: src/store/map.js:415 +#: src/store/map.js:416 msgid "Length" msgstr "" -#: src/store/map.js:436 +#: src/store/map.js:437 msgid "Area" msgstr ""
--- a/client/src/locale/translations.json Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/locale/translations.json Tue Jan 15 10:07:10 2019 +0100 @@ -1,1 +1,1 @@ -{"bg_BG":{},"de_AT":{"Accepted":"Akzeptiert","Accesslog":"Zugriffs-Protokoll","Add User":"Benutzer hinzufügen","Author":"Autor","back to login":"zurück zur Anmeldung","Backend Error":"Server-Fehler","Bottleneck":"Seichtstelle","Bottleneck Areas fill-color":"Flächenfüllfarbe Seichtstelle","Bottleneck Areas stroke-color":"Flächenumrandungsfarbe Seichtstelle","Bottlenecks":"Seichtstellen","Cancel Upload":"Hochladen abbrechen","Chainage":"Stationierung","choose .zip- file":"Wählen Sie eine .zip Datei","Chose format:":"Format wählen:","Compare with":"Vergleiche mit","Configuration":"Konfiguration","Confirm":"Bestätigen","Coordinates copied to clipboard!":"Koordinaten auf die Zwischenablage kopiert!","Country":"Land","Date":"Datum","Depthreference":"Tiefenreferenz","Download":"Herunterladen","Download Meta.json":"Meta.json Herunterladen","Email":"E-Mail","Email address":"E-Mail Adresse","Enqueued":"Hinzugefügt","Enter coordinates manually":"Manuelle Koordinateneingabe","Enter label for cross profile":"Namen für Profilschnitt eingeben","Enter passphrase":"Passphrase eingeben","Enter username":"Benutzername eingeben","Error while saving user":"Während des Speicherns der Nutzerdaten trat ein Fehler auf","Errorlog":"Fehlerprotokoll","Failed":"Fehlgeschlagen","Forgot password":"Passwort vergessen","Generate PDF":"PDF generieren","Identified":"Identifiziert","Import":"Daten-Import","Import soundingresults":"Seichtstellenmessungen importieren","Import Soundingresults":"Seichtstellenmessungen importieren","Imported":"Importiert","Importqueue":"Import-Warteschlange","Importschedule":"Import-Zeitplan","invalid email":"Ungültige E-Mail","Invalid input":"Ungültige Eingabe","Kind":"Art","landscape":"Querformat","Last refresh:":"Letzter Abgleich:","Latest":"Neuste","Layers":"Ebenen","Login":"Login","Login failed":"Login fehlgeschlagen","Logout":"Abmelden","Logs":"Protokolle","Mail was sent":"E-Mail wurde gesendet","Map":"Karte","Measurement":"Messung","Name":"Name","New import":"Neuer Import","New Import":"Neuer Import","No features identified.":"Keine Objekte identifiziert.","No results.":"Keine Ergebnisse.","No schedules":"Keine Pläne","Open in new window":"In neuem Fenster öffnen","password":"Passwort","Password":"Passwort","password again":"Noch einmal das Passwort","Password reset requested!":"Passwort Zurücksetzung angefragt!","Password should at least be 8 char long including 1 digit and 1 special char like $":"Das Passwort sollte mindestens 8 Zeichen lang sein, eine Zahlenziffer und ein Sonderzeichen wie etwa $ enthalten","Passwords do not match!":"Die Passwörter stimmen nicht überein!","Pending":"Ausstehend","Please choose a country":"Bitte wählen Sie ein Land aus","Please choose a role":"Bitte wählen Sie eine Rolle aus","Please enter a date":"Bitte ein Datum eingeben","Please enter a projection":"Bitte eine Projektion eingeben","Please enter a reference":"Bitte ein Höhenreferenzsystem eingeben","Please enter correct coordinates in the format: Lat,Lon,Lat,Lon":"Bitte geben Sie die Koordinaten in folgendem Format an: Lat,Lon,Lat,Lon","Please select a bottleneck":"Bitte eine Seichtstelle wählen","Please select one":"Bitte auswählen","portrait":"Hochformat","Profile deleted!":"Profil gelöscht!","Profile saved!":"Profil gespeichert!","Profiles":"Profile","Projection":"Projektion","Refresh":"Aktualisieren","Rejected":"Abgelehnt","Repeat Password":"Passwort erneut eingeben","Request password reset!":"Passwort-Zurücksetzung anfragen!","Role":"Rolle","Saved cross profiles":"Gespeicherte Profile","Schedule":"Zeitplan","Select Bottleneck":"Wähle Seichtstelle","Send":"Absenden","Send testmail":"Test-E-Mail versenden","Sounding Result":"Seichtstellenvermessungsergebnisse","source-code":"Quelltext","Staging area":"Import-Überprüfung","Staging Area":"Import-Überprüfung","Start":"Start","Starting import for ":"Import gestartet ","State":"Zustand","Submit":"Abschicken","Success":"Erfolg","Successful":"Erfolgreich","Sysadmin":"Sys-Admin","Systemadministration":"System-Administration","Systemconfiguration":"System-Konfiguation","Type":"Typ","Upload":"Hochladen","User":"Benutzer","Username":"Benutzername","Users":"Benutzer","Waterway Admin":"Waterway-Admin","Waterway User":"Waterway-Benutzer","You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile.":"Sie können diese Koordinaten aus dem \"Gespeicherte Profile\"-Menü auswählen, um diesen Profilschnitt wieder herzustellen."},"en_GB":{},"hr_HR":{},"hu_HU":{},"ro_RO":{},"sk_SK":{}} \ No newline at end of file +{"bg_BG":{},"de_AT":{"Accepted":"Akzeptiert","Accesslog":"Zugriffs-Protokoll","Add User":"Benutzer hinzufügen","Author":"Autor","back to login":"zurück zur Anmeldung","Backend Error":"Server-Fehler","Bottleneck":"Seichtstelle","Bottleneck Areas fill-color":"Flächenfüllfarbe Seichtstelle","Bottleneck Areas stroke-color":"Flächenumrandungsfarbe Seichtstelle","Bottlenecks":"Seichtstellen","Cancel Upload":"Hochladen abbrechen","Chainage":"Stationierung","choose .zip- file":"Wählen Sie eine .zip Datei","Compare with":"Vergleiche mit","Configuration":"Konfiguration","Confirm":"Bestätigen","Coordinates copied to clipboard!":"Koordinaten auf die Zwischenablage kopiert!","Country":"Land","Date":"Datum","Depthreference":"Tiefenreferenz","Download":"Herunterladen","Download Meta.json":"Meta.json Herunterladen","Email":"E-Mail","Email address":"E-Mail Adresse","Email Notification":"E-Mail Benachrichtigung","Enqueued":"Hinzugefügt","Enter coordinates manually":"Manuelle Koordinateneingabe","Enter label for cross profile":"Namen für Profilschnitt eingeben","Enter passphrase":"Passphrase eingeben","Enter username":"Benutzername eingeben","Error while saving user":"Während des Speicherns der Nutzerdaten trat ein Fehler auf","Errorlog":"Fehlerprotokoll","Failed":"Fehlgeschlagen","Forgot password":"Passwort vergessen","Generate PDF":"PDF generieren","Identified":"Identifiziert","Import":"Daten-Import","Import soundingresults":"Seichtstellenmessungen importieren","Import Soundingresults":"Seichtstellenmessungen importieren","Imported":"Importiert","Importqueue":"Import-Warteschlange","Imports":"Daten-Import","invalid email":"Ungültige E-Mail","Invalid input":"Ungültige Eingabe","Kind":"Art","landscape":"Querformat","Last refresh:":"Letzter Abgleich:","Latest":"Neuste","Layers":"Ebenen","Login":"Login","Login failed":"Login fehlgeschlagen","Logout":"Abmelden","Logs":"Protokolle","Mail was sent":"E-Mail wurde gesendet","Map":"Karte","Measurement":"Messung","Name":"Name","New Import":"Neuer Import","No features identified.":"Keine Objekte identifiziert.","No results.":"Keine Ergebnisse.","Open in new window":"In neuem Fenster öffnen","password":"Passwort","Password":"Passwort","password again":"Noch einmal das Passwort","Password reset requested!":"Passwort Zurücksetzung angefragt!","Password should at least be 8 char long including 1 digit and 1 special char like $":"Das Passwort sollte mindestens 8 Zeichen lang sein, eine Zahlenziffer und ein Sonderzeichen wie etwa $ enthalten","Passwords do not match!":"Die Passwörter stimmen nicht überein!","Pending":"Ausstehend","Please choose a country":"Bitte wählen Sie ein Land aus","Please choose a role":"Bitte wählen Sie eine Rolle aus","Please enter a date":"Bitte ein Datum eingeben","Please enter a projection":"Bitte eine Projektion eingeben","Please enter a reference":"Bitte ein Höhenreferenzsystem eingeben","Please enter correct coordinates in the format: Lat,Lon,Lat,Lon":"Bitte geben Sie die Koordinaten in folgendem Format an: Lat,Lon,Lat,Lon","Please select a bottleneck":"Bitte eine Seichtstelle wählen","Please select one":"Bitte auswählen","portrait":"Hochformat","Profile deleted!":"Profil gelöscht!","Profile saved!":"Profil gespeichert!","Profiles":"Profile","Projection":"Projektion","Refresh":"Aktualisieren","Rejected":"Abgelehnt","Repeat Password":"Passwort erneut eingeben","Request password reset!":"Passwort-Zurücksetzung anfragen!","Role":"Rolle","Saved cross profiles":"Gespeicherte Profile","Schedule":"Zeitplan","Select Bottleneck":"Wähle Seichtstelle","Send":"Absenden","Send testmail":"Test-E-Mail versenden","Signer":"Überprüft durch","Sounding Result":"Seichtstellenvermessung","source-code":"Quelltext","Staging area":"Import-Überprüfung","Staging Area":"Import-Überprüfung","Start":"Start","Starting import for ":"Import gestartet ","State":"Zustand","Submit":"Abschicken","Success":"Erfolg","Successful":"Erfolgreich","Sysadmin":"Sys-Admin","Systemadministration":"System-Administration","Systemconfiguration":"System-Konfiguation","Type":"Typ","Upload":"Hochladen","User":"Benutzer","Username":"Benutzername","Users":"Benutzer","Waterway Admin":"Waterway-Admin","Waterway User":"Waterway-Benutzer","You can now select these coordinates from the \"Saved cross profiles\" menu to restore this cross profile.":"Sie können diese Koordinaten aus dem \"Gespeicherte Profile\"-Menü auswählen, um diesen Profilschnitt wieder herzustellen.","Length":"Länge","Area":"Fläche"},"en_GB":{},"hr_HR":{},"hu_HU":{},"ro_RO":{},"sk_SK":{}} \ No newline at end of file
--- a/client/src/main.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/main.js Tue Jan 15 10:07:10 2019 +0100 @@ -35,6 +35,7 @@ faAngleUp, faBars, faBook, + faBullseye, faChartArea, faCheck, faCity, @@ -57,6 +58,7 @@ faPlay, faPlus, faPowerOff, + faRoad, faRuler, faSearch, faShip, @@ -85,6 +87,7 @@ faAngleUp, faBars, faBook, + faBullseye, faChartArea, faCheck, faCity, @@ -107,6 +110,7 @@ faPlay, faPlus, faPowerOff, + faRoad, faRuler, faSearch, faShip,
--- a/client/src/router.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/router.js Tue Jan 15 10:07:10 2019 +0100 @@ -20,19 +20,6 @@ /* facilitate codesplitting */ const Login = () => import("./components/Login.vue"); -const Main = () => import("./components/Main.vue"); -const Usermanagement = () => - import("./components/usermanagement/Usermanagement.vue"); -const Logs = () => import("./components/Logs.vue"); -const Importqueue = () => import("./components/importqueue/Importqueue.vue"); -const Importschedule = () => - import("./components/importschedule/Importschedule.vue"); -const Systemconfiguration = () => - import("./components/Systemconfiguration.vue"); -const Importsoundingresults = () => - import("./components/ImportSoundingresults.vue"); - -const Importstretches = () => import("./components/ImportStretches.vue"); Vue.use(Router); @@ -46,7 +33,7 @@ { path: "/usermanagement", name: "usermanagement", - component: Usermanagement, + component: () => import("./components/usermanagement/Usermanagement.vue"), meta: { requiresAuth: true }, @@ -62,7 +49,7 @@ { path: "/logs", name: "logs", - component: Logs, + component: () => import("./components/Logs.vue"), meta: { requiresAuth: true }, @@ -78,7 +65,7 @@ { path: "/systemconfiguration", name: "systemconfiguration", - component: Systemconfiguration, + component: () => import("./components/Systemconfiguration.vue"), meta: { requiresAuth: true }, @@ -93,8 +80,24 @@ }, { path: "/importqueue", + name: "importqueues", + component: () => import("./components/importqueue/Importqueue.vue"), + meta: { + requiresAuth: true + }, + beforeEnter: (to, from, next) => { + const isWaterwayAdmin = store.getters["user/isWaterwayAdmin"]; + if (!isWaterwayAdmin) { + next("/"); + } else { + next(); + } + } + }, + { + path: "/importqueue/:id", name: "importqueue", - component: Importqueue, + component: () => import("./components/importqueue/Importqueue.vue"), meta: { requiresAuth: true }, @@ -110,7 +113,7 @@ { path: "/importsoundingresults", name: "importsoundingresults", - component: Importsoundingresults, + component: () => import("./components/ImportSoundingresults.vue"), meta: { requiresAuth: true }, @@ -124,9 +127,25 @@ } }, { - path: "/importstretches", - name: "importstretches", - component: Importstretches, + path: "/importwaterwayprofiles", + name: "waterwayprofiles", + component: () => import("./components/ImportWaterwayProfiles"), + meta: { + requiresAuth: true + }, + beforeEnter: (to, from, next) => { + const isWaterwayAdmin = store.getters["user/isWaterwayAdmin"]; + if (!isWaterwayAdmin) { + next("/"); + } else { + next(); + } + } + }, + { + path: "/importapprovedgaugemeasurement", + name: "approvedgaugemeasurement", + component: () => import("./components/ImportApprovedGaugeMeasurement"), meta: { requiresAuth: true }, @@ -142,7 +161,7 @@ { path: "/importschedule", name: "importschedule", - component: Importschedule, + component: () => import("./components/importschedule/Importschedule.vue"), meta: { requiresAuth: true }, @@ -158,7 +177,7 @@ { path: "/", name: "mainview", - component: Main, + component: () => import("./components/Main.vue"), meta: { requiresAuth: true },
--- a/client/src/store/imports.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/store/imports.js Tue Jan 15 10:07:10 2019 +0100 @@ -12,7 +12,8 @@ * Thomas Junk <thomas.junk@intevation.de> */ -import { HTTP } from "../lib/http"; +import { HTTP } from "@/lib/http"; +import Vue from "vue"; /* eslint-disable no-unused-vars */ /* eslint-disable no-unreachable */ @@ -22,18 +23,78 @@ REJECTED: "declined" }; +const IMPORTTYPES = { + BOTTLENECK: "bottleneck", + WATERWAYAXIS: "waterwayaxis", + GAUGEMEASUREMENT: "gaugemeasurement", + FAIRWAYAVAILABILITY: "fairwayavailability", + WATERWAYAREA: "waterwayarea", + FAIRWAYDIMENSIONS: "fd", + WATERWAYGAUGES: "wg" +}; + const SCHEDULES = { DAILY: "daily", MONTHLY: "monthly" }; +const IMPORTTYPEKIND = { + bottleneck: "bn", + fairwayavailability: "fa", + gaugemeasurement: "gm", + waterwayaxis: "wx", + waterwayarea: "wa", + fairwaydimensions: "fd", + waterwaygauges: "wg" +}; + +const KINDIMPORTTYPE = { + bn: "bottleneck", + fa: "fairwayavailability", + gm: "gaugemeasurement", + wx: "waterwayaxis", + wa: "waterwayarea", + fd: "fairwaydimensions", + wg: "waterwaygauges" +}; + +const initializeCurrentSchedule = () => { + return { + id: null, + importType: null, + schedule: null, + import_: null, + importSource: null, + eMailNotification: false, + scheduled: false, + easyCron: true, + cronString: "* * * * ", + cronMode: "", + minutes: null, + month: null, + hour: null, + day: null, + dayOfMonth: null, + simple: null, + url: null, + insecure: false, + triggerActive: true, + featureType: null, + sortBy: null, + username: "", + password: "" + }; +}; + // initial state const init = () => { return { imports: [], staging: [], schedules: [], - importScheduleDetailVisible: false + importScheduleDetailVisible: false, + currentSchedule: initializeCurrentSchedule(), + importToReview: null }; }; @@ -42,8 +103,8 @@ namespaced: true, state: init(), mutations: { - deleteSchedule: (state, index) => { - state.schedules.splice(index, 1); + clearCurrentSchedule: state => { + state.currentSchedule = initializeCurrentSchedule(); }, setImportScheduleDetailInvisible: state => { state.importScheduleDetailVisible = false; @@ -63,7 +124,11 @@ }); state.staging = enriched; }, - + setImportToReview: (state, id) => { + if (!isNaN(parseFloat(id)) && isFinite(id)) { + state.importToReview = id; + } + }, toggleApproval: (state, change) => { const { id, newStatus } = change; const stagedResult = state.staging.find(e => { @@ -74,17 +139,129 @@ } else { stagedResult.status = newStatus; } + }, + unmarshallCurrentSchedule: (state, payload) => { + const { kind, id, cron, url, attributes } = payload; + const eMailNotification = payload["send-email"]; + Vue.set(state.currentSchedule, "import_", KINDIMPORTTYPE[kind]); + Vue.set(state.currentSchedule, "id", id); + if (cron) { + Vue.set(state.currentSchedule, "scheduled", true); + Vue.set(state.currentSchedule, "easyCron", false); + Vue.set(state.currentSchedule, "cronString", cron); + } + if (eMailNotification) { + Vue.set(state.currentSchedule, "eMailNotification", eMailNotification); + } + if (url) { + Vue.set(state.currentSchedule, "url", url); + } + if (attributes) { + let { insecure, username, password } = attributes; + let sortBy = attributes["sort-by"]; + const featureType = attributes["feature-type"]; + insecure = insecure == "true"; + if (insecure) { + Vue.set(state.currentSchedule, "insecure", insecure); + } + if (featureType) { + Vue.set(state.currentSchedule, "featureType", featureType); + } + if (sortBy) { + Vue.set(state.currentSchedule, "sortBy", sortBy); + } + if (username) { + Vue.set(state.currentSchedule, "username", username); + } + if (password) { + Vue.set(state.currentSchedule, "password", password); + } + } } }, actions: { - getSchedules({ commit }) { - throw new Error("Not Implemented!"); + loadSchedule({ commit }, id) { return new Promise((resolve, reject) => { - HTTP.get("/imports", { + HTTP.get("/imports/config/" + id, { headers: { "X-Gemma-Auth": localStorage.getItem("token") } }) .then(response => { - commit("setImports", response.data.imports); + commit("unmarshallCurrentSchedule", response.data); + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + deleteSchedule({ commit }, id) { + return new Promise((resolve, reject) => { + HTTP.delete("imports/config/" + id, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token") + } + }) + .then(response => { + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + updateCurrentSchedule({ commit }, payload) { + const { data, id } = payload; + return new Promise((resolve, reject) => { + HTTP.patch("imports/config/" + id, data, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token") + } + }) + .then(response => { + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + saveCurrentSchedule({ commit }, data) { + return new Promise((resolve, reject) => { + HTTP.post("imports/config", data, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token") + } + }) + .then(response => { + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + loadSchedules({ commit }) { + return new Promise((resolve, reject) => { + HTTP.get("/imports/config", { + headers: { "X-Gemma-Auth": localStorage.getItem("token") } + }) + .then(response => { + commit("setSchedules", response.data); + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + triggerImport({ commit }, { type, data }) { + return new Promise((resolve, reject) => { + HTTP.post("imports/" + type, data, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token") + } + }) + .then(response => { resolve(response); }) .catch(error => { @@ -123,4 +300,11 @@ } }; -export { imports, STATES, SCHEDULES }; +export { + imports, + STATES, + SCHEDULES, + IMPORTTYPES, + IMPORTTYPEKIND, + initializeCurrentSchedule +};
--- a/client/src/store/user.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/src/store/user.js Tue Jan 15 10:07:10 2019 +0100 @@ -75,7 +75,7 @@ const handleResponse = response => { const { expires } = response.data; const renew = - (new Date(toMillisFromString(expires)) - new Date()) / 1010; + (new Date(toMillisFromString(expires)) - new Date()) * 0.9; commit("authSuccess", response.data); resolve(response); setTimeout(() => {
--- a/client/vue.config.js Tue Jan 15 09:54:46 2019 +0100 +++ b/client/vue.config.js Tue Jan 15 10:07:10 2019 +0100 @@ -1,7 +1,9 @@ const CopyWebpackPlugin = require("copy-webpack-plugin"); - module.exports = { outputDir: "../web", + configureWebpack: { + devtool: "source-map" + }, chainWebpack: config => { let vendorImgPath = process.env.VUE_APP_VENDOR_IMG_PATH; if (!vendorImgPath) return; @@ -10,6 +12,11 @@ .use(CopyWebpackPlugin, [[{ from: vendorImgPath, to: "img" }]], { copyUnmodified: true }); + if (process.env.ANALYZE) { + var BundleAnalyzerPlugin = require("webpack-bundle-analyzer") + .BundleAnalyzerPlugin; + config.plugin("BundleAnalyzerPlugin").use(BundleAnalyzerPlugin, []); + } }, css: { loaderOptions: {
--- a/cmd/wfs/dump.go Tue Jan 15 09:54:46 2019 +0100 +++ b/cmd/wfs/dump.go Tue Jan 15 10:07:10 2019 +0100 @@ -18,6 +18,7 @@ "errors" "fmt" "io" + "log" "os" "gemma.intevation.de/gemma/pkg/wfs" @@ -40,13 +41,19 @@ crsName = defaultCRS } fmt.Printf("CRS: %s\n", crsName) - types := map[string]struct{}{} + epsg, err := wfs.CRSToEPSG(crsName) + if err != nil { + log.Printf("error: %v\n", err) + } else { + fmt.Printf("EPSG: %d\n", epsg) + } + types := map[string]int{} for _, feature := range rfc.Features { - types[feature.Geometry.Type] = struct{}{} + types[feature.Geometry.Type]++ } fmt.Printf("found types in %d features:\n", len(rfc.Features)) - for typ := range types { - fmt.Printf("\t%s\n", typ) + for typ, cnt := range types { + fmt.Printf("\t%s: %d\n", typ, cnt) } return nil })
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/common/attributes.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,69 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package common + +import ( + "log" + "strconv" + "strings" + "time" +) + +// Attributes is a map of optional key/value attributes +// of a configuration. +type Attributes map[string]string + +// Get fetches a value for given key out of the configuration. +// If the key was not found the bool component of the return value +// return false. +func (ca Attributes) Get(key string) (string, bool) { + if ca == nil { + return "", false + } + value, found := ca[key] + return value, found +} + +// Bool returns a bool value for a given key. +func (ca Attributes) Bool(key string) bool { + s, found := ca.Get(key) + return found && strings.ToLower(s) == "true" +} + +// Time gives a time.Time for a given key. +func (ca Attributes) Time(key string) (time.Time, bool) { + s, found := ca.Get(key) + if !found { + return time.Time{}, false + } + t, err := time.Parse("2006-01-02T15:04:05", s) + if err != nil { + log.Printf("error: %v\n", err) + return time.Time{}, false + } + return t, true +} + +func (ca Attributes) Int(key string) (int, bool) { + s, found := ca.Get(key) + if !found { + return 0, false + } + i, err := strconv.Atoi(s) + if err != nil { + log.Printf("error: %v\n", err) + return 0, false + } + return i, true +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/controllers/agmimports.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,135 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package controllers + +import ( + "bufio" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "strconv" + "time" + + "gemma.intevation.de/gemma/pkg/auth" + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/config" + "gemma.intevation.de/gemma/pkg/imports" +) + +const ( + maxApprovedGaugeMeasurementSize = 25 * 1024 * 1024 + approvedGaugeMeasurementsName = "approvedgm" +) + +func storeApprovedGaugeMeasurements(req *http.Request) (string, error) { + + // Check for direct upload. + f, _, err := req.FormFile(approvedGaugeMeasurementsName) + if err != nil { + return "", err + } + defer f.Close() + + dir, err := ioutil.TempDir(config.TmpDir(), approvedGaugeMeasurementsName) + if err != nil { + return "", err + } + + o, err := os.Create(filepath.Join(dir, "agm.csv")) + if err != nil { + os.RemoveAll(dir) + return "", err + } + + out := bufio.NewWriter(o) + + if _, err = io.Copy(out, io.LimitReader(f, maxApprovedGaugeMeasurementSize)); err != nil { + o.Close() + os.RemoveAll(dir) + return "", err + } + + if err = out.Flush(); err != nil { + o.Close() + os.RemoveAll(dir) + return "", err + } + + return dir, nil +} + +func importApprovedGaugeMeasurements(rw http.ResponseWriter, req *http.Request) { + + dir, err := storeApprovedGaugeMeasurements(req) + if err != nil { + log.Printf("error: %v\n", err) + http.Error(rw, "error: "+err.Error(), http.StatusInternalServerError) + return + } + + agm := &imports.ApprovedGaugeMeasurements{Dir: dir} + + serialized, err := common.ToJSONString(agm) + if err != nil { + log.Printf("error: %v\n", err) + http.Error(rw, "error: "+err.Error(), http.StatusInternalServerError) + return + } + + session, _ := auth.GetSession(req) + + sendEmail := req.FormValue("email") != "" + + var due time.Time + if d := req.FormValue("due"); d != "" { + var err error + if due, err = time.Parse("2006-01-02T15:04:05", d); err != nil { + log.Printf("error: %v\n", err) + } + } + + retries := -1 + if r := req.FormValue("retries"); r != "" { + var err error + if retries, err = strconv.Atoi(r); err != nil { + log.Printf("error: %v\n", err) + retries = -1 + } + } + + jobID, err := imports.AddJob( + imports.AGMJobKind, + due, retries, + session.User, + sendEmail, + serialized) + + if err != nil { + log.Printf("error: %v\n", err) + http.Error(rw, "error: "+err.Error(), http.StatusInternalServerError) + return + } + + log.Printf("info: added import #%d to queue\n", jobID) + + result := struct { + ID int64 `json:"id"` + }{ + ID: jobID, + } + SendJSON(rw, http.StatusCreated, &result) +}
--- a/pkg/controllers/bnimports.go Tue Jan 15 09:54:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -// This is Free Software under GNU Affero General Public License v >= 3.0 -// without warranty, see README.md and license for details. -// -// SPDX-License-Identifier: AGPL-3.0-or-later -// License-Filename: LICENSES/AGPL-3.0.txt -// -// Copyright (C) 2018 by via donau -// – Österreichische Wasserstraßen-Gesellschaft mbH -// Software engineering by Intevation GmbH -// -// Author(s): -// * Sascha L. Teichmann <sascha.teichmann@intevation.de> - -package controllers - -import ( - "database/sql" - "log" - "net/http" - - "gemma.intevation.de/gemma/pkg/auth" - "gemma.intevation.de/gemma/pkg/common" - "gemma.intevation.de/gemma/pkg/imports" - "gemma.intevation.de/gemma/pkg/models" -) - -func importBottleneck( - input interface{}, - req *http.Request, - conn *sql.Conn, -) (jr JSONResult, err error) { - - bi := input.(*models.BottleneckImport) - - bn := &imports.Bottleneck{ - URL: bi.URL, - Insecure: bi.Insecure, - } - - var serialized string - serialized, err = common.ToJSONString(bn) - if err != nil { - return - } - - session, _ := auth.GetSession(req) - - jobID, err := imports.AddJob( - imports.BNJobKind, session.User, - false, false, - serialized) - - if err != nil { - return - } - - log.Printf("info: added import #%d to queue\n", jobID) - - result := struct { - ID int64 `json:"id"` - }{ - ID: jobID, - } - - jr = JSONResult{ - Code: http.StatusCreated, - Result: &result, - } - return -}
--- a/pkg/controllers/geostyling.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/geostyling.go Tue Jan 15 10:07:10 2019 +0100 @@ -73,7 +73,7 @@ return } - log.Printf("uploaded file length: %d\n", len(style)) + log.Printf("info: uploaded file length: %d\n", len(style)) if err := models.UpdateInternalStyle(req, feature, style); err != nil { log.Printf("error: %v\n", err)
--- a/pkg/controllers/gmimports.go Tue Jan 15 09:54:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -// This is Free Software under GNU Affero General Public License v >= 3.0 -// without warranty, see README.md and license for details. -// -// SPDX-License-Identifier: AGPL-3.0-or-later -// License-Filename: LICENSES/AGPL-3.0.txt -// -// Copyright (C) 2018 by via donau -// – Österreichische Wasserstraßen-Gesellschaft mbH -// Software engineering by Intevation GmbH -// -// Author(s): -// * Raimund Renkert <raimund.renkert@intevation.de> - -package controllers - -import ( - "database/sql" - "log" - "net/http" - - "gemma.intevation.de/gemma/pkg/auth" - "gemma.intevation.de/gemma/pkg/common" - "gemma.intevation.de/gemma/pkg/imports" - "gemma.intevation.de/gemma/pkg/models" -) - -func importGaugeMeasurement( - input interface{}, - req *http.Request, - conn *sql.Conn, -) (jr JSONResult, err error) { - - bi := input.(*models.GaugeMeasurementImport) - - bn := &imports.GaugeMeasurement{ - URL: bi.URL, - Insecure: bi.Insecure, - } - - var serialized string - serialized, err = common.ToJSONString(bn) - if err != nil { - return - } - - session, _ := auth.GetSession(req) - - jobID, err := imports.AddJob( - imports.GMJobKind, - session.User, - false, true, - serialized) - - if err != nil { - return - } - - log.Printf("info: added import #%d to queue\n", jobID) - - result := struct { - ID int64 `json:"id"` - }{ - ID: jobID, - } - - jr = JSONResult{ - Code: http.StatusCreated, - Result: &result, - } - return -}
--- a/pkg/controllers/importconfig.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/importconfig.go Tue Jan 15 10:07:10 2019 +0100 @@ -14,15 +14,18 @@ package controllers import ( + "context" "database/sql" "errors" "fmt" "net/http" + "sort" "strconv" "github.com/gorilla/mux" "gemma.intevation.de/gemma/pkg/auth" + "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/imports" "gemma.intevation.de/gemma/pkg/scheduler" ) @@ -34,7 +37,6 @@ username, kind, send_email, - auto_accept, cron, url FROM waterway.import_configuration` @@ -47,30 +49,66 @@ insertImportConfigurationSQL = ` INSERT INTO waterway.import_configuration -(username, kind, cron, send_email, auto_accept, url) -VALUES ($1, $2, $3, $4, $5, $6) +(username, kind, cron, send_email, url) +VALUES ($1, $2, $3, $4, $5) RETURNING id` + insertImportConfigurationAttributeSQL = ` +INSERT INTO waterway.import_configuration_attributes +(import_configuration_id, k, v) +VALUES ($1, $2, $3)` + hasImportConfigurationSQL = ` SELECT true FROM waterway.import_configuration WHERE id = $1` + deleteImportConfiguationAttributesSQL = ` +DELETE FROM waterway.import_configuration_attributes +WHERE import_configuration_id = $1` + deleteImportConfiguationSQL = ` DELETE FROM waterway.import_configuration WHERE id = $1` updateImportConfigurationSQL = ` UPDATE waterway.import_configuration SET - username = $2 + username = $2, kind = $3, cron = $4, url = $5, - send_email = $6, - auto_accept = $7 + send_email = $6 WHERE id = $1 ` ) +func runImportConfig( + _ interface{}, + req *http.Request, + conn *sql.Conn, +) (jr JSONResult, err error) { + + id, _ := strconv.ParseInt(mux.Vars(req)["id"], 10, 64) + + ctx := req.Context() + + var jobID int64 + if jobID, err = imports.RunConfiguredImportContext(ctx, conn, id); err != nil { + return + } + + var result = struct { + ID int64 `json:"id"` + }{ + ID: jobID, + } + + jr = JSONResult{ + Code: http.StatusCreated, + Result: &result, + } + return +} + func modifyImportConfig( input interface{}, req *http.Request, @@ -93,7 +131,7 @@ var ( entry imports.IDConfig kind string - cron sql.NullString + dummy sql.NullString url sql.NullString ) @@ -102,8 +140,7 @@ &entry.User, &kind, &entry.SendEMail, - &entry.AutoAccept, - &cron, + &dummy, &url, ) @@ -121,8 +158,10 @@ session, _ := auth.GetSession(req) entry.SendEMail = importConfig.SendEMail - entry.AutoAccept = importConfig.AutoAccept + // We always take the cron spec from the input. + // If there is no spec remove schedule. + var cron sql.NullString if importConfig.Cron != nil { cron = sql.NullString{String: string(*importConfig.Cron), Valid: true} } @@ -138,19 +177,26 @@ cron, url, importConfig.SendEMail, - importConfig.AutoAccept, ); err != nil { return } + if importConfig.Attributes != nil { + if _, err = tx.ExecContext(ctx, deleteImportConfiguationAttributesSQL, id); err != nil { + return + } + if err = storeConfigAttributes(ctx, tx, id, importConfig.Attributes); err != nil { + return + } + } + scheduler.UnbindByID(id) if cron.Valid { if err = scheduler.BindAction( string(importConfig.Kind), cron.String, - session.User, - &id, + id, ); err != nil { return } @@ -180,41 +226,18 @@ id, _ := strconv.ParseInt(mux.Vars(req)["id"], 10, 64) - var ( - entry imports.IDConfig - kind string - cron sql.NullString - url sql.NullString - ) + var entry *imports.IDConfig - err = conn.QueryRowContext(ctx, selectImportConfigurationIDSQL, id).Scan( - &entry.ID, - &entry.User, - &kind, - &entry.SendEMail, - &entry.AutoAccept, - &cron, - &url, - ) - + entry, err = imports.LoadIDConfigContext(ctx, conn, id) switch { - case err == sql.ErrNoRows: + case err != nil: + return + case entry == nil: err = JSONError{ Code: http.StatusNotFound, Message: fmt.Sprintf("No schedule %d found", id), } return - case err != nil: - return - } - - entry.Kind = imports.ImportKind(kind) - if cron.Valid { - cs := imports.CronSpec(cron.String) - entry.Cron = &cs - } - if url.Valid { - entry.URL = &url.String } jr = JSONResult{Result: &entry} @@ -253,6 +276,10 @@ return } + if _, err = tx.ExecContext(ctx, deleteImportConfiguationAttributesSQL, id); err != nil { + return + } + if _, err = tx.ExecContext(ctx, deleteImportConfiguationSQL, id); err != nil { return } @@ -274,6 +301,36 @@ return } +func storeConfigAttributes( + ctx context.Context, + tx *sql.Tx, + id int64, + attrs common.Attributes, +) error { + if len(attrs) == 0 { + return nil + } + attrStmt, err := tx.PrepareContext(ctx, insertImportConfigurationAttributeSQL) + if err != nil { + return err + } + defer attrStmt.Close() + // Sort to make it deterministic + keys := make([]string, len(attrs)) + i := 0 + for key := range attrs { + keys[i] = key + i++ + } + sort.Strings(keys) + for _, key := range keys { + if _, err := attrStmt.ExecContext(ctx, id, key, attrs[key]); err != nil { + return err + } + } + return nil +} + func addImportConfig( input interface{}, req *http.Request, @@ -310,19 +367,22 @@ string(importConfig.Kind), cron, importConfig.SendEMail, - importConfig.AutoAccept, url, ).Scan(&id); err != nil { return } + // Store extra attributes + if err = storeConfigAttributes(ctx, tx, id, importConfig.Attributes); err != nil { + return + } + // Need to start a scheduler job right away? if importConfig.Cron != nil { if err = scheduler.BindAction( string(importConfig.Kind), string(*importConfig.Cron), - session.User, - &id, + id, ); err != nil { return } @@ -330,6 +390,7 @@ if err = tx.Commit(); err != nil { scheduler.UnbindByID(id) + return } var result = struct { @@ -373,7 +434,6 @@ &entry.User, &kind, &entry.SendEMail, - &entry.AutoAccept, &cron, &url, ); err != nil {
--- a/pkg/controllers/importqueue.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/importqueue.go Tue Jan 15 10:07:10 2019 +0100 @@ -66,6 +66,25 @@ DELETE FROM waterway.imports WHERE id = $1` ) +func toInt8Array(txt string) *pgtype.Int8Array { + parts := strings.Split(txt, ",") + var ints []int64 + for _, part := range parts { + part = strings.TrimSpace(part) + v, err := strconv.ParseInt(part, 10, 64) + if err != nil { + continue + } + ints = append(ints, v) + } + var ia pgtype.Int8Array + if err := ia.Set(ints); err != nil { + log.Printf("warn: %v\n", err) + return nil + } + return &ia +} + func toTextArray(txt string, allowed []string) *pgtype.TextArray { parts := strings.Split(txt, ",") var accepted []string @@ -101,6 +120,7 @@ args []interface{} states *pgtype.TextArray kinds *pgtype.TextArray + ids *pgtype.Int8Array ) arg := func(format string, v interface{}) { @@ -116,8 +136,12 @@ kinds = toTextArray(ks, imports.ImportKindNames()) } + if idss := req.FormValue("ids"); idss != "" { + ids = toInt8Array(idss) + } + stmt.WriteString(selectImportsSQL) - if states != nil || kinds != nil { + if states != nil || kinds != nil || ids != nil { stmt.WriteString(" WHERE ") } @@ -125,7 +149,7 @@ arg(" state = ANY($%d) ", states) } - if states != nil && kinds != nil { + if states != nil && (kinds != nil || ids != nil) { stmt.WriteString("AND") } @@ -133,6 +157,14 @@ arg(" kind = ANY($%d) ", kinds) } + if (states != nil || kinds != nil) && ids != nil { + stmt.WriteString("AND") + } + + if ids != nil { + arg(" id = ANY($%d) ", ids) + } + stmt.WriteString(" ORDER BY enqueued DESC ") if lim := req.FormValue("limit"); lim != "" { @@ -411,12 +443,12 @@ err = tx.QueryRowContext(ctx, isPendingSQL, id).Scan(&pending, &kind) switch { case err == sql.ErrNoRows: - err = fmt.Errorf("Cannot find import #%d.", id) + err = fmt.Errorf("cannot find import #%d", id) return case err != nil: return case !pending: - err = fmt.Errorf("Import %d is not pending.", id) + err = fmt.Errorf("import %d is not pending", id) return }
--- a/pkg/controllers/json.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/json.go Tue Jan 15 10:07:10 2019 +0100 @@ -26,33 +26,71 @@ "gemma.intevation.de/gemma/pkg/auth" ) +// JSONResult defines the return type of JSONHandler handler function. type JSONResult struct { - Code int + // Code is the HTTP status code to be set which defaults to http.StatusOK (200). + Code int + // Result is serialized to JSON. + // If the type is an io.Reader its copied through. Result interface{} } +// JSONDefaultLimit is default size limit in bytes of an accepted +// input document. +const JSONDefaultLimit = 2048 + +// JSONHandler implements a middleware to ease the handing JSON input +// streams and return JSON documents as output. type JSONHandler struct { - Input func() interface{} - Handle func(interface{}, *http.Request, *sql.Conn) (JSONResult, error) + // Input (if not nil) is called to fill a data structure + // returned by this function. + Input func() interface{} + // Handle is called to handle the incoming HTTP request. + // in is the data structure returned by Input. Its nil if Input is nil. + // req is the incoming HTTP request. + // conn is the impersonated connection to the database. + Handle func(in interface{}, rep *http.Request, conn *sql.Conn) (JSONResult, error) + // NoConn if set to true no database connection is established and + // the conn parameter of the Handle call is nil. NoConn bool + // Limit overides the default size of accepted input documents. + // Set to a negative value to allow an arbitrary size. + // Handle with care! + Limit int64 } +// JSONError is an error if returned by the JSONHandler.Handle function +// which ends up encoded as a JSON document. type JSONError struct { - Code int + // Code is the HTTP status code of the result defaults + // to http.StatusInternalServerError if not set. + Code int + // The message of the error. Message string } +// Error implements the error interface. func (je JSONError) Error() string { return fmt.Sprintf("%d: %s", je.Code, je.Message) } +// ServeHTTP makes the JSONHandler a middleware. func (j *JSONHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { var input interface{} if j.Input != nil { input = j.Input() defer req.Body.Close() - if err := json.NewDecoder(req.Body).Decode(input); err != nil { + var r io.Reader + switch { + case j.Limit == 0: + r = io.LimitReader(req.Body, JSONDefaultLimit) + case j.Limit > 0: + r = io.LimitReader(req.Body, j.Limit) + default: + r = req.Body + } + if err := json.NewDecoder(r).Decode(input); err != nil { http.Error(rw, "error: "+err.Error(), http.StatusBadRequest) return } @@ -135,6 +173,8 @@ } } +// SendJSON sends data JSON encoded to the response writer +// with a given HTTP status code. func SendJSON(rw http.ResponseWriter, code int, data interface{}) { rw.Header().Set("Content-Type", "application/json") rw.WriteHeader(code)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/controllers/manualimports.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,132 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> +// * Raimund Renkert <raimund.renkert@intevation.de> + +package controllers + +import ( + "database/sql" + "log" + "net/http" + "time" + + "gemma.intevation.de/gemma/pkg/auth" + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/imports" + "gemma.intevation.de/gemma/pkg/models" +) + +func retry(a common.Attributes) (time.Time, int) { + due, _ := a.Time("due") + retries, ok := a.Int("retries") + if !ok { + retries = -1 + } + return due, retries +} + +func importBottleneck(input interface{}) (interface{}, time.Time, int, bool) { + bi := input.(*models.BottleneckImport) + bn := &imports.Bottleneck{ + URL: bi.URL, + Insecure: bi.Insecure, + } + due, retries := retry(bi.Attributes) + return bn, due, retries, bi.SendEmail +} + +func importGaugeMeasurement(input interface{}) (interface{}, time.Time, int, bool) { + gi := input.(*models.GaugeMeasurementImport) + gm := &imports.GaugeMeasurement{ + URL: gi.URL, + Insecure: gi.Insecure, + } + due, retries := retry(gi.Attributes) + return gm, due, retries, gi.SendEmail +} + +func importFairwayAvailability(input interface{}) (interface{}, time.Time, int, bool) { + fai := input.(*models.FairwayAvailabilityImport) + fa := &imports.FairwayAvailability{ + URL: fai.URL, + Insecure: fai.Insecure, + } + due, retries := retry(fai.Attributes) + return fa, due, retries, fai.SendEmail +} + +func importWaterwayAxis(input interface{}) (interface{}, time.Time, int, bool) { + wxi := input.(*models.WaterwayAxisImport) + wx := &imports.WaterwayAxis{ + URL: wxi.URL, + FeatureType: wxi.FeatureType, + SortBy: wxi.SortBy, + } + due, retries := retry(wxi.Attributes) + return wx, due, retries, wxi.SendEmail +} + +func importWaterwayArea(input interface{}) (interface{}, time.Time, int, bool) { + wai := input.(*models.WaterwayAreaImport) + wa := &imports.WaterwayArea{ + URL: wai.URL, + FeatureType: wai.FeatureType, + SortBy: wai.SortBy, + } + due, retries := retry(wai.Attributes) + return wa, due, retries, wai.SendEmail +} + +func manualImport( + kind imports.JobKind, + setup func(interface{}) (interface{}, time.Time, int, bool), +) func(interface{}, *http.Request, *sql.Conn) (JSONResult, error) { + + return func(input interface{}, req *http.Request, _ *sql.Conn) ( + jr JSONResult, err error) { + + what, due, retries, sendEmail := setup(input) + + var serialized string + if serialized, err = common.ToJSONString(what); err != nil { + return + } + + session, _ := auth.GetSession(req) + + var jobID int64 + if jobID, err = imports.AddJob( + kind, + due, retries, + session.User, + sendEmail, + serialized, + ); err != nil { + return + } + + log.Printf("info: added import #%d to queue\n", jobID) + + result := struct { + ID int64 `json:"id"` + }{ + ID: jobID, + } + + jr = JSONResult{ + Code: http.StatusCreated, + Result: &result, + } + return + } +}
--- a/pkg/controllers/proxy.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/proxy.go Tue Jan 15 10:07:10 2019 +0100 @@ -118,7 +118,7 @@ ) { switch enc := h.Get("Content-Encoding"); { case strings.Contains(enc, "gzip"): - log.Println("gzip compression") + log.Println("info: gzip compression") return func(r io.Reader) (io.ReadCloser, error) { return gzip.NewReader(r) }, @@ -126,7 +126,7 @@ return gzip.NewWriter(w), nil } case strings.Contains(enc, "deflate"): - log.Println("Deflate compression") + log.Println("info: deflate compression") return func(r io.Reader) (io.ReadCloser, error) { return flate.NewReader(r), nil }, @@ -134,7 +134,7 @@ return flate.NewWriter(w, flate.DefaultCompression) } default: - log.Println("No content compression") + log.Println("info: no content compression") return func(r io.Reader) (io.ReadCloser, error) { if r2, ok := r.(io.ReadCloser); ok { return r2, nil @@ -183,13 +183,12 @@ w.Close() pw.Close() force.Close() - log.Printf("rewrite took %s\n", time.Since(start)) + log.Printf("info: rewrite took %s\n", time.Since(start)) }() if err := rewrite(suffix, w, r); err != nil { - log.Printf("rewrite failed: %v\n", err) + log.Printf("error: rewrite failed: %v\n", err) return } - log.Println("rewrite successful") }(resp.Body) resp.Body = pr
--- a/pkg/controllers/routes.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/routes.go Tue Jan 15 10:07:10 2019 +0100 @@ -26,6 +26,7 @@ "gemma.intevation.de/gemma/pkg/models" ) +// BindRoutes binds all the API endpoints to the exposed router. func BindRoutes(m *mux.Router) { api := m.PathPrefix("/api").Subrouter() @@ -171,19 +172,48 @@ api.Handle("/imports/soundingresult", waterwayAdmin( http.HandlerFunc(importSoundingResult))).Methods(http.MethodPost) + api.Handle("/imports/approvedgm", waterwayAdmin( + http.HandlerFunc(importApprovedGaugeMeasurements))).Methods(http.MethodPost) + api.Handle("/imports/bottleneck", waterwayAdmin(&JSONHandler{ Input: func() interface{} { return new(models.BottleneckImport) }, - Handle: importBottleneck, + Handle: manualImport(imports.BNJobKind, importBottleneck), + NoConn: true, })).Methods(http.MethodPost) api.Handle("/imports/gaugemeasurement", waterwayAdmin(&JSONHandler{ Input: func() interface{} { return new(models.GaugeMeasurementImport) }, - Handle: importGaugeMeasurement, + Handle: manualImport(imports.GMJobKind, importGaugeMeasurement), + NoConn: true, + })).Methods(http.MethodPost) + + api.Handle("/imports/fairwayavailability", waterwayAdmin(&JSONHandler{ + Input: func() interface{} { return new(models.FairwayAvailabilityImport) }, + Handle: manualImport(imports.FAJobKind, importFairwayAvailability), + NoConn: true, + })).Methods(http.MethodPost) + + api.Handle("/imports/waterwayaxis", waterwayAdmin(&JSONHandler{ + Input: func() interface{} { return new(models.WaterwayAxisImport) }, + Handle: manualImport(imports.WXJobKind, importWaterwayAxis), + NoConn: true, + })).Methods(http.MethodPost) + + api.Handle("/imports/waterwayarea", waterwayAdmin(&JSONHandler{ + Input: func() interface{} { return new(models.WaterwayAreaImport) }, + Handle: manualImport(imports.WAJobKind, importWaterwayArea), + NoConn: true, })).Methods(http.MethodPost) // Import scheduler configuration + api.Handle("/imports/config/{id:[0-9]+}/run", + waterwayAdmin(&JSONHandler{ + Handle: runImportConfig, + })).Methods(http.MethodGet) + api.Handle("/imports/config/{id:[0-9]+}", waterwayAdmin(&JSONHandler{ + Input: func() interface{} { return new(imports.Config) }, Handle: modifyImportConfig, })).Methods(http.MethodPatch)
--- a/pkg/controllers/srimports.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/srimports.go Tue Jan 15 10:07:10 2019 +0100 @@ -161,10 +161,30 @@ session, _ := auth.GetSession(req) + sendEmail := req.FormValue("email") != "" + + var due time.Time + if d := req.FormValue("due"); d != "" { + var err error + if due, err = time.Parse("2006-01-02T15:04:05", d); err != nil { + log.Printf("error: %v\n", err) + } + } + + retries := -1 + if r := req.FormValue("retries"); r != "" { + var err error + if retries, err = strconv.Atoi(r); err != nil { + log.Printf("error: %v\n", err) + retries = -1 + } + } + jobID, err := imports.AddJob( imports.SRJobKind, + due, retries, session.User, - false, false, + sendEmail, serialized) if err != nil {
--- a/pkg/controllers/user.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/controllers/user.go Tue Jan 15 10:07:10 2019 +0100 @@ -113,9 +113,21 @@ return } + ctx := req.Context() + + // Remove scheduled tasks. + ids, err2 := scheduler.ScheduledUserIDs(ctx, db, user) + if err2 == nil { + if len(ids) > 0 { + go func() { scheduler.UnbindByIDs(ids) }() + } + } else { + log.Printf("error: %v\n", err2) + } + var res sql.Result - if res, err = db.ExecContext(req.Context(), deleteUserSQL, user); err != nil { + if res, err = db.ExecContext(ctx, deleteUserSQL, user); err != nil { return } @@ -129,7 +141,6 @@ // Running in a go routine should not be necessary. go func() { auth.Sessions.Logout(user) }() - go func() { scheduler.UnbindUser(user) }() jr = JSONResult{Code: http.StatusNoContent} return @@ -207,7 +218,6 @@ if user != newUser.User { // Running in a go routine should not be necessary. go func() { auth.Sessions.Logout(string(user)) }() - go func() { scheduler.UnbindUser(string(user)) }() } jr = JSONResult{
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/agm.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,327 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "bufio" + "context" + "database/sql" + "encoding/csv" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/models" +) + +type ApprovedGaugeMeasurements struct { + Dir string `json:"dir"` +} + +// GMAPJobKind is the unique name of an approved gauge measurements import job. +const AGMJobKind JobKind = "agm" + +type agmJobCreator struct{} + +func init() { + RegisterJobCreator(AGMJobKind, agmJobCreator{}) +} + +func (agmJobCreator) AutoAccept() bool { return false } + +func (agmJobCreator) Description() string { + return "approved gauge measurements" +} + +func (agmJobCreator) Create(_ JobKind, data string) (Job, error) { + agm := new(ApprovedGaugeMeasurements) + if err := common.FromJSONString(data, agm); err != nil { + return nil, err + } + return agm, nil +} + +func (agmJobCreator) Depends() []string { + return []string{ + "gauges", + "gauge_measurements", + } +} + +const ( + // delete the old and keep the new measures. + agmStageDoneDeleteSQL = ` +WITH staged AS ( + SELECT key + FROM waterway.track_imports + WHERE import_id = $1 AND + relation = 'waterway.gauge_measurements'::regclass +), +to_delete AS ( + SELECT o.id AS id + FROM waterway.gauge_measurements o + JOIN waterway.gauge_measurements n + ON n.fk_gauge_id = o.fk_gauge_id AND n.measure_date = o.measure_date + WHERE n.id IN (SELECT key FROM staged) + AND o.id NOT IN (SELECT key FROM staged) +) +DELETE FROM waterway.gauge_measurements WHERE id IN (SELECT id from to_delete)` + + agmStageDoneSQL = ` +UPDATE waterway.gauge_measurements SET staging_done = true +WHERE id IN ( + SELECT key FROM waterway.track_imports + WHERE import_id = $1 AND + relation = 'waterway.gauge_measurements'::regclass)` +) + +func (agmJobCreator) StageDone( + ctx context.Context, + tx *sql.Tx, + id int64, +) error { + _, err := tx.ExecContext(ctx, agmStageDoneDeleteSQL, id) + if err == nil { + _, err = tx.ExecContext(ctx, agmStageDoneSQL, id) + } + return err +} + +// CleanUp removes the folder containing the CSV file with the +// the approved gauge measurements. +func (agm *ApprovedGaugeMeasurements) CleanUp() error { + return os.RemoveAll(agm.Dir) +} + +func guessDate(s string) (time.Time, error) { + var err error + var t time.Time + for _, layout := range [...]string{ + "02.01.2006 15:04", + "2006-01-02T15:04:05-07:00", + } { + if t, err = time.Parse(layout, s); err == nil { + break + } + } + return t, err +} + +// Do executes the actual approved gauge measurements import. +func (agm *ApprovedGaugeMeasurements) Do( + ctx context.Context, + importID int64, + conn *sql.Conn, + feedback Feedback, +) (interface{}, error) { + + start := time.Now() + + f, err := os.Open(filepath.Join(agm.Dir, "agm.csv")) + if err != nil { + return nil, err + } + defer f.Close() + + r := csv.NewReader(bufio.NewReader(f)) + r.Comma = ';' + r.ReuseRecord = true + + headers, err := r.Read() + if err != nil { + return nil, err + } + + headerIndices := map[string]int{} + + for i, f := range headers { + headerIndices[strings.Replace( + strings.ToLower( + strings.TrimSpace(f)), " ", "_", -1)] = i + } + + var missing []string + + for _, m := range [...]string{ + "fk_gauge_id", + "measure_date", + "from", // "sender", + "language_code", + "country_code", + "date_issue", + "reference_code", + "value", // "water_level", + "predicted", + // "is_waterlevel", + "value_min", + "value_max", + "date_info", + "originator", // "source_organization", + } { + if _, found := headerIndices[m]; !found { + missing = append(missing, m) + } + } + + if len(missing) > 0 { + return nil, fmt.Errorf("Missing columns: %s", strings.Join(missing, ", ")) + } + + inCm, _ := rescale("cm") + scaler := func(row []string) (func(float32) float32, error) { + idx, found := headerIndices["unit"] + if !found { + return inCm, nil + } + unit := row[idx] + if unit == "cm" { + return inCm, nil + } + s, err := rescale(unit) + return s, err + } + + tx, err := conn.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + + insertStmt, err := tx.PrepareContext(ctx, insertGMSQL) + if err != nil { + return nil, err + } + defer insertStmt.Close() + trackStmt, err := tx.PrepareContext(ctx, trackImportSQL) + if err != nil { + return nil, err + } + defer trackStmt.Close() + + ids := []int64{} + + args := make([]interface{}, 19) + + args[18] = false // staging_done + +lines: + for line := 1; ; line++ { + + row, err := r.Read() + switch { + case err == io.EOF || len(row) == 0: + break lines + case err != nil: + return nil, fmt.Errorf("CSV parsing failed: %v", err) + } + convert, err := scaler(row) + if err != nil { + return nil, fmt.Errorf("line %d: %v", line, err) + } + + gids := row[headerIndices["fk_gauge_id"]] + gid, err := models.IsrsFromString(gids) + if err != nil { + return nil, fmt.Errorf("Invalid ISRS code line %d: %v", line, err) + } + + args[0] = gid.CountryCode + args[1] = gid.LoCode + args[2] = gid.FairwaySection + args[3] = gid.Orc + args[4] = gid.Hectometre + + md, err := guessDate(row[headerIndices["measure_date"]]) + if err != nil { + return nil, fmt.Errorf("Invalid 'measure_date' line %d: %v", line, err) + } + args[5] = md + + args[6] = row[headerIndices["from"]] + args[7] = row[headerIndices["language_code"]] + args[8] = row[headerIndices["country_code"]] + + dis, err := guessDate(row[headerIndices["date_issue"]]) + if err != nil { + return nil, fmt.Errorf("Invalid 'date_issue' line %d: %v", line, err) + } + args[9] = dis + + args[10] = row[headerIndices["reference_code"]] + + value, err := strconv.ParseFloat(row[headerIndices["value"]], 32) + if err != nil { + return nil, fmt.Errorf("Invalid 'value' line %d: %v", line, err) + } + args[11] = convert(float32(value)) + + predicted := strings.ToLower(row[headerIndices["predicted"]]) == "true" + args[12] = predicted + + args[13] = true // is_waterlevel + + valueMin, err := strconv.ParseFloat(row[headerIndices["value_min"]], 32) + if err != nil { + return nil, fmt.Errorf("Invalid 'value_min' line %d: %v", line, err) + } + args[14] = convert(float32(valueMin)) + + valueMax, err := strconv.ParseFloat(row[headerIndices["value_max"]], 32) + if err != nil { + return nil, fmt.Errorf("Invalid 'value_max' line %d: %v", line, err) + } + args[15] = convert(float32(valueMax)) + + din, err := guessDate(row[headerIndices["date_info"]]) + if err != nil { + return nil, fmt.Errorf("Invalid 'date_info' line %d: %v", line, err) + } + args[16] = din + + args[17] = row[headerIndices["originator"]] + + // args[18] (staging_done) is set to true outside the loop. + + var id int64 + if err := insertStmt.QueryRowContext(ctx, args...).Scan(&id); err != nil { + return nil, fmt.Errorf("Failed to insert line %d: %v", line, err) + } + ids = append(ids, id) + + if _, err := trackStmt.ExecContext( + ctx, importID, "waterway.gauge_measurements", id, + ); err != nil { + return nil, err + } + } + + if err := tx.Commit(); err != nil { + return nil, fmt.Errorf("Commit failed: %v", err) + } + + feedback.Info("Importing approved gauge measurements took %s", + time.Since(start)) + + summary := struct { + IDs []int64 `json:"ids"` + }{ + IDs: ids, + } + return &summary, nil +}
--- a/pkg/imports/bn.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/imports/bn.go Tue Jan 15 10:07:10 2019 +0100 @@ -25,18 +25,24 @@ "gemma.intevation.de/gemma/pkg/soap/ifbn" ) +// Bottleneck is an import job to import +// bottlenecks from an IFBN SOAP service. type Bottleneck struct { - URL string `json:"url"` - Insecure bool `json:"insecure"` + // URL is the URL of the SOAP service. + URL string `json:"url"` + // Insecure indicates if HTTPS traffic + // should validate certificates or not. + Insecure bool `json:"insecure"` } +// BNJobKind is the import queue type identifier. const BNJobKind JobKind = "bn" const ( hasBottleneckSQL = ` SELECT true FROM waterway.bottlenecks WHERE bottleneck_id = $1` - insertSQL = ` + insertBottleneckSQL = ` INSERT INTO waterway.bottlenecks ( bottleneck_id, fk_g_fid, @@ -79,6 +85,10 @@ RegisterJobCreator(BNJobKind, bnJobCreator{}) } +func (bnJobCreator) Description() string { return "bottlenecks" } + +func (bnJobCreator) AutoAccept() bool { return false } + func (bnJobCreator) Create(_ JobKind, data string) (Job, error) { bn := new(Bottleneck) if err := common.FromJSONString(data, bn); err != nil { @@ -89,13 +99,12 @@ func (bnJobCreator) Depends() []string { return []string{ - "waterway.gauges", - "waterway.bottlenecks", + "gauges", + "bottlenecks", } } const ( - // TODO: waterway.bottlenecks needs an integer id column. bnStageDoneSQL = ` UPDATE waterway.bottlenecks SET staging_done = true WHERE id IN ( @@ -115,7 +124,7 @@ } // CleanUp of a bottleneck import is a NOP. -func (bn *Bottleneck) CleanUp() error { return nil } +func (*Bottleneck) CleanUp() error { return nil } var rblbRe = regexp.MustCompile(`(..)_(..)`) @@ -174,7 +183,7 @@ stmt **sql.Stmt }{ {hasBottleneckSQL, &hasStmt}, - {insertSQL, &insertStmt}, + {insertBottleneckSQL, &insertStmt}, {trackImportSQL, &trackStmt}, } { var err error @@ -241,7 +250,7 @@ ); err != nil { return nil, err } - feedback.Info("Inserted '%s'into database", bn.OBJNAM) + feedback.Info("Inserted '%s' into database", bn.OBJNAM) } if len(nids) == 0 { feedback.Error("No new bottlenecks found") @@ -253,7 +262,6 @@ feedback.Info("Import of bottlenecks was successful") } - // TODO: needs to be filled more useful. summary := struct { Bottlenecks []string `json:"bottlenecks"` }{
--- a/pkg/imports/config.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/imports/config.go Tue Jan 15 10:07:10 2019 +0100 @@ -14,35 +14,58 @@ package imports import ( + "context" + "database/sql" "encoding/json" "fmt" "github.com/robfig/cron" + + "gemma.intevation.de/gemma/pkg/auth" + "gemma.intevation.de/gemma/pkg/common" ) type ( - CronSpec string + // CronSpec is a string containing a cron line. + CronSpec string + + // ImportKind is a string which has to be one + // of the registered import types. ImportKind string + // Config is JSON serialized form of a import configuration. Config struct { - Kind ImportKind `json:"kind"` - SendEMail bool `json:"send-email"` - AutoAccept bool `json:"auto-accept"` - Cron *CronSpec `json:"cron"` - URL *string `json:"url"` + // Kind is the import type. + Kind ImportKind `json:"kind"` + // SendEMail indicates if a mail should be be send + // when the import was changed to states + // 'pending' or 'failed'. + SendEMail bool `json:"send-email"` + // Cron is the cron schedule + // of this configuration if this value is not + // nil. If nil the import is not scheduled. + Cron *CronSpec `json:"cron"` + // URL is an optional URL used by the import. + URL *string `json:"url"` + // Attributes are optional key/value pairs for a configuration. + Attributes common.Attributes `json:"attributes,omitempty"` } + // IDConfig is the same as Config with an ID. + // Mainly used for server delivered configurations. IDConfig struct { - ID int64 `json:"id"` - User string `json:"user"` - Kind ImportKind `json:"kind"` - SendEMail bool `json:"send-email"` - AutoAccept bool `json:"auto-accept"` - Cron *CronSpec `json:"cron,omitempty"` - URL *string `json:"url,omitempty"` + ID int64 `json:"id"` + User string `json:"user"` + Kind ImportKind `json:"kind"` + SendEMail bool `json:"send-email"` + Cron *CronSpec `json:"cron,omitempty"` + URL *string `json:"url,omitempty"` + Attributes common.Attributes `json:"attributes,omitempty"` } ) +// UnmarshalJSON checks if the incoming string +// is a registered import type. func (ik *ImportKind) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { @@ -58,6 +81,8 @@ return nil } +// UnmarshalJSON checks if the incoming string is +// a valid cron line. func (cs *CronSpec) UnmarshalJSON(data []byte) error { var spec string if err := json.Unmarshal(data, &spec); err != nil { @@ -67,5 +92,94 @@ return err } *cs = CronSpec(spec) + return nil } + +const ( + configUser = "sys_admin" + + loadConfigSQL = ` +SELECT + username, + kind, + send_email, + cron, + url +FROM waterway.import_configuration +WHERE id = $1` + + loadConfigAttributesSQL = ` +SELECT k, v +FROM waterway.import_configuration_attributes +WHERE import_configuration_id = $1` +) + +// LoadIDConfigContext loads an import configuration from database. +func LoadIDConfigContext(ctx context.Context, conn *sql.Conn, id int64) (*IDConfig, error) { + + cfg := &IDConfig{ID: id} + var kind ImportKind + var cron, url sql.NullString + err := conn.QueryRowContext(ctx, loadConfigSQL, id).Scan( + &cfg.User, + &kind, + &cfg.SendEMail, + &cron, + &url, + ) + + switch { + case err == sql.ErrNoRows: + return nil, nil + case err != nil: + return nil, err + } + + cfg.Kind = ImportKind(kind) + if cron.Valid { + c := CronSpec(cron.String) + cfg.Cron = &c + } + if url.Valid { + cfg.URL = &url.String + } + // load the extra attributes. + rows, err := conn.QueryContext(ctx, loadConfigAttributesSQL, id) + if err != nil { + return nil, err + } + defer rows.Close() + var attributes common.Attributes + for rows.Next() { + var k, v string + if err = rows.Scan(&k, &v); err != nil { + return nil, err + } + if attributes == nil { + attributes = common.Attributes{} + } + attributes[k] = v + } + if err = rows.Err(); err != nil { + return nil, err + } + if len(attributes) > 0 { + cfg.Attributes = attributes + } + return cfg, nil +} + +func loadIDConfig(id int64) (*IDConfig, error) { + return loadIDConfigContext(context.Background(), id) +} + +func loadIDConfigContext(ctx context.Context, id int64) (*IDConfig, error) { + var cfg *IDConfig + err := auth.RunAs(ctx, configUser, func(conn *sql.Conn) error { + var err error + cfg, err = LoadIDConfigContext(ctx, conn, id) + return err + }) + return cfg, err +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/email.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,91 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "context" + "database/sql" + "log" + "strings" + "text/template" + + "gemma.intevation.de/gemma/pkg/auth" + "gemma.intevation.de/gemma/pkg/config" + "gemma.intevation.de/gemma/pkg/misc" +) + +const ( + selectEmailSQL = `SELECT email_address FROM users.list_users WHERE username = $1` + + importNotificationMailSubject = `import notification mail` +) + +var ( + importNotificationMailTmpl = template.Must( + template.New("notification").Parse(`Dear {{ .User }}, + +a {{ .Description }} import on server {{ .Server }} triggered +this email notification. + +{{ if eq .State "accepted" }}The imported data were successfully integrated into the database.{{ end -}} +{{ if eq .State "failed" }}The import failed for some reasons.{{ end -}} +{{ if eq .State "pending" }}The imported data could be integrated into the database +but your final decision is needed.{{ end }} + +Please follow this link to have a closer look at the details: + +{{ .Server }}/#/?{{ if eq .State "pending" }}review{{ else }}importlog{{ end }}={{ .ID }} + +Best regards + Your service team`)) +) + +func sendNotificationMail(user, description, state string, id int64) { + config.WaitReady() + + ctx := context.Background() + var email string + if err := auth.RunAs(ctx, user, + func(conn *sql.Conn) error { + return conn.QueryRowContext(ctx, selectEmailSQL, user).Scan(&email) + }, + ); err != nil { + log.Printf("error: %v\n", err) + return + } + + data := struct { + User string + Description string + Server string + State string + ID int64 + }{ + User: user, + Description: description, + Server: config.ExternalURL(), + State: state, + ID: id, + } + + var body strings.Builder + if err := importNotificationMailTmpl.Execute(&body, &data); err != nil { + log.Printf("error: %v\n", err) + return + } + + if err := misc.SendMail(email, importNotificationMailSubject, body.String()); err != nil { + log.Printf("error: %v\n", err) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/fa.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,460 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Raimund Renkert <raimund.renkert@intevation.de> + +package imports + +import ( + "context" + "database/sql" + "errors" + "time" + + "github.com/jackc/pgx/pgtype" + + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/models" + "gemma.intevation.de/gemma/pkg/soap/ifaf" +) + +// FairwayAvailability imports fairway availability data +// from an IFAF SOAP service. +type FairwayAvailability struct { + // URL is the URL of the SOAP service. + URL string `json:"url"` + // Insecure indicates if HTTPS traffic + // should validate certificates or not. + Insecure bool `json:"insecure"` +} + +// FAJobKind is import queue type identifier. +const FAJobKind JobKind = "fa" + +const ( + listBottlenecksSQL = ` +SELECT + bottleneck_id, + responsible_country +FROM waterway.bottlenecks +WHERE responsible_country = users.current_user_country() + AND staging_done = true +` + latestMeasureDateSQL = ` +SELECT + measure_date +FROM waterway.effective_fairway_availability +ORDER BY measure_date DESC LIMIT 1 +` + listFairwayAvailabilitySQL = ` +SELECT + fa.id, + bn.bottleneck_id, + fa.surdat +FROM waterway.fairway_availability fa +JOIN waterway.bottlenecks bn ON bn.id = fa.bottleneck_id +` + insertFASQL = ` +INSERT INTO waterway.fairway_availability ( + position_code, + bottleneck_id, + surdat, + critical, + date_info, + source_organization +) VALUES ( + $1, + (SELECT id FROM waterway.bottlenecks WHERE bottleneck_id = $2), + $3, + $4, + $5, + $6 +) +RETURNING id` + + insertBnPdfsSQL = ` +INSERT INTO waterway.bottleneck_pdfs ( + fairway_availability_id, + profile_pdf_filename, + profile_pdf_url, + pdf_generation_date, + source_organization +) VALUES ( + $1, + $2, + $3, + $4, + $5 +) ON CONFLICT ON CONSTRAINT bottleneck_pdfs_pkey DO NOTHING` + insertEFASQL = ` +INSERT INTO waterway.effective_fairway_availability ( + fairway_availability_id, + measure_date, + level_of_service, + available_depth_value, + available_width_value, + water_level_value, + measure_type, + source_organization, + forecast_generation_time, + value_lifetime +) VALUES ( + $1, + $2, + (SELECT + level_of_service + FROM levels_of_service + WHERE name = $3), + $4, + $5, + $6, + $7, + $8, + $9, + $10 +) ON CONFLICT ON CONSTRAINT effective_fairway_availability_pkey DO NOTHING` + insertFAVSQL = ` +INSERT INTO waterway.fa_reference_values ( + fairway_availability_id, + level_of_service, + fairway_depth, + fairway_width, + fairway_radius, + shallowest_spot +) VALUES ( + $1, + (SELECT + level_of_service + FROM levels_of_service + WHERE name = $2), + $3, + $4, + $5, + ST_MakePoint($6, $7)::geography +)ON CONFLICT ON CONSTRAINT fa_reference_values_pkey DO NOTHING` +) + +type faJobCreator struct{} + +func init() { + RegisterJobCreator(FAJobKind, faJobCreator{}) +} + +func (faJobCreator) Description() string { + return "fairway availability" +} + +func (faJobCreator) Create(_ JobKind, data string) (Job, error) { + fa := new(FairwayAvailability) + if err := common.FromJSONString(data, fa); err != nil { + return nil, err + } + return fa, nil +} + +func (faJobCreator) Depends() []string { + return []string{ + "bottlenecks", + "fairway_availability", + "bottleneck_pdfs", + "effective_fairway_availability", + "fa_reference_values", + "levels_of_service", + } +} + +func (faJobCreator) AutoAccept() bool { return true } + +// StageDone moves the imported fairway availablities out of the staging area. +// Currently doing nothing. +func (faJobCreator) StageDone(context.Context, *sql.Tx, int64) error { + return nil +} + +// CleanUp of a fairway availablities import is a NOP. +func (*FairwayAvailability) CleanUp() error { return nil } + +// Do executes the actual fairway availability import. +func (fa *FairwayAvailability) Do( + ctx context.Context, + importID int64, + conn *sql.Conn, + feedback Feedback, +) (interface{}, error) { + + // Get available bottlenecks from database for use as filter in SOAP request + var rows *sql.Rows + + rows, err := conn.QueryContext(ctx, listBottlenecksSQL) + if err != nil { + return nil, err + } + defer rows.Close() + + bottlenecks := []models.Bottleneck{} + + for rows.Next() { + var bn models.Bottleneck + if err = rows.Scan( + &bn.ID, + &bn.ResponsibleCountry, + ); err != nil { + return nil, err + } + bottlenecks = append(bottlenecks, bn) + } + if err = rows.Err(); err != nil { + return nil, err + } + + var faRows *sql.Rows + faRows, err = conn.QueryContext(ctx, listFairwayAvailabilitySQL) + if err != nil { + return nil, err + } + fairwayAvailabilities := map[models.UniqueFairwayAvailability]int64{} + for faRows.Next() { + var id int64 + var bnId string + var sd time.Time + if err = faRows.Scan( + &id, + &bnId, + &sd, + ); err != nil { + return nil, err + } + key := models.UniqueFairwayAvailability{ + BottleneckId: bnId, + Surdat: sd, + } + fairwayAvailabilities[key] = id + } + if err = faRows.Err(); err != nil { + return nil, err + } + + var latestDate pgtype.Timestamp + err = conn.QueryRowContext(ctx, latestMeasureDateSQL).Scan(&latestDate) + switch { + case err == sql.ErrNoRows: + latestDate = pgtype.Timestamp{ + // Fill Database with data of the last 5 days. Change this to a more useful value. + Time: time.Now().AddDate(0, 0, -5), + } + case err != nil: + return nil, err + } + + faids, err := fa.doForFAs(ctx, bottlenecks, fairwayAvailabilities, latestDate, conn, feedback) + if err != nil { + feedback.Error("Error processing data: %s", err) + } + if len(faids) == 0 { + feedback.Info("No new fairway availablity data found") + return nil, nil + } + feedback.Info("Processed %d fairway availabilities", len(faids)) + // TODO: needs to be filled more useful. + summary := struct { + FairwayAvailabilities []string `json:"fairwayAvailabilities"` + }{ + FairwayAvailabilities: faids, + } + return &summary, err +} + +func (fa *FairwayAvailability) doForFAs( + ctx context.Context, + bottlenecks []models.Bottleneck, + fairwayAvailabilities map[models.UniqueFairwayAvailability]int64, + latestDate pgtype.Timestamp, + conn *sql.Conn, + feedback Feedback, +) ([]string, error) { + start := time.Now() + + client := ifaf.NewFairwayAvailabilityService(fa.URL, fa.Insecure, nil) + + var bnIds []string + for _, bn := range bottlenecks { + bnIds = append(bnIds, bn.ID) + } + var period ifaf.RequestedPeriod + period.Date_start = latestDate.Time + period.Date_end = time.Now() + + ids := ifaf.ArrayOfString{ + String: bnIds, + } + + req := &ifaf.Get_bottleneck_fa{ + Bottleneck_id: &ids, + Period: &period, + } + resp, err := client.Get_bottleneck_fa(req) + if err != nil { + feedback.Error("%v", err) + return nil, err + } + + if resp.Get_bottleneck_faResult == nil { + err := errors.New("no fairway availabilities found") + return nil, err + } + + result := resp.Get_bottleneck_faResult + + tx, err := conn.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + + insertFAStmt, err := tx.PrepareContext(ctx, insertFASQL) + if err != nil { + return nil, err + } + defer insertFAStmt.Close() + insertBnPdfsStmt, err := tx.PrepareContext(ctx, insertBnPdfsSQL) + if err != nil { + return nil, err + } + defer insertBnPdfsStmt.Close() + insertEFAStmt, err := tx.PrepareContext(ctx, insertEFASQL) + if err != nil { + return nil, err + } + defer insertEFAStmt.Close() + insertFAVStmt, err := tx.PrepareContext(ctx, insertFAVSQL) + if err != nil { + return nil, err + } + defer insertFAVStmt.Close() + + var faIDs []string + var faID int64 + feedback.Info("Found %d fairway availabilities", len(result.FairwayAvailability)) + for _, faRes := range result.FairwayAvailability { + uniqueFa := models.UniqueFairwayAvailability{ + BottleneckId: faRes.Bottleneck_id, + Surdat: faRes.SURDAT, + } + var found bool + if faID, found = fairwayAvailabilities[uniqueFa]; !found { + err = insertFAStmt.QueryRowContext( + ctx, + faRes.POSITION, + faRes.Bottleneck_id, + faRes.SURDAT, + faRes.Critical, + faRes.Date_Info, + faRes.Source, + ).Scan(&faID) + if err != nil { + return nil, err + } + fairwayAvailabilities[uniqueFa] = faID + } + feedback.Info("Processing for Bottleneck %s", faRes.Bottleneck_id) + faIDs = append(faIDs, faRes.Bottleneck_id) + if faRes.Bottleneck_PDFs != nil { + bnPdfCount := 0 + for _, bnPdfs := range faRes.Bottleneck_PDFs.PdfInfo { + res, err := insertBnPdfsStmt.ExecContext( + ctx, + faID, + bnPdfs.ProfilePdfFilename, + bnPdfs.ProfilePdfURL, + bnPdfs.PDF_Generation_Date, + bnPdfs.Source, + ) + if err != nil { + return nil, err + } + affected, err := res.RowsAffected() + if err == nil { + bnPdfCount += int(affected) + } else { + bnPdfCount++ + } + } + feedback.Info("Add %d Pdfs", bnPdfCount) + } + if faRes.Effective_fairway_availability != nil { + efaCount := 0 + for _, efa := range faRes.Effective_fairway_availability.EffectiveFairwayAvailability { + los := efa.Level_of_Service + fgt := efa.Forecast_generation_time + if efa.Forecast_generation_time.Status == pgtype.Undefined { + fgt = pgtype.Timestamp{ + Status: pgtype.Null, + } + } + res, err := insertEFAStmt.ExecContext( + ctx, + faID, + efa.Measure_date, + string(*los), + efa.Available_depth_value, + efa.Available_width_value, + efa.Water_level_value, + efa.Measure_type, + efa.Source, + fgt.Get(), + efa.Value_lifetime, + ) + if err != nil { + return nil, err + } + affected, err := res.RowsAffected() + if err == nil { + efaCount += int(affected) + } else { + efaCount++ + } + } + feedback.Info("Add %d Effective Fairway Availability", efaCount) + } + + if faRes.Reference_values != nil { + rvCount := 0 + for _, fav := range faRes.Reference_values.ReferenceValue { + res, err := insertFAVStmt.ExecContext( + ctx, + faID, + fav.Level_of_Service, + fav.Fairway_depth, + fav.Fairway_width, + fav.Fairway_radius, + fav.Shallowest_spot_Lat, + fav.Shallowest_spot_Lon, + ) + if err != nil { + return nil, err + } + affected, err := res.RowsAffected() + if err == nil { + rvCount += int(affected) + } else { + rvCount++ + } + } + feedback.Info("Add %d Reference Values", rvCount) + } + } + feedback.Info("Storing fairway availabilities took %s", time.Since(start)) + if err = tx.Commit(); err == nil { + feedback.Info("Import of fairway availabilities was successful") + } + + return faIDs, nil +}
--- a/pkg/imports/fwsched.go Tue Jan 15 09:54:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -// This is Free Software under GNU Affero General Public License v >= 3.0 -// without warranty, see README.md and license for details. -// -// SPDX-License-Identifier: AGPL-3.0-or-later -// License-Filename: LICENSES/AGPL-3.0.txt -// -// Copyright (C) 2018 by via donau -// – Österreichische Wasserstraßen-Gesellschaft mbH -// Software engineering by Intevation GmbH -// -// Author(s): -// * Sascha L. Teichmann <sascha.teichmann@intevation.de> - -package imports - -import ( - "log" - - "gemma.intevation.de/gemma/pkg/scheduler" -) - -func init() { - scheduler.RegisterAction("fw", scheduledFW) -} - -func scheduledFW(user string, cfgID *int64) { - log.Println("info: scheduled FW import") - // TODO: Implement me! -}
--- a/pkg/imports/gm.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/imports/gm.go Tue Jan 15 10:07:10 2019 +0100 @@ -10,12 +10,15 @@ // // Author(s): // * Raimund Renkert <raimund.renkert@intevation.de> + package imports import ( "context" "database/sql" "errors" + "fmt" + "strings" "time" "gemma.intevation.de/gemma/pkg/common" @@ -23,11 +26,17 @@ "gemma.intevation.de/gemma/pkg/soap/nts" ) +// GaugeMeasurement is an import job to import +// gauges measurement data from a NtS SOAP service. type GaugeMeasurement struct { - URL string `json:"url"` - Insecure bool `json:"insecure"` + // URL is the URL of the SOAP service. + URL string `json:"url"` + // Insecure indicates if HTTPS traffic + // should validate certificates or not. + Insecure bool `json:"insecure"` } +// GMJobKind is the import queue type identifier. const GMJobKind JobKind = "gm" const ( @@ -41,23 +50,23 @@ FROM waterway.gauges WHERE (location).country_code = users.current_user_country()` - hasGaugeMeasurementSQL = ` -SELECT true FROM waterway.gauge_measurements WHERE fk_gauge_id = $1` - insertGMSQL = ` INSERT INTO waterway.gauge_measurements ( fk_gauge_id, measure_date, sender, language_code, + country_code, date_issue, + reference_code, water_level, predicted, is_waterlevel, value_min, value_max, date_info, - source_organization + source_organization, + staging_done ) VALUES( ($1, $2, $3, $4, $5), $6, @@ -70,7 +79,10 @@ $13, $14, $15, - $16 + $16, + $17, + $18, + $19 ) RETURNING id` ) @@ -81,6 +93,10 @@ RegisterJobCreator(GMJobKind, gmJobCreator{}) } +func (gmJobCreator) Description() string { + return "gauge measurements" +} + func (gmJobCreator) Create(_ JobKind, data string) (Job, error) { gm := new(GaugeMeasurement) if err := common.FromJSONString(data, gm); err != nil { @@ -91,23 +107,21 @@ func (gmJobCreator) Depends() []string { return []string{ - "waterway.gauges", - "waterway.gauge_measurements", + "gauges", + "gauge_measurements", } } +func (gmJobCreator) AutoAccept() bool { return true } + // StageDone moves the imported gauge measurement out of the staging area. // Currently doing nothing. -func (gmJobCreator) StageDone( - ctx context.Context, - tx *sql.Tx, - id int64, -) error { +func (gmJobCreator) StageDone(context.Context, *sql.Tx, int64) error { return nil } // CleanUp of a gauge measurement import is a NOP. -func (gm *GaugeMeasurement) CleanUp() error { return nil } +func (*GaugeMeasurement) CleanUp() error { return nil } // Do executes the actual bottleneck import. func (gm *GaugeMeasurement) Do( @@ -149,7 +163,8 @@ // TODO get date_issue for selected gauges gids, err := gm.doForGM(ctx, gauges, conn, feedback) if err != nil { - feedback.Error("Error processing %d gauges: %s", len(gauges), err) + feedback.Error("Error processing %d gauges: %v", len(gauges), err) + return nil, err } if len(gids) == 0 { feedback.Info("No new gauge measurements found") @@ -164,6 +179,32 @@ return &summary, err } +// rescale returns a scaling function to bring the unit all to cm. +func rescale(unit string) (func(float32) float32, error) { + + var scale float32 + + switch strings.ToLower(unit) { + case "mm": + scale = 0.1 + case "cm": + scale = 1.0 + case "dm": + scale = 10.0 + case "m": + scale = 100.0 + case "hm": + scale = 10000.0 + case "km": + scale = 100000.0 + default: + return nil, fmt.Errorf("unknown unit '%s'", unit) + } + + fn := func(x float32) float32 { return scale * x } + return fn, nil +} + func (gm *GaugeMeasurement) doForGM( ctx context.Context, gauges []models.GaugeMeasurement, @@ -225,7 +266,25 @@ feedback.Warn("Invalid ISRS code %v", err) continue } + var referenceCode string + if wrm.Reference_code == nil { + feedback.Info("'Reference_code' not specified. Assuming 'ZPG'") + referenceCode = "ZPG" + } else { + referenceCode = string(*wrm.Reference_code) + } for _, measure := range wrm.Measure { + var unit string + if measure.Unit == nil { + feedback.Info("'Unit' not specified. Assuming 'cm'") + unit = "cm" + } else { + unit = string(*measure.Unit) + } + convert, err := rescale(unit) + if err != nil { + return nil, err + } isWaterlevel := *measure.Measure_code == nts.Measure_code_enumWAL err = insertStmt.QueryRowContext( ctx, @@ -237,14 +296,17 @@ measure.Measuredate, msg.Identification.From, msg.Identification.Language_code, + msg.Identification.Country_code, msg.Identification.Date_issue, - measure.Value, + referenceCode, + convert(measure.Value), measure.Predicted, isWaterlevel, - measure.Value_min, - measure.Value_max, + convert(measure.Value_min), + convert(measure.Value_max), msg.Identification.Date_issue, msg.Identification.Originator, + true, // staging_done ).Scan(&gid) if err != nil { return nil, err
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/misc.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "fmt" + "strings" +) + +type stringCounter map[string]int + +func (sc stringCounter) String() string { + var b strings.Builder + for t, c := range sc { + if b.Len() > 0 { + b.WriteString(", ") + } + b.WriteString(fmt.Sprintf("%s: %d", t, c)) + } + return b.String() +}
--- a/pkg/imports/polygon.go Tue Jan 15 09:54:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -// This is Free Software under GNU Affero General Public License v >= 3.0 -// without warranty, see README.md and license for details. -// -// SPDX-License-Identifier: AGPL-3.0-or-later -// License-Filename: LICENSES/AGPL-3.0.txt -// -// Copyright (C) 2018 by via donau -// – Österreichische Wasserstraßen-Gesellschaft mbH -// Software engineering by Intevation GmbH -// -// Author(s): -// * Sascha L. Teichmann <sascha.teichmann@intevation.de> - -package imports - -import ( - "bytes" - "encoding/binary" - "fmt" - "math" - - shp "github.com/jonas-p/go-shp" -) - -type ( - point struct { - X float64 - Y float64 - } - lineString []point - polygon []lineString -) - -const ( - wkbNDR byte = 1 - wkbPolygon uint32 = 3 -) - -func shapeToPolygon(s shp.Shape) (polygon, error) { - switch p := s.(type) { - case *shp.Polygon: - return toPolygon(p.NumParts, p.Parts, p.Points), nil - case *shp.PolygonZ: - return toPolygon(p.NumParts, p.Parts, p.Points), nil - case *shp.PolygonM: - return toPolygon(p.NumParts, p.Parts, p.Points), nil - } - return nil, fmt.Errorf("Unsupported shape type %T", s) -} - -func toPolygon(numParts int32, parts []int32, points []shp.Point) polygon { - out := make(polygon, numParts) - var pos int32 - - for i := range out { - var howMany int32 - if i+1 >= len(parts) { - howMany = int32(len(points)) - pos - } else { - howMany = parts[i+1] - parts[i] - } - - line := make(lineString, howMany) - for j := int32(0); j < howMany; j, pos = j+1, pos+1 { - p := &points[pos] - line[j] = point{p.X, p.Y} - } - out[i] = line - } - return out -} - -func (p polygon) asWKB() []byte { - if p == nil { - return nil - } - // pre-calculate size to avoid reallocations. - size := 1 + 4 + 4 - for _, ring := range p { - size += 4 + len(ring)*2*8 - } - - buf := bytes.NewBuffer(make([]byte, 0, size)) - - binary.Write(buf, binary.LittleEndian, wkbNDR) - binary.Write(buf, binary.LittleEndian, wkbPolygon) - binary.Write(buf, binary.LittleEndian, uint32(len(p))) - - for _, ring := range p { - binary.Write(buf, binary.LittleEndian, uint32(len(ring))) - for _, v := range ring { - binary.Write(buf, binary.LittleEndian, math.Float64bits(v.X)) - binary.Write(buf, binary.LittleEndian, math.Float64bits(v.Y)) - } - } - - return buf.Bytes() -}
--- a/pkg/imports/queue.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/imports/queue.go Tue Jan 15 10:07:10 2019 +0100 @@ -42,6 +42,15 @@ Error(fmt string, args ...interface{}) } + // RetryError is an error type to signal that + // the import should be tried again. + RetryError struct { + // Message is the error message. + Message string + // When is the new scheduled execution time. + When time.Time + } + // Job is the central abstraction of an import job // run by the import queue. Job interface { @@ -66,6 +75,8 @@ // JobCreator is used to bring a job to life as it is stored // in pure meta-data form to the database. JobCreator interface { + // Description is the long name of the import. + Description() string // Create build the actual job. // kind is the name of the import type. // data is a free form string to pass arguments to the creation @@ -80,15 +91,18 @@ // (state = accepted). This can be used to finalize the imported // data to move it e.g from the staging area. StageDone(context.Context, *sql.Tx, int64) error + // AutoAccept indicates that imports of this kind + // don't need a review. + AutoAccept() bool } idJob struct { - id int64 - kind JobKind - user string - sendEmail bool - autoAccept bool - data string + id int64 + kind JobKind + user string + trysLeft sql.NullInt64 + sendEmail bool + data string } ) @@ -129,33 +143,36 @@ insertJobSQL = ` INSERT INTO waterway.imports ( kind, + due, + trys_left, username, send_email, - auto_accept, data ) VALUES ( $1, - $2, + COALESCE($2, CURRENT_TIMESTAMP), $3, $4, - $5 + $5, + $6 ) RETURNING id` selectJobSQL = ` SELECT id, kind, + trys_left, username, send_email, - auto_accept, data FROM waterway.imports -WHERE state = 'queued'::waterway.import_state AND enqueued IN ( - SELECT min(enqueued) - FROM waterway.imports - WHERE state = 'queued'::waterway.import_state AND - kind = ANY($1) -) +WHERE + due <= CURRENT_TIMESTAMP + interval '5 seconds' AND + state = 'queued'::waterway.import_state AND enqueued IN ( + SELECT min(enqueued) + FROM waterway.imports + WHERE state = 'queued'::waterway.import_state AND + kind = ANY($1)) LIMIT 1` updateStateSQL = ` @@ -184,6 +201,11 @@ go iqueue.importLoop() } +// Error makes RetryError an error. +func (re *RetryError) Error() string { + return re.Message +} + func (q *importQueue) registerJobCreator(kind JobKind, jc JobCreator) { q.creatorsMu.Lock() defer q.creatorsMu.Unlock() @@ -201,7 +223,7 @@ return iqueue.importKindNames() } -// HasImportKind checks if the import queue supports a given kind. +// HasImportKindName checks if the import queue supports a given kind. func HasImportKindName(kind string) bool { return iqueue.hasImportKindName(kind) } @@ -234,6 +256,13 @@ return names } +func (idj *idJob) trys() int { + if !idj.trysLeft.Valid { + return -1 + } + return int(idj.trysLeft.Int64) +} + func (q *importQueue) jobCreator(kind JobKind) JobCreator { q.creatorsMu.Lock() defer q.creatorsMu.Unlock() @@ -242,20 +271,30 @@ func (q *importQueue) addJob( kind JobKind, + due time.Time, + trysLeft int, user string, - sendEmail, autoAccept bool, + sendEmail bool, data string, ) (int64, error) { ctx := context.Background() var id int64 - err := auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error { + if due.IsZero() { + due = time.Now() + } + var tl sql.NullInt64 + if trysLeft >= 0 { + tl = sql.NullInt64{Int64: int64(trysLeft), Valid: true} + } + err := auth.RunAs(ctx, user, func(conn *sql.Conn) error { return conn.QueryRowContext( ctx, insertJobSQL, string(kind), + due, + tl, user, sendEmail, - autoAccept, data).Scan(&id) }) if err == nil { @@ -268,10 +307,18 @@ } // AddJob adds a job to the global import queue to be executed -// as soon as possible. This is gone in a separate Go routine +// as soon as possible after due. +// This is gone in a separate Go routine // so this will not block. -func AddJob(kind JobKind, user string, sendEmail, autoAccept bool, data string) (int64, error) { - return iqueue.addJob(kind, user, sendEmail, autoAccept, data) +func AddJob( + kind JobKind, + due time.Time, + trysLeft int, + user string, + sendEmail bool, + data string, +) (int64, error) { + return iqueue.addJob(kind, due, trysLeft, user, sendEmail, data) } type logFeedback int64 @@ -284,7 +331,7 @@ return err }) if err != nil { - log.Printf("logging failed: %v\n", err) + log.Printf("error: logging failed: %v\n", err) } } @@ -355,9 +402,9 @@ if err = tx.QueryRowContext(ctx, selectJobSQL, &kinds).Scan( &ji.id, &ji.kind, + &ji.trysLeft, &ji.user, &ji.sendEmail, - &ji.autoAccept, &ji.data, ); err != nil { return err @@ -425,7 +472,7 @@ // re-enqueue the jobs that are in state running. // They where in progess when the server went down. if err := reEnqueueRunning(); err != nil { - log.Printf("re-enqueuing failed: %v", err) + log.Printf("error: re-enqueuing failed: %v", err) } for { @@ -434,7 +481,7 @@ for { if idj, err = q.fetchJob(); err != nil && err != sql.ErrNoRows { - log.Printf("db error: %v\n", err) + log.Printf("error: db: %v\n", err) } if idj != nil { break @@ -445,7 +492,7 @@ } } - log.Printf("starting import #%d\n", idj.id) + log.Printf("info: starting import #%d\n", idj.id) jc := q.jobCreator(idj.kind) if jc == nil { @@ -500,25 +547,52 @@ if errDo != nil { feedback.Error("error do: %v", errDo) } - errCleanup := survive(job.CleanUp)() - if errCleanup != nil { - feedback.Error("error cleanup: %v", errCleanup) + // Should we try again? + retry, shouldRetry := errDo.(*RetryError) + + if shouldRetry && idj.trysLeft.Valid { // NULL -> limit less + if idj.trysLeft.Int64--; idj.trysLeft.Int64 <= 0 { + shouldRetry = false + } + } + + var errCleanup error + if !shouldRetry { // cleanup debris + errCleanup = survive(job.CleanUp)() + if errCleanup != nil { + feedback.Error("error cleanup: %v", errCleanup) + } } var state string switch { case errDo != nil || errCleanup != nil: state = "failed" - case idj.autoAccept: + case jc.AutoAccept(): state = "accepted" default: state = "pending" } if err := updateStateSummary(ctx, idj.id, state, summary); err != nil { - log.Printf("setting state of job %d failed: %v\n", idj.id, err) + log.Printf("error: setting state of job %d failed: %v\n", idj.id, err) + } + log.Printf("info: import #%d finished: %s\n", idj.id, state) + if idj.sendEmail { + go sendNotificationMail(idj.user, jc.Description(), state, idj.id) } - // TODO: Send email if sendEmail is set. - log.Printf("import #%d finished: %s\n", idj.id, state) + + if shouldRetry { + nid, err := q.addJob( + idj.kind, + retry.When, idj.trys(), + idj.user, idj.sendEmail, + idj.data) + if err != nil { + log.Printf("error: retry enqueue failed: %v\n", err) + } else { + log.Printf("info: re-enqueued job with id %d\n", nid) + } + } }(jc, idj) } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/scheduled.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,167 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "context" + "database/sql" + "errors" + "fmt" + "log" + + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/scheduler" +) + +// JobKindSetups maps JobKinds to special setup functions. +var JobKindSetups = map[JobKind]func(*IDConfig) (interface{}, error){ + + GMJobKind: func(cfg *IDConfig) (interface{}, error) { + log.Println("info: schedule 'gm' import") + insecure := cfg.Attributes.Bool("insecure") + return &GaugeMeasurement{ + URL: *cfg.URL, + Insecure: insecure, + }, nil + }, + + FAJobKind: func(cfg *IDConfig) (interface{}, error) { + log.Println("info: schedule 'fa' import") + insecure := cfg.Attributes.Bool("insecure") + return &FairwayAvailability{ + URL: *cfg.URL, + Insecure: insecure, + }, nil + }, + + BNJobKind: func(cfg *IDConfig) (interface{}, error) { + log.Println("info: schedule 'bn' import") + insecure := cfg.Attributes.Bool("insecure") + return &Bottleneck{ + URL: *cfg.URL, + Insecure: insecure, + }, nil + }, + + WXJobKind: func(cfg *IDConfig) (interface{}, error) { + log.Println("info: schedule 'wx' import") + ft, found := cfg.Attributes.Get("feature-type") + if !found { + return nil, errors.New("cannot find 'feature-type' attribute") + } + sb, found := cfg.Attributes.Get("sort-by") + if !found { + return nil, errors.New("cannot find 'sort-by' attribute") + } + return &WaterwayAxis{ + URL: *cfg.URL, + FeatureType: ft, + SortBy: sb, + }, nil + }, + + WAJobKind: func(cfg *IDConfig) (interface{}, error) { + log.Println("info: schedule 'wa' import") + ft, found := cfg.Attributes.Get("feature-type") + if !found { + return nil, errors.New("cannot find 'feature-type' attribute") + } + sb, found := cfg.Attributes.Get("sort-by") + if !found { + return nil, errors.New("cannot find 'sort-by' attribute") + } + return &WaterwayArea{ + URL: *cfg.URL, + FeatureType: ft, + SortBy: sb, + }, nil + }, +} + +func init() { + run := func(cfgID int64) { + jobID, err := RunConfiguredImport(cfgID) + if err != nil { + log.Printf("error: running scheduled import failed: %v\n", err) + return + } + log.Printf("info: added import #%d to queue\n", jobID) + } + + for kind := range JobKindSetups { + scheduler.RegisterAction(string(kind), run) + } +} + +// RunConfiguredImportContext runs an import configured from the database. +func RunConfiguredImportContext(ctx context.Context, conn *sql.Conn, id int64) (int64, error) { + cfg, err := LoadIDConfigContext(ctx, conn, id) + return runConfiguredImport(id, cfg, err) +} + +// RunConfiguredImport runs an import configured from the database. +func RunConfiguredImport(id int64) (int64, error) { + cfg, err := loadIDConfig(id) + return runConfiguredImport(id, cfg, err) +} + +func runConfiguredImport(id int64, cfg *IDConfig, err error) (int64, error) { + + if err != nil { + return 0, err + } + if cfg == nil { + return 0, fmt.Errorf("no config found for id %d.\n", id) + } + if cfg.URL == nil { + return 0, errors.New("error: No URL specified") + } + + kind := JobKind(cfg.Kind) + + setup := JobKindSetups[kind] + if setup == nil { + return 0, fmt.Errorf("unknown job kind: %s", cfg.Kind) + } + + what, err := setup(cfg) + if err != nil { + return 0, err + } + + var serialized string + if serialized, err = common.ToJSONString(what); err != nil { + return 0, err + } + + due, _ := cfg.Attributes.Time("due") + + retries, found := cfg.Attributes.Int("retries") + if !found { + retries = -1 + } + + var jobID int64 + if jobID, err = AddJob( + kind, + due, retries, + cfg.User, + cfg.SendEMail, + serialized, + ); err != nil { + return 0, err + } + + return jobID, nil +}
--- a/pkg/imports/sr.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/imports/sr.go Tue Jan 15 10:07:10 2019 +0100 @@ -73,6 +73,10 @@ RegisterJobCreator(SRJobKind, srJobCreator{}) } +func (srJobCreator) Description() string { return "sounding results" } + +func (srJobCreator) AutoAccept() bool { return false } + func (srJobCreator) Create(_ JobKind, data string) (Job, error) { sr := new(SoundingResult) if err := common.FromJSONString(data, sr); err != nil { @@ -83,9 +87,9 @@ func (srJobCreator) Depends() []string { return []string{ - "waterway.sounding_results", - "waterway.sounding_results_contour_lines", - "waterway.bottlenecks", + "sounding_results", + "sounding_results_contour_lines", + "bottlenecks", } } @@ -424,7 +428,7 @@ return loadXYZReader(r, feedback) } -func loadBoundary(z *zip.ReadCloser) (polygon, error) { +func loadBoundary(z *zip.ReadCloser) (polygonSlice, error) { shpF := common.FindInZIP(z, ".shp") if shpF == nil { return nil, nil
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/wa.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,282 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "io" + "strconv" + "time" + + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/wfs" +) + +// WaterwayArea is an import job to import +// the waterway area in form of polygon geometries +// and attribute data from a WFS service. +type WaterwayArea struct { + // URL the GetCapabilities URL of the WFS service. + URL string `json:"url"` + // FeatureType selects the feature type of the WFS service. + FeatureType string `json:"feature-type"` + // SortBy works around misconfigured services to + // establish a sort order to get the features. + SortBy string `json:"sort-by"` +} + +// WAJobKind is the import queue type identifier. +const WAJobKind JobKind = "wa" + +type waJobCreator struct{} + +func init() { + RegisterJobCreator(WAJobKind, waJobCreator{}) +} + +func (waJobCreator) Description() string { return "waterway area" } + +func (waJobCreator) AutoAccept() bool { return true } + +func (waJobCreator) Create(_ JobKind, data string) (Job, error) { + wa := new(WaterwayArea) + if err := common.FromJSONString(data, wa); err != nil { + return nil, err + } + return wa, nil +} + +func (waJobCreator) Depends() []string { + return []string{ + "waterway_area", + } +} + +// StageDone is a NOP for waterway area imports. +func (waJobCreator) StageDone(context.Context, *sql.Tx, int64) error { + return nil +} + +// CleanUp for waterway area imports is a NOP. +func (*WaterwayArea) CleanUp() error { return nil } + +type waterwayAreaProperties struct { + Catccl *string `json:"ienc_catccl"` + Dirimp *string `json:"ienc_dirimp"` +} + +const ( + deleteWaterwayAreaSQL = ` +WITH resp AS ( + SELECT best_utm(area::geometry) AS t, + ST_Transform(area::geometry, best_utm(area::geometry)) AS a + FROM users.responsibility_areas + WHERE country = users.current_user_country() +) +DELETE FROM waterway.waterway_area +WHERE ST_Covers( + (SELECT a FROM resp), + ST_Transform(area::geometry, (SELECT t FROM resp))) +` + insertWaterwayAreaSQL = ` +WITH resp AS ( + SELECT best_utm(area::geometry) AS t, + ST_Transform(area::geometry, best_utm(area::geometry)) AS a + FROM users.responsibility_areas + WHERE country = users.current_user_country() +) +INSERT INTO waterway.waterway_area (area, catccl, dirimp) +SELECT ST_Transform(clipped.geom, 4326)::geography, $3, $4 FROM ( + SELECT (ST_Dump( + ST_Intersection( + (SELECT a FROM resp), + ST_Transform( + ST_GeomFromWKB($1, $2::integer), + (SELECT t FROM resp) + ) + ) + )).geom AS geom + ) AS clipped + WHERE clipped.geom IS NOT NULL +` +) + +// Do executes the actual waterway axis import. +func (wx *WaterwayArea) Do( + ctx context.Context, + importID int64, + conn *sql.Conn, + feedback Feedback, +) (interface{}, error) { + + start := time.Now() + + feedback.Info("Import waterway area") + + feedback.Info("Loading capabilities from %s", wx.URL) + caps, err := wfs.GetCapabilities(wx.URL) + if err != nil { + feedback.Error("Loading capabilities failed: %v", err) + return nil, err + } + + ft := caps.FindFeatureType(wx.FeatureType) + if ft == nil { + return nil, fmt.Errorf("Unknown feature type '%s'", wx.FeatureType) + } + + feedback.Info("Found feature type '%s", wx.FeatureType) + + epsg, err := wfs.CRSToEPSG(ft.DefaultCRS) + if err != nil { + feedback.Error("Unsupported CRS name '%s'", ft.DefaultCRS) + return nil, err + } + + if wx.SortBy != "" { + feedback.Info("Features will be sorted by '%s'", wx.SortBy) + } + + urls, err := wfs.GetFeaturesGET( + caps, wx.FeatureType, "application/json", wx.SortBy) + if err != nil { + feedback.Error("Cannot create GetFeature URLs. %v", err) + return nil, err + } + + tx, err := conn.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + + insertStmt, err := tx.PrepareContext(ctx, insertWaterwayAreaSQL) + if err != nil { + return nil, err + } + defer insertStmt.Close() + + // Delete the old features. + if _, err := tx.ExecContext(ctx, deleteWaterwayAreaSQL); err != nil { + return nil, err + } + + var ( + unsupported = stringCounter{} + missingProperties int + badProperties int + features int + ) + + if err := wfs.DownloadURLs(urls, func(r io.Reader) error { + rfc, err := wfs.ParseRawFeatureCollection(r) + if err != nil { + return fmt.Errorf("parsing GetFeature document failed: %v", err) + } + if rfc.CRS != nil { + crsName := rfc.CRS.Properties.Name + if epsg, err = wfs.CRSToEPSG(crsName); err != nil { + feedback.Error("Unsupported CRS: %d", crsName) + return err + } + } + + // No features -> ignore. + if rfc.Features == nil { + return nil + } + + feedback.Info("Using EPSG: %d", epsg) + + for _, feature := range rfc.Features { + if feature.Properties == nil || feature.Geometry.Coordinates == nil { + missingProperties++ + continue + } + + var props waterwayAreaProperties + + if err := json.Unmarshal(*feature.Properties, &props); err != nil { + badProperties++ + continue + } + + var catccl sql.NullInt64 + if props.Catccl != nil { + if value, err := strconv.ParseInt(*props.Catccl, 10, 64); err == nil { + catccl = sql.NullInt64{Int64: value, Valid: true} + } + } + var dirimp sql.NullInt64 + if props.Dirimp != nil { + if value, err := strconv.ParseInt(*props.Dirimp, 10, 64); err == nil { + dirimp = sql.NullInt64{Int64: value, Valid: true} + } + } + + switch feature.Geometry.Type { + case "Polygon": + var p polygonSlice + if err := json.Unmarshal(*feature.Geometry.Coordinates, &p); err != nil { + return err + } + if _, err := insertStmt.ExecContext( + ctx, + p.asWKB(), + epsg, + catccl, + dirimp, + ); err != nil { + return err + } + features++ + default: + unsupported[feature.Geometry.Type]++ + } + } + return nil + }); err != nil { + feedback.Error("Downloading features failed: %v", err) + return nil, err + } + + if features == 0 { + err := errors.New("No features found") + feedback.Error("%v", err) + return nil, err + } + + if badProperties > 0 { + feedback.Warn("Bad properties: %d", badProperties) + } + + if missingProperties > 0 { + feedback.Warn("Missing properties: %d", missingProperties) + } + + if len(unsupported) != 0 { + feedback.Warn("Unsupported types found: %s", unsupported) + } + + if err = tx.Commit(); err == nil { + feedback.Info("Storing %d features took %s", + features, time.Since(start)) + } + + return nil, err +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/wkb.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,131 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + + shp "github.com/jonas-p/go-shp" +) + +type ( + lineSlice [][]float64 + polygonSlice [][][]float64 +) + +const ( + wkbNDR byte = 1 + + wkbLineString uint32 = 2 + wkbPolygon uint32 = 3 +) + +func (l lineSlice) asWKB() []byte { + + size := 1 + 4 + 4 + len(l)*(2*8) + + buf := bytes.NewBuffer(make([]byte, 0, size)) + + binary.Write(buf, binary.LittleEndian, wkbNDR) + binary.Write(buf, binary.LittleEndian, wkbLineString) + binary.Write(buf, binary.LittleEndian, uint32(len(l))) + + for _, c := range l { + var lat, lon float64 + if len(c) > 0 { + lat = c[0] + } + if len(c) > 1 { + lon = c[1] + } + binary.Write(buf, binary.LittleEndian, math.Float64bits(lat)) + binary.Write(buf, binary.LittleEndian, math.Float64bits(lon)) + } + + return buf.Bytes() +} + +func (p polygonSlice) asWKB() []byte { + if p == nil { + return nil + } + // pre-calculate size to avoid reallocations. + size := 1 + 4 + 4 + for _, ring := range p { + size += 4 + len(ring)*2*8 + } + + buf := bytes.NewBuffer(make([]byte, 0, size)) + + binary.Write(buf, binary.LittleEndian, wkbNDR) + binary.Write(buf, binary.LittleEndian, wkbPolygon) + binary.Write(buf, binary.LittleEndian, uint32(len(p))) + + for _, ring := range p { + binary.Write(buf, binary.LittleEndian, uint32(len(ring))) + for _, v := range ring { + var lat, lon float64 + if len(v) > 0 { + lat = v[0] + } + if len(v) > 1 { + lon = v[1] + } + binary.Write(buf, binary.LittleEndian, math.Float64bits(lat)) + binary.Write(buf, binary.LittleEndian, math.Float64bits(lon)) + } + } + + return buf.Bytes() +} + +func shapeToPolygon(s shp.Shape) (polygonSlice, error) { + switch p := s.(type) { + case *shp.Polygon: + return toPolygon(p.NumParts, p.Parts, p.Points), nil + case *shp.PolygonZ: + return toPolygon(p.NumParts, p.Parts, p.Points), nil + case *shp.PolygonM: + return toPolygon(p.NumParts, p.Parts, p.Points), nil + } + return nil, fmt.Errorf("Unsupported shape type %T", s) +} + +func toPolygon(numParts int32, parts []int32, points []shp.Point) polygonSlice { + out := make(polygonSlice, numParts) + var pos int32 + + for i := range out { + var howMany int32 + if i+1 >= len(parts) { + howMany = int32(len(points)) - pos + } else { + howMany = parts[i+1] - parts[i] + } + + line := make([][]float64, howMany) + vertices := make([]float64, 2*howMany) + for j := int32(0); j < howMany; j, pos = j+1, pos+1 { + p := &points[pos] + vertex := vertices[j*2 : j*2+2] + vertex[0], vertex[1] = p.X, p.Y + line[j] = vertex + } + out[i] = line + } + return out +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/imports/wx.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,291 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package imports + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "io" + "time" + + "gemma.intevation.de/gemma/pkg/common" + "gemma.intevation.de/gemma/pkg/wfs" +) + +// WaterwayAxis is an import job to import +// the waterway axes in form of line string geometries +// and attribute data from a WFS service. +type WaterwayAxis struct { + // URL the GetCapabilities URL of the WFS service. + URL string `json:"url"` + // FeatureType selects the feature type of the WFS service. + FeatureType string `json:"feature-type"` + // SortBy works around misconfigured services to + // establish a sort order to get the features. + SortBy string `json:"sort-by"` +} + +// WXJobKind is the import queue type identifier. +const WXJobKind JobKind = "wx" + +type wxJobCreator struct{} + +func init() { + RegisterJobCreator(WXJobKind, wxJobCreator{}) +} + +func (wxJobCreator) Description() string { return "waterway axis" } + +func (wxJobCreator) AutoAccept() bool { return true } + +func (wxJobCreator) Create(_ JobKind, data string) (Job, error) { + wx := new(WaterwayAxis) + if err := common.FromJSONString(data, wx); err != nil { + return nil, err + } + return wx, nil +} + +func (wxJobCreator) Depends() []string { + return []string{ + "waterway_axis", + } +} + +// StageDone is a NOP for waterway axis imports. +func (wxJobCreator) StageDone(context.Context, *sql.Tx, int64) error { + return nil +} + +// CleanUp for waterway axis imports is a NOP. +func (*WaterwayAxis) CleanUp() error { return nil } + +type waterwayAxisProperties struct { + ObjNam string `json:"hydro_objnam"` + NObjNnm *string `json:"hydro_nobjnm"` +} + +const ( + deleteWaterwayAxisSQL = ` +WITH resp AS ( + SELECT best_utm(area::geometry) AS t, + ST_Transform(area::geometry, best_utm(area::geometry)) AS a + FROM users.responsibility_areas + WHERE country = users.current_user_country() +) +DELETE FROM waterway.waterway_axis +WHERE ST_Covers( + (SELECT a FROM resp), + ST_Transform(wtwaxs::geometry, (SELECT t FROM resp))) +` + + insertWaterwayAxisSQL = ` +WITH resp AS ( + SELECT best_utm(area::geometry) AS t, + ST_Transform(area::geometry, best_utm(area::geometry)) AS a + FROM users.responsibility_areas + WHERE country = users.current_user_country() +) +INSERT INTO waterway.waterway_axis (wtwaxs, objnam, nobjnam) +SELECT ST_Transform(clipped.geom, 4326)::geography, $3, $4 FROM ( + SELECT (ST_Dump( + ST_Intersection( + (SELECT a FROM resp), + ST_Transform( + ST_GeomFromWKB($1, $2::integer), + (SELECT t FROM resp) + ) + ) + )).geom AS geom + ) AS clipped + WHERE clipped.geom IS NOT NULL +` +) + +// Do executes the actual waterway axis import. +func (wx *WaterwayAxis) Do( + ctx context.Context, + importID int64, + conn *sql.Conn, + feedback Feedback, +) (interface{}, error) { + + start := time.Now() + + feedback.Info("Import waterway axis") + + feedback.Info("Loading capabilities from %s", wx.URL) + caps, err := wfs.GetCapabilities(wx.URL) + if err != nil { + feedback.Error("Loading capabilities failed: %v", err) + return nil, err + } + + ft := caps.FindFeatureType(wx.FeatureType) + if ft == nil { + return nil, fmt.Errorf("Unknown feature type '%s'", wx.FeatureType) + } + + feedback.Info("Found feature type '%s", wx.FeatureType) + + epsg, err := wfs.CRSToEPSG(ft.DefaultCRS) + if err != nil { + feedback.Error("Unsupported CRS name '%s'", ft.DefaultCRS) + return nil, err + } + + if wx.SortBy != "" { + feedback.Info("Features will be sorted by '%s'", wx.SortBy) + } + + urls, err := wfs.GetFeaturesGET( + caps, wx.FeatureType, "application/json", wx.SortBy) + if err != nil { + feedback.Error("Cannot create GetFeature URLs. %v", err) + return nil, err + } + + tx, err := conn.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + + insertStmt, err := tx.PrepareContext(ctx, insertWaterwayAxisSQL) + if err != nil { + return nil, err + } + defer insertStmt.Close() + + // Delete the old features. + if _, err := tx.ExecContext(ctx, deleteWaterwayAxisSQL); err != nil { + return nil, err + } + + var ( + unsupported = stringCounter{} + missingProperties int + badProperties int + features int + ) + + if err := wfs.DownloadURLs(urls, func(r io.Reader) error { + rfc, err := wfs.ParseRawFeatureCollection(r) + if err != nil { + return fmt.Errorf("parsing GetFeature document failed: %v", err) + } + if rfc.CRS != nil { + crsName := rfc.CRS.Properties.Name + if epsg, err = wfs.CRSToEPSG(crsName); err != nil { + feedback.Error("Unsupported CRS: %d", crsName) + return err + } + } + + // No features -> ignore. + if rfc.Features == nil { + return nil + } + + feedback.Info("Using EPSG: %d", epsg) + + for _, feature := range rfc.Features { + if feature.Properties == nil || feature.Geometry.Coordinates == nil { + missingProperties++ + continue + } + + var props waterwayAxisProperties + + if err := json.Unmarshal(*feature.Properties, &props); err != nil { + badProperties++ + continue + } + + var nobjnam sql.NullString + if props.NObjNnm != nil { + nobjnam = sql.NullString{String: *props.NObjNnm, Valid: true} + } + + switch feature.Geometry.Type { + case "LineString": + var l lineSlice + if err := json.Unmarshal(*feature.Geometry.Coordinates, &l); err != nil { + return err + } + if _, err := insertStmt.ExecContext( + ctx, + l.asWKB(), + epsg, + props.ObjNam, + nobjnam, + ); err != nil { + return err + } + features++ + case "MultiLineString": + var ls []lineSlice + if err := json.Unmarshal(*feature.Geometry.Coordinates, &ls); err != nil { + return err + } + for _, l := range ls { + if _, err := insertStmt.ExecContext( + ctx, + l.asWKB(), + epsg, + props.ObjNam, + nobjnam, + ); err != nil { + return err + } + features++ + } + default: + unsupported[feature.Geometry.Type]++ + } + } + return nil + }); err != nil { + feedback.Error("Downloading features failed: %v", err) + return nil, err + } + + if features == 0 { + err := errors.New("No features found") + feedback.Error("%v", err) + return nil, err + } + + if badProperties > 0 { + feedback.Warn("Bad properties: %d", badProperties) + } + + if missingProperties > 0 { + feedback.Warn("Missing properties: %d", missingProperties) + } + + if len(unsupported) != 0 { + feedback.Warn("Unsupported types found: %s", unsupported) + } + + if err = tx.Commit(); err == nil { + feedback.Info("Storing %d features took %s", + features, time.Since(start)) + } + + return nil, err +}
--- a/pkg/models/bn.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/models/bn.go Tue Jan 15 10:07:10 2019 +0100 @@ -13,7 +13,16 @@ package models +import "gemma.intevation.de/gemma/pkg/common" + type BottleneckImport struct { - URL string `json:"url"` - Insecure bool `json:"insecure"` + URL string `json:"url"` + Insecure bool `json:"insecure"` + SendEmail bool `json:"send-email"` + Attributes common.Attributes `json:"attributes,omitempty"` } + +type Bottleneck struct { + ID string + ResponsibleCountry string +}
--- a/pkg/models/cross.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/models/cross.go Tue Jan 15 10:07:10 2019 +0100 @@ -155,7 +155,7 @@ func (lc GeoJSONLineCoordinates) AsWKB() []byte { - size := 1 + 4 + 4 + len(lc)*(1+4+2*8) + size := 1 + 4 + 4 + len(lc)*(2*8) buf := bytes.NewBuffer(make([]byte, 0, size))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/models/fa.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,34 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Raimund Renkert <raimund.renkert@intevation.de> + +package models + +import ( + "time" + + "gemma.intevation.de/gemma/pkg/common" +) + +// FairwayAvailabilityImport contains data used to define the endpoint +type FairwayAvailabilityImport struct { + URL string `json:"url"` + Insecure bool `json:"insecure"` + SendEmail bool `json:"send-email"` + // Attributes are optional attributes. + Attributes common.Attributes `json:"attributes,omitempty"` +} + +type UniqueFairwayAvailability struct { + BottleneckId string + Surdat time.Time +}
--- a/pkg/models/gauge.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/models/gauge.go Tue Jan 15 10:07:10 2019 +0100 @@ -18,12 +18,17 @@ "fmt" "strconv" "time" + + "gemma.intevation.de/gemma/pkg/common" ) // GaugeMeasurementImport contains data used to define the endpoint type GaugeMeasurementImport struct { - URL string `json:"url"` - Insecure bool `json:"insecure"` + URL string `json:"url"` + Insecure bool `json:"insecure"` + SendEmail bool `json:"send-email"` + // Attributes are optional attributes. + Attributes common.Attributes `json:"attributes,omitempty"` } // GaugeMeasurement holds information about a gauge and the latest measurement
--- a/pkg/models/user.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/models/user.go Tue Jan 15 10:07:10 2019 +0100 @@ -23,11 +23,17 @@ ) type ( - Email string - Country string - Role string + // Email is a string formed by a valid EMail address. + Email string + // Country is a valid country 2 letter code. + Country string + // Role is a string with a valid gemma role. + Role string + // UserName is a string forming a valid user name. UserName string + // BoundingBox is a spatial bounding box of user's + // responsibility area. BoundingBox struct { X1 float64 `json:"x1"` Y1 float64 `json:"y1"` @@ -35,6 +41,7 @@ Y2 float64 `json:"y2"` } + // User is a serialized JSON form of user data. User struct { User UserName `json:"user"` Role Role `json:"role"` @@ -44,6 +51,7 @@ Extent *BoundingBox `json:"extent"` } + // PWResetUser is send to request a password reset for a user. PWResetUser struct { User string `json:"user"` } @@ -65,6 +73,7 @@ errNoEmailAddress = errors.New("Not a valid email address") ) +// UnmarshalJSON ensures that the given string forms a valid email address. func (e *Email) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { @@ -77,10 +86,12 @@ return nil } +// Value implements the driver.Valuer interface. func (e Email) Value() (driver.Value, error) { return string(e), nil } +// Scan implements the sql.Scanner interface. func (e *Email) Scan(src interface{}) (err error) { if s, ok := src.(string); ok { *e = Email(s) @@ -92,10 +103,12 @@ var errNoValidUser = errors.New("Not a valid user") +// IsValid checks if a given user name is valid. func (u UserName) IsValid() bool { return u != "" } +// UnmarshalJSON ensures that the given string forms a valid user name. func (u *UserName) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { @@ -108,6 +121,7 @@ return errNoValidUser } +// Scan implements the sql.Scanner interface. func (u *UserName) Scan(src interface{}) (err error) { if s, ok := src.(string); ok { *u = UserName(s) @@ -125,6 +139,8 @@ errNoValidCountry = errors.New("Not a valid country") ) +// UnmarshalJSON ensures that the given string forms a valid +// two letter country code. func (c *Country) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { @@ -140,10 +156,12 @@ return errNoValidCountry } +// Value implements the driver.Valuer interface. func (c Country) Value() (driver.Value, error) { return string(c), nil } +// Scan implements the sql.Scanner interfaces. func (c *Country) Scan(src interface{}) (err error) { if s, ok := src.(string); ok { *c = Country(s) @@ -162,10 +180,12 @@ errNoValidRole = errors.New("Not a valid role") ) +// Value implements the driver.Valuer interface. func (r Role) Value() (driver.Value, error) { return string(r), nil } +// Scan implements the sql.Scanner interface. func (r *Role) Scan(src interface{}) (err error) { if s, ok := src.(string); ok { *r = Role(s) @@ -175,6 +195,7 @@ return } +// UnmarshalJSON ensure that the given string is a valid user name. func (r *Role) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/models/waterway.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,48 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package models + +import "gemma.intevation.de/gemma/pkg/common" + +type ( + // WaterwayAxisImport specifies an import of the waterway axis. + WaterwayAxisImport struct { + // URL is the capabilities URL of the WFS. + URL string `json:"url"` + // FeatureType is the layer to use. + FeatureType string `json:"feature-type"` + // SortBy sorts the feature by this key. + SortBy string `json:"sort-by"` + // SendEmail is set to true if an email should be send after + // importing the axis. + SendEmail bool `json:"send-email"` + // Attributes are optional attributes. + Attributes common.Attributes `json:"attributes,omitempty"` + } + + // WaterwayAreaImport specifies an import of the waterway area. + WaterwayAreaImport struct { + // URL is the capabilities URL of the WFS. + URL string `json:"url"` + // FeatureType is the layer to use. + FeatureType string `json:"feature-type"` + // SortBy sorts the feature by this key. + SortBy string `json:"sort-by"` + // SendEmail is set to true if an email should be send after + // importing the axis. + SendEmail bool `json:"send-email"` + // Attributes are optional attributes. + Attributes common.Attributes `json:"attributes,omitempty"` + } +)
--- a/pkg/octree/builder.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/builder.go Tue Jan 15 10:07:10 2019 +0100 @@ -22,6 +22,7 @@ "github.com/golang/snappy" ) +// Builder is used to turn a TIN into an Octree. type Builder struct { t *Tin nodes int @@ -57,10 +58,12 @@ } } +// NewBuilder creates a new Builder for a TIN. func NewBuilder(t *Tin) *Builder { return &Builder{t: t} } +// Build builds the Octree. func (tb *Builder) Build() { triangles := make([]int32, len(tb.t.Triangles)) @@ -72,9 +75,9 @@ tb.buildRecursive(triangles, tb.t.Min, tb.t.Max, 0) tb.index[0] = int32(len(tb.index)) - log.Printf("num nodes: %d\n", tb.index[0]) + log.Printf("info: num nodes: %d\n", tb.index[0]) - log.Printf("nodes: %d leaves: %d index %d\n", + log.Printf("info: nodes: %d leaves: %d index %d\n", tb.nodes, tb.leaves, tb.index[0]) } @@ -144,7 +147,7 @@ return int32(pos) } -func (tb *Builder) Serialize(w io.Writer) error { +func (tb *Builder) serialize(w io.Writer) error { var buf [binary.MaxVarintLen32]byte if err := binary.Write(w, binary.LittleEndian, tb.index[0]); err != nil { @@ -167,7 +170,7 @@ last = x } - log.Printf("compressed octree index in bytes: %d (%d)\n", + log.Printf("info: compressed octree index in bytes: %d (%d)\n", written, 4*len(tb.index)) return nil @@ -175,15 +178,16 @@ func (tb *Builder) writeTo(w io.Writer) error { out := snappy.NewBufferedWriter(w) - if err := tb.t.Serialize(out); err != nil { + if err := tb.t.serialize(out); err != nil { return err } - if err := tb.Serialize(out); err != nil { + if err := tb.serialize(out); err != nil { return err } return out.Flush() } +// Bytes serializes an Octree into a byte slice. func (tb *Builder) Bytes() ([]byte, error) { var buf bytes.Buffer if err := tb.writeTo(&buf); err != nil { @@ -192,6 +196,7 @@ return buf.Bytes(), nil } +// Tree returns an Octree from the Builder. func (tb *Builder) Tree() *Tree { return &Tree{ EPSG: tb.t.EPSG,
--- a/pkg/octree/cache.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/cache.go Tue Jan 15 10:07:10 2019 +0100 @@ -31,6 +31,9 @@ tree *Tree access time.Time } + + // Cache holds Octrees for a defined amount of time in memory + // before they are released. Cache struct { sync.Mutex entries map[cacheKey]*cacheEntry @@ -87,6 +90,7 @@ } } +// FromCache fetches an Octree from the global Octree cache. func FromCache( ctx context.Context, conn *sql.Conn, @@ -135,7 +139,7 @@ } } - tree, err := Deserialize(data) + tree, err := deserialize(data) if err != nil { return nil, err }
--- a/pkg/octree/contours.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/contours.go Tue Jan 15 10:07:10 2019 +0100 @@ -19,6 +19,8 @@ "sync" ) +// ContourResult stores an calculated iso line for a given height. +// Is used as a future variable in the concurrent iso line calculation. type ContourResult struct { Height float64 Lines MultiLineStringZ @@ -28,6 +30,8 @@ cond *sync.Cond } +// NewContourResult prepares a future variable to later hold +// the result of the iso line calculation. func NewContourResult(height float64) *ContourResult { cr := ContourResult{Height: height} cr.cond = sync.NewCond(&cr.mu) @@ -56,6 +60,10 @@ cr.cond.Signal() } +// DoContours calculates the iso line for the given heights. +// This is done concurrently. +// It is guaranteed that the results are given to the store +// function in order of the original heights values. func DoContours(tree *Tree, heights []float64, store func(*ContourResult)) { contours := make([]*ContourResult, len(heights))
--- a/pkg/octree/loader.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/loader.go Tue Jan 15 10:07:10 2019 +0100 @@ -18,7 +18,6 @@ "bytes" "encoding/binary" "log" - "os" "github.com/golang/snappy" ) @@ -30,7 +29,7 @@ return nil, err } - log.Printf("EPSG: %d\n", tree.EPSG) + log.Printf("info: EPSG: %d\n", tree.EPSG) if err := tree.Min.Read(r); err != nil { return nil, err @@ -40,7 +39,7 @@ return nil, err } - log.Printf("BBOX: [[%f, %f, %f], [%f, %f, %f]]\n", + log.Printf("info: BBOX: [[%f, %f, %f], [%f, %f, %f]]\n", tree.Min.X, tree.Min.Y, tree.Min.Z, tree.Max.X, tree.Max.Y, tree.Max.Z) @@ -49,7 +48,7 @@ return nil, err } - log.Printf("vertices: %d\n", numVertices) + log.Printf("info: vertices: %d\n", numVertices) vertices := make([]Vertex, numVertices) tree.vertices = vertices @@ -65,7 +64,7 @@ return nil, err } - log.Printf("triangles: %d\n", numTriangles) + log.Printf("info: triangles: %d\n", numTriangles) indices := make([]int32, 3*numTriangles) triangles := make([][]int32, numTriangles) @@ -93,7 +92,7 @@ return nil, err } - log.Printf("num nodes: %d\n", numNodes) + log.Printf("info: num nodes: %d\n", numNodes) tree.index = make([]int32, numNodes) entries := tree.index[1:] @@ -112,19 +111,7 @@ return tree, nil } -func LoadTree(fname string) (*Tree, error) { - - f, err := os.Open(fname) - if err != nil { - return nil, err - } - defer f.Close() - return loadReader( - bufio.NewReader( - snappy.NewReader(f))) -} - -func Deserialize(data []byte) (*Tree, error) { +func deserialize(data []byte) (*Tree, error) { return loadReader( bufio.NewReader( snappy.NewReader(
--- a/pkg/octree/tin.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/tin.go Tue Jan 15 10:07:10 2019 +0100 @@ -32,17 +32,26 @@ errTooLessPoints = errors.New("Too less points") ) +// Tin stores a mesh of triangles with common vertices. type Tin struct { - EPSG uint32 - Vertices []Vertex + // EPSG holds the projection. + EPSG uint32 + // Vertices are the shared vertices. + Vertices []Vertex + // Triangles are the triangles. Triangles [][]int32 + // Min is the lower left corner of the bbox. Min Vertex + // Max is the upper right corner of the bbox. Max Vertex } +// FromWKB constructs the TIN from a WKB representation. +// Shared vertices are identified and referenced by the +// same index. func (t *Tin) FromWKB(data []byte) error { - log.Printf("data length %d\n", len(data)) + log.Printf("info: data length %d\n", len(data)) r := bytes.NewReader(data) @@ -166,7 +175,7 @@ triangles = append(triangles, triangle) } - log.Printf("bbox: [[%f, %f], [%f, %f]]\n", + log.Printf("info: bbox: [[%f, %f], [%f, %f]]\n", min.X, min.Y, max.X, max.Y) *t = Tin{ @@ -201,6 +210,8 @@ loadTinByIDSQL = tinSQLPrefix + `WHERE id = $2` + tinSQLSuffix ) +// GenerateTinByID generated a TIN by triangulating a point cloud +// from the database. func GenerateTinByID( ctx context.Context, conn *sql.Conn, @@ -219,6 +230,7 @@ return &tin, nil } +// Scan implements the sql.Scanner interface. func (t *Tin) Scan(raw interface{}) error { if raw == nil { return nil @@ -230,7 +242,7 @@ return t.FromWKB(data) } -func (t *Tin) Serialize(w io.Writer) error { +func (t *Tin) serialize(w io.Writer) error { if err := binary.Write(w, binary.LittleEndian, t.EPSG); err != nil { return err @@ -253,7 +265,7 @@ return err } } - log.Printf("vertices %d (%d)\n", len(t.Vertices), len(t.Vertices)*3*8) + log.Printf("info: vertices %d (%d)\n", len(t.Vertices), len(t.Vertices)*3*8) if err := binary.Write( w, binary.LittleEndian, uint32(len(t.Triangles))); err != nil { @@ -277,7 +289,7 @@ last = idx } } - log.Printf("compressed tin indices in bytes: %d (%d)\n", + log.Printf("info: compressed tin indices in bytes: %d (%d)\n", written, 3*4*len(t.Triangles)) return nil
--- a/pkg/octree/tree.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/tree.go Tue Jan 15 10:07:10 2019 +0100 @@ -17,14 +17,18 @@ "math" ) +// Tree is an Octree holding triangles. type Tree struct { + // EPSG is the projection. EPSG uint32 vertices []Vertex triangles [][]int32 index []int32 + // Min is the lower left corner of the bbox. Min Vertex + // Max is the upper right corner of the bbox. Max Vertex } @@ -35,6 +39,7 @@ {0.5, 0.5, 1.0, 1.0}, } +// Vertical does a vertical cross cut from (x1, y1) to (x2, y2). func (ot *Tree) Vertical(x1, y1, x2, y2 float64, fn func(*Triangle)) { box := Box2D{ @@ -70,26 +75,28 @@ stack = stack[:len(stack)-1] if top.pos > 0 { // node - for i := int32(0); i < 4; i++ { - a := ot.index[top.pos+i] - b := ot.index[top.pos+i+4] - if a == 0 && b == 0 { - continue - } - dx := top.X2 - top.X1 - dy := top.Y2 - top.Y1 - nbox := Box2D{ - dx*scale[i][0] + top.X1, - dy*scale[i][1] + top.Y1, - dx*scale[i][2] + top.X1, - dy*scale[i][3] + top.Y1, - } - if nbox.Intersects(box) && nbox.IntersectsPlane(line) { - if a != 0 { - stack = append(stack, frame{a, nbox}) + if index := ot.index[top.pos:]; len(index) > 7 { + for i := 0; i < 4; i++ { + a := index[i] + b := index[i+4] + if a == 0 && b == 0 { + continue } - if b != 0 { - stack = append(stack, frame{b, nbox}) + dx := top.X2 - top.X1 + dy := top.Y2 - top.Y1 + nbox := Box2D{ + dx*scale[i][0] + top.X1, + dy*scale[i][1] + top.Y1, + dx*scale[i][2] + top.X1, + dy*scale[i][3] + top.Y1, + } + if nbox.Intersects(box) && nbox.IntersectsPlane(line) { + if a != 0 { + stack = append(stack, frame{a, nbox}) + } + if b != 0 { + stack = append(stack, frame{b, nbox}) + } } } } @@ -123,6 +130,7 @@ } } +// Horizontal does a horizontal cross cut. func (ot *Tree) Horizontal(h float64, fn func(*Triangle)) { if h < ot.Min.Z || ot.Max.Z < h { @@ -156,11 +164,13 @@ } else { max = mid } - stack = append(stack, - frame{ot.index[pos+0], min, max}, - frame{ot.index[pos+1], min, max}, - frame{ot.index[pos+2], min, max}, - frame{ot.index[pos+3], min, max}) + if index := ot.index[pos:]; len(index) > 3 { + stack = append(stack, + frame{index[0], min, max}, + frame{index[1], min, max}, + frame{index[2], min, max}, + frame{index[3], min, max}) + } } else { // leaf pos = -pos - 1 n := ot.index[pos]
--- a/pkg/octree/vertex.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/octree/vertex.go Tue Jan 15 10:07:10 2019 +0100 @@ -23,20 +23,30 @@ ) type ( + // Vertex is a 3D vertex. Vertex struct { X float64 Y float64 Z float64 } + // Triangle is a triangle consisting of three vertices. Triangle [3]Vertex + // Line is a line defined by first vertex on that line + // and the second being the direction. Line [2]Vertex - MultiPointZ []Vertex - LineStringZ []Vertex + // MultiPointZ is a set of vertices. + MultiPointZ []Vertex + + // LineStringZ is a line string formed of vertices. + LineStringZ []Vertex + + // MultiLineStringZ is a set of line strings. MultiLineStringZ []LineStringZ + // Box2D is 2D area from (X1, Y1) to (X2, Y2). Box2D struct { X1 float64 Y1 float64 @@ -44,6 +54,7 @@ Y2 float64 } + // Plane2D is a 2D plane (a line in 2D space). Plane2D struct { A float64 B float64 @@ -51,6 +62,8 @@ } ) +// Minimize adjust this vertex v to hold the minimum +// values component-wise of v and w. func (v *Vertex) Minimize(w Vertex) { if w.X < v.X { v.X = w.X @@ -63,6 +76,8 @@ } } +// Maximize adjust this vertex v to hold the maximum +// values component-wise of v and w. func (v *Vertex) Maximize(w Vertex) { if w.X > v.X { v.X = w.X @@ -75,6 +90,7 @@ } } +// Sub returns (v - w) component-wise. func (v Vertex) Sub(w Vertex) Vertex { return Vertex{ v.X - w.X, @@ -83,6 +99,7 @@ } } +// Add returns (v + w) component-wise. func (v Vertex) Add(w Vertex) Vertex { return Vertex{ v.X + w.X, @@ -91,6 +108,7 @@ } } +// Scale returns s*v component-wise. func (v Vertex) Scale(s float64) Vertex { return Vertex{ s * v.X, @@ -99,6 +117,8 @@ } } +// Interpolate returns a function that return s*v2 + v1 +// component-wise. func Interpolate(v1, v2 Vertex) func(Vertex) Vertex { v2 = v2.Sub(v1) return func(s Vertex) Vertex { @@ -110,10 +130,13 @@ } } +// Less returns if one of v component is less than the +// corresponing component in w. func (v Vertex) Less(w Vertex) bool { return v.X < w.X || v.Y < w.Y || v.Z < w.Z } +// NewLine return a line of point/direction. func NewLine(p1, p2 Vertex) Line { return Line{ p2.Sub(p1), @@ -121,10 +144,13 @@ } } +// Eval returns the vertex for t*l[0] + l[1]. func (l Line) Eval(t float64) Vertex { return l[0].Scale(t).Add(l[1]) } +// IntersectHorizontal returns the intersection point +// for a given z value. func (l Line) IntersectHorizontal(h float64) Vertex { t := (h - l[1].Z) / l[0].Z return l.Eval(t) @@ -140,6 +166,11 @@ return 0 } +// IntersectHorizontal calculates the line string that +// results when cutting a triangle a a certain height. +// Can be empty (on intersection), +// one vertex (only touching an vertex) or +// two vertices (real intersection). func (t *Triangle) IntersectHorizontal(h float64) LineStringZ { sides := [3]int{ side(t[0].Z, h), @@ -213,12 +244,17 @@ } } +// EpsEquals returns true if v and w are equal component-wise +// with the values within a epsilon range. func (v Vertex) EpsEquals(w Vertex) bool { const eps = 1e-5 return math.Abs(v.X-w.X) < eps && math.Abs(v.Y-w.Y) < eps && math.Abs(v.Z-w.Z) < eps } +// JoinOnLine joins the the elements of a given multi line string +// under the assumption that the segments are all on the line segment +// from (x1, y1) to (x2, y2). func (mls MultiLineStringZ) JoinOnLine(x1, y1, x2, y2 float64) MultiLineStringZ { position := linearScale(x1, y1, x2, y2) @@ -264,11 +300,13 @@ out = append(out, curr) } - log.Printf("ignored parts: %d\n", ignored) + log.Printf("info: ignored parts: %d\n", ignored) return out } +// Write writes a Vertex as three 64 bit values in little endian order +// to the given writer. func (v *Vertex) Write(w io.Writer) error { if err := binary.Write( w, binary.LittleEndian, math.Float64bits(v.X)); err != nil { @@ -282,6 +320,8 @@ w, binary.LittleEndian, math.Float64bits(v.Z)) } +// Read fills this vertex with three 64 bit values stored as +// little endian from the given reader. func (v *Vertex) Read(r io.Reader) error { var buf [8]byte b := buf[:] @@ -300,6 +340,7 @@ return nil } +// AsWKB returns the WKB representation of the given multi line string. func (mls MultiLineStringZ) AsWKB() []byte { // pre-calculate size to avoid reallocations. @@ -328,6 +369,8 @@ return buf.Bytes() } +// AsWKB2D returns the WKB representation of the given multi line string +// leaving the z component out. func (mls MultiLineStringZ) AsWKB2D() []byte { // pre-calculate size to avoid reallocations. @@ -355,7 +398,7 @@ return buf.Bytes() } -// Join joins two lines leaving the first of the secoung out. +// Join joins two lines leaving the first of the second out. func (ls LineStringZ) Join(other LineStringZ) LineStringZ { nline := make(LineStringZ, len(ls)+len(other)-1) copy(nline, ls) @@ -363,6 +406,8 @@ return nline } +// Merge merges line segments of a given multi line string +// by finding common start and end vertices. func (mls MultiLineStringZ) Merge() MultiLineStringZ { var out MultiLineStringZ @@ -467,15 +512,13 @@ return out } +// Intersects checks if two Box2Ds intersect. func (a Box2D) Intersects(b Box2D) bool { return !(a.X2 < a.X1 || a.X2 < b.X1 || a.Y2 < a.Y1 || a.Y2 < b.Y1) } -func (a Box2D) Mid() (float64, float64) { - return (a.X2-a.X1)*0.5 + a.X1, (a.Y2-a.Y1)*0.5 + a.Y1 -} - +// Xi returns the i-th x component. func (a Box2D) Xi(i int) float64 { if i == 0 { return a.X1 @@ -483,6 +526,7 @@ return a.X2 } +// Yi returns the i-th y component. func (a Box2D) Yi(i int) float64 { if i == 0 { return a.Y1 @@ -490,6 +534,7 @@ return a.Y2 } +// NewPlane2D creates a new Plane2D from two given points. func NewPlane2D(x1, y1, x2, y2 float64) Plane2D { b := x2 - x1 a := -(y2 - y1) @@ -503,6 +548,9 @@ return Plane2D{a, b, c} } +// Eval determines the distance of a given point +// from the plane. The sign of the result indicates +// the sideness. func (p Plane2D) Eval(x, y float64) float64 { return p.A*x + p.B*y + p.C } @@ -516,6 +564,8 @@ return s | 1 } +// IntersectsPlane checks if a Box2D intersects with +// a given Plane2D. func (a Box2D) IntersectsPlane(p Plane2D) bool { var s int for i := 0; i < 2; i++ { @@ -537,6 +587,7 @@ return false } +// Cross calculates the cross product of two vertices. func (v Vertex) Cross(w Vertex) Vertex { return Vertex{ v.Y*w.Z - v.Z*w.Y, @@ -545,6 +596,9 @@ } } +// Intersection calcultes the 2D intersection point of +// two Plane2Ds. If they do not intersect the returned +// bool flags is set to false. func (p Plane2D) Intersection(o Plane2D) (float64, float64, bool) { u1 := Vertex{p.A, p.B, p.C} @@ -559,6 +613,7 @@ return plane.X / plane.Z, plane.Y / plane.Z, true } +// VerticalLine is a 2D line segment. type VerticalLine struct { x1 float64 y1 float64 @@ -568,6 +623,7 @@ line Plane2D } +// NewVerticalLine creates a new 2D line segment. func NewVerticalLine(x1, y1, x2, y2 float64) *VerticalLine { return &VerticalLine{ x1: x1, @@ -605,6 +661,7 @@ func inRange(a float64) bool { return 0 <= a && a <= 1 } +// Intersection intersects a line segment with a triangle. func (vl *VerticalLine) Intersection(t *Triangle) LineStringZ { var out LineStringZ @@ -780,6 +837,7 @@ return out } +// AsWKB returns a WKB representation of the given point cloud. func (mpz MultiPointZ) AsWKB() []byte { size := 1 + 4 + 4 + len(mpz)*(1+4+3*8)
--- a/pkg/scheduler/boot.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/scheduler/boot.go Tue Jan 15 10:07:10 2019 +0100 @@ -26,9 +26,13 @@ bootRole = "sys_admin" selectImportConfSQL = ` -SELECT id, username, kind, cron +SELECT id, username, cron FROM waterway.import_configuration WHERE cron IS NOT NULL` + + scheduledIDsSQL = ` +SELECT id from waterway.import_configuration +WHERE username = $1 AND cron IS NOT NULL` ) func init() { go boot() } @@ -57,13 +61,12 @@ var id int64 if err = rows.Scan( &id, - &ba.User, &ba.Name, &ba.Spec, ); err != nil { return false, err } - ba.CfgID = &id + ba.CfgID = id return true, nil }) if err != nil { @@ -75,3 +78,28 @@ log.Printf("error: %v\n", err) } } + +// ScheduledUserIDs returns the IDs with a schedule for a given user. +func ScheduledUserIDs( + ctx context.Context, + conn *sql.Conn, + user string, +) (map[int64]struct{}, error) { + ids := map[int64]struct{}{} + rows, err := conn.QueryContext(ctx, scheduledIDsSQL, user) + if err != nil { + return nil, nil + } + defer rows.Close() + for rows.Next() { + var id int64 + if err := rows.Scan(&id); err != nil { + return nil, err + } + ids[id] = struct{}{} + } + if err := rows.Err(); err != nil { + return nil, err + } + return ids, nil +}
--- a/pkg/scheduler/scheduler.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/scheduler/scheduler.go Tue Jan 15 10:07:10 2019 +0100 @@ -14,24 +14,19 @@ package scheduler import ( - "errors" "log" "sync" "github.com/robfig/cron" ) -// ErrNoSuchAction if no fitting action was found. -var ErrNoSuchAction = errors.New("No such action") - -// Action is called with a user and an optional configuration id. -type Action func(user string, cfgID *int64) +// Action is called with a configuration id. +type Action func(cfgID int64) type userAction struct { scheduler *scheduler - user string name string - cfgID *int64 + cfgID int64 } type scheduler struct { @@ -43,7 +38,7 @@ // Run implements cron.Job. func (ua *userAction) Run() { if a := ua.scheduler.action(ua.name); a != nil { - a(ua.user, ua.cfgID) + a(ua.cfgID) } else { log.Printf("warn: scheduled action '%s' not found.", ua.name) } @@ -65,13 +60,12 @@ } // BoundAction is a complete set of infos for -// an action to be bound to a user, schedule and -// optional configuration id. +// an action to be bound to a schedule and +// configuration id. type BoundAction struct { Name string Spec string - User string - CfgID *int64 + CfgID int64 } // BootActions setup the global scheduler with a set @@ -99,8 +93,6 @@ } job := &userAction{ scheduler: s, - user: ba.User, - name: ba.Name, cfgID: ba.CfgID, } cr.Schedule(schedule, job) @@ -116,16 +108,16 @@ return nil } -// BindAction binds a named action to a user, a cron spec and -// an optional configuration id. -func BindAction(name, spec, user string, cfgID *int64) error { - return global.bindAction(name, spec, user, cfgID) +// BindAction binds a named action to a cron spec and +// a configuration id. +func BindAction(name, spec string, cfgID int64) error { + return global.bindAction(name, spec, cfgID) } // UnbindAction unbinds a named action from a user and -// an optional configuration id. -func UnbindAction(name, user string, cfgID *int64) { - global.unbindAction(name, user, cfgID) +// a configuration id. +func UnbindAction(name string, cfgID int64) { + global.unbindAction(name, cfgID) } // UnbindByID unbinds all schedules with a given id. @@ -133,9 +125,9 @@ global.unbindByID(cfgID) } -// UnbindUser unbinds all schedules for a given user. -func UnbindUser(user string) { - global.unbindUser(user) +// UnbindByIDs unbinds all schedules for a given user. +func UnbindByIDs(ids map[int64]struct{}) { + global.unbindByIDs(ids) } // HasAction asks if there is an action with a given name. @@ -149,11 +141,7 @@ return s.actions[name] != nil } -func sameCfgID(a, b *int64) bool { - return (a == nil && b == nil) || (a != nil && b != nil && *a == *b) -} - -func (s *scheduler) unbindUser(user string) { +func (s *scheduler) unbindByIDs(ids map[int64]struct{}) { s.mu.Lock() defer s.mu.Unlock() @@ -166,8 +154,7 @@ var found bool for _, entry := range entries { ua := entry.Job.(*userAction) - if ua.user == user { - found = true + if _, found = ids[ua.cfgID]; found { break } } @@ -179,7 +166,7 @@ s.cr = cron.New() for _, entry := range entries { ua := entry.Job.(*userAction) - if ua.user != user { + if _, found := ids[ua.cfgID]; !found { s.cr.Schedule(entry.Schedule, entry.Job) } } @@ -195,7 +182,7 @@ var found bool for _, entry := range entries { ua := entry.Job.(*userAction) - if ua.cfgID != nil && *ua.cfgID == cfgID { + if ua.cfgID == cfgID { found = true break } @@ -209,14 +196,14 @@ s.cr = cron.New() for _, entry := range entries { ua := entry.Job.(*userAction) - if ua.cfgID == nil || *ua.cfgID != cfgID { + if ua.cfgID != cfgID { s.cr.Schedule(entry.Schedule, entry.Job) } } s.cr.Start() } -func (s *scheduler) unbindAction(name, user string, cfgID *int64) { +func (s *scheduler) unbindAction(name string, cfgID int64) { s.mu.Lock() defer s.mu.Unlock() @@ -225,8 +212,8 @@ var found *userAction for _, entry := range entries { ua := entry.Job.(*userAction) - if ua.name == name && ua.user == user && sameCfgID(cfgID, ua.cfgID) { - // Already have such a user/action/cfg tuple -> re-schedule. + if ua.name == name && cfgID == ua.cfgID { + // Already have such a action/cfg tuple -> re-schedule. found = ua break } @@ -247,7 +234,7 @@ s.cr.Start() } -func (s *scheduler) bindAction(name, spec, user string, cfgID *int64) error { +func (s *scheduler) bindAction(name, spec string, cfgID int64) error { schedule, err := cron.Parse(spec) if err != nil { @@ -262,7 +249,7 @@ var found *userAction for _, entry := range entries { ua := entry.Job.(*userAction) - if ua.name == name && ua.user == user && sameCfgID(cfgID, ua.cfgID) { + if ua.name == name && cfgID == ua.cfgID { // Already have such a user/action/cfg tuple -> re-schedule. found = ua break @@ -271,7 +258,7 @@ if found == nil { // Add to current plan. - job := &userAction{scheduler: s, user: user, name: name, cfgID: cfgID} + job := &userAction{scheduler: s, name: name, cfgID: cfgID} s.cr.Schedule(schedule, job) } else { // If found re-build all.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/soap/erdms/service.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,2499 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package erdms + +import ( + "encoding/xml" + "time" + + "gemma.intevation.de/gemma/pkg/soap" +) + +// against "unused imports" +var _ time.Time +var _ xml.Name + +// adn (dgs goods) internal adn code (unnr + seqnr: to make each adn good unique) +type AdnCodeType string + +// adn class and classifications codes +type AdnClassType string + +// adn (dgs) labels applicable for a dgs good +type AdnLabelType string + +// Older CBS (number) code (3 digits) use by statistics organisations (backw comp) but replaced by newer iso 3166 (2 letters). +type CbscountryCodeType string + +// iso 6346 container type (code) +type ContIdType string + +// containertype textual description +type ContNameType string + +// indication (flag) wheater shiptype code is a single ship or a transport combination +type CombinationFlagType bool + +// Country iso 3166 code (2 alpha) +type CountryCodeType string + +// Country iso 3166 code (3 alpha) +type CountryCode3Type string + +// Countryname type (name of the country) +type CountryNameType string + +// Country iso 3166 numcode (3 digits) +type CountryIsoNumType string + +// techn directive craft type code (eu hull db) +type CraftTypeType string + +// inland cones 0,1,2,3,V (as used for DGS goods and cones calculations) +type ConesType string + +const ( + ConesType0 ConesType = "0" + + ConesType1 ConesType = "1" + + ConesType2 ConesType = "2" + + ConesType3 ConesType = "3" + + ConesTypeV ConesType = "V" +) + +// dimension type (length, width, draught etc) in [cm] (no decimals) +type DimType int64 + +// older 2 digit dvk shiptype code (replaced by unrec +type DvkTypeType string + +// record erased (not actual anymore) flag type +type ErasedType bool + +// eri location code type as used for eri_locations +type ErilocCodeType string + +// eri location name type as used in eri_locations +type ErilocNameType string + +// flags 0,B,V (dang goods indication seagoing vessels) +type FlagsType string + +const ( + FlagsType0 FlagsType = "0" + + FlagsTypeB FlagsType = "B" + + FlagsTypeV FlagsType = "V" +) + +// fairway code (text) +type FwCodeType string + +// ADN dangerous goodnames, description +type GoodDgsNameType string + +// non-dangerous goodnames, description +type GoodNameType string + +// ADN dangerous good synonym name,description +type GoodSynType string + +// HS code as used by customs +type HsCodeType string + +// hectometre code (hectomere in [hm] (no decimals) on the fairway) +type KmCodeType string + +// lastupdate, last modification date time of this record +type LastupdateType time.Time + +// one of the supported languages (basic and additional set), based on ISO 639-1 +type LangType string + +const ( + LangTypeXX LangType = "%%" + + LangTypeLc LangType = "lc" + + LangTypeNl LangType = "nl" + + LangTypeDe LangType = "de" + + LangTypeFr LangType = "fr" + + LangTypeEn LangType = "en" + + LangTypeBg LangType = "bg" + + LangTypeCs LangType = "cs" + + LangTypeDa LangType = "da" + + LangTypeEl LangType = "el" + + LangTypeEs LangType = "es" + + LangTypeEt LangType = "et" + + LangTypeFi LangType = "fi" + + LangTypeHu LangType = "hu" + + LangTypeIt LangType = "it" + + LangTypeLt LangType = "lt" + + LangTypeLv LangType = "lv" + + LangTypePl LangType = "pl" + + LangTypePt LangType = "pt" + + LangTypeRo LangType = "ro" + + LangTypeSk LangType = "sk" + + LangTypeSl LangType = "sl" + + LangTypeSv LangType = "sv" + + LangTypeHr LangType = "hr" + + LangTypeRu LangType = "ru" + + LangTypeSr LangType = "sr" +) + +// lat long (WGS decimal [.] coordinates) coordinate type +type LatlonType float64 + +// un locode (2 country + 3 location code) +type LoCodeType string + +// Number of possible (departure) exist (routes) for a location +type LocExitsType int32 + +// VTS/IVS (code)name active in certain area and acting as a reporting point (when departing from that location) +type LocVtsCodeType string + +// location type, terminal type (a number indicating type of location,terminal) +type LocTypeType int32 + +// lloyds flag code for a certain country (seagoing) +type LloydsflagType string + +// NST code (corresponing nst2007 EU statistics codes for goods). +type NstCodeType string + +// NtS (Notices to Skippers) code/Value datatype +type NtsCodeType string + +// NtS name/description (belonging to a ntsCode) datatype +type NtsNameType string + +// NtS subtype (sub data type, there are several nts tables) +type NtstypeType string + +// ADN dangerous good packing group I, II, III +type PackGrpType string + +// UN rec 21 inner package code +type PackIdType string + +// Un rec 21 inner package name/description +type PackNameType string + +// Password type (belonging to a user, used to identify a user and its role and home country). +type PasswordType string + +// quay type (terminals), indicating quay nrs/id's +type QuayType string + +// record counter (nr of records available or in a set) +type RecCountType int64 + +// reftype Type (reftool data reftype, such as: risidx, eri_location, eri_hscode, nts_data) +type ReftypeType string + +const ( + ReftypeTypeRis_idx ReftypeType = "ris_idx" + + ReftypeTypeEri_location ReftypeType = "eri_location" + + ReftypeTypeEri_hscode ReftypeType = "eri_hscode" + + ReftypeTypeEri_adncode ReftypeType = "eri_adncode" + + ReftypeTypeEri_conttype ReftypeType = "eri_conttype" + + ReftypeTypeEri_packtype ReftypeType = "eri_packtype" + + ReftypeTypeEri_shiptype ReftypeType = "eri_shiptype" + + ReftypeTypeEri_country ReftypeType = "eri_country" + + ReftypeTypeNts_barrage ReftypeType = "nts_barrage" + + ReftypeTypeNts_communication ReftypeType = "nts_communication" + + ReftypeTypeNts_country ReftypeType = "nts_country" + + ReftypeTypeNts_direction ReftypeType = "nts_direction" + + ReftypeTypeNts_direction_max ReftypeType = "nts_direction_max" + + ReftypeTypeNts_direction_min ReftypeType = "nts_direction_min" + + ReftypeTypeNts_gui_labels ReftypeType = "nts_gui_labels" + + ReftypeTypeNts_ice_accessibility ReftypeType = "nts_ice_accessibility" + + ReftypeTypeNts_ice_condition ReftypeType = "nts_ice_condition" + + ReftypeTypeNts_ice_classification ReftypeType = "nts_ice_classification" + + ReftypeTypeNts_ice_situation ReftypeType = "nts_ice_situation" + + ReftypeTypeNts_indication ReftypeType = "nts_indication" + + ReftypeTypeNts_interval ReftypeType = "nts_interval" + + ReftypeTypeNts_language ReftypeType = "nts_language" + + ReftypeTypeNts_limitation ReftypeType = "nts_limitation" + + ReftypeTypeNts_measure ReftypeType = "nts_measure" + + ReftypeTypeNts_position ReftypeType = "nts_position" + + ReftypeTypeNts_reason ReftypeType = "nts_reason" + + ReftypeTypeNts_reference ReftypeType = "nts_reference" + + ReftypeTypeNts_regime ReftypeType = "nts_regime" + + ReftypeTypeNts_reporting ReftypeType = "nts_reporting" + + ReftypeTypeNts_subject ReftypeType = "nts_subject" + + ReftypeTypeNts_tag ReftypeType = "nts_tag" + + ReftypeTypeNts_target_group ReftypeType = "nts_target_group" + + ReftypeTypeNts_type ReftypeType = "nts_type" + + ReftypeTypeNts_weather_category ReftypeType = "nts_weather_category" + + ReftypeTypeNts_weather_class ReftypeType = "nts_weather_class" + + ReftypeTypeNts_weather_direction ReftypeType = "nts_weather_direction" + + ReftypeTypeNts_weather_item ReftypeType = "nts_weather_item" +) + +// RefData record version numbering (automaically, starts with 1 and increments on updates, requesting version 0=dont care) +type RefrecVersionType int64 + +// remarks string (remarks about the maintance of a record or any other remarks) +type RemarksType string + +// ris index (ISRS) code type (full ris index code) +type RisCodeType string + +// locationames as used in the ris index +type RislocNameType string + +// ris idx related ENC's type as used in ris idx (list of ENC id's) +type RisrelencType string + +// ris idx object code type (single code for a junction, termcode etc padded), subpart of the ris idx. +type RisobjCodeType string + +// ris idx communication information TXTDSC +type RiscomminfoType string + +// ris idx object function code type (junction, berths etc) +type RisobjfuncType string + +// ris idx section node id +type RissectnodeType string + +// ris idx national Gauge Code type (can be utilised to store any national coding/id/link for gauges) +type RisgaugeidType string + +// ris idx applicability from / to km (in hectometres (no decimals)) +type RisapplikmType int64 + +// ris idx reference level code (indicating the ref level) +type RisreflevelcodeType string + +// ris idx reference level value [cm] +type RisreflevelvalueType int64 + +// ris idx geod reference +type RisgeodrefType string + +// ris idx zero point [cm] +type RiszeropointType int64 + +// ris idx category of time schedule (cattab) +type RiscatoftimeschedType string + +// ris idx for ship type +type RisforshiptypeType string + +// ris idx use of ship useshp +type RisuseofshipType string + +// ris idx link to external XML file +type RislinkextxmlType string + +// UNrec 28 shiptype (4 digits) +type ShipTypeType string + +// shiptype name (textual) description +type ShiptypeNameType string + +// source string, to specify the source of this record/data or the update. +type SourceType string + +// ADN signal code (to group dgs for cones/flags calculations) +type SignalCodeType string + +// terminal code type (remark: termcode is the only part of eri locs with variable length, historical reasons). +type TermCodeType string + +// unlocode countrycode part (first 2 of unlocode) as used in ris idx +type UnlocCCType string + +// unlocode location code part (last 3 of unlocode) as used in ris idx +type UnlocLCType string + +// UN nr (4 digits) adn nr for identifying ADN dangerous goods (according CCNR) +type UnnrType string + +// reftool username(reftool accountname) type, to idetify a user and its role. +type UserNameType string + +// cargo weight type, integer nr (units [tons], [kg]) depending on element) +type WeightType int64 + +// adn warning (dgs) card id +type WarnIdType string + +// adn warning card +type WarnCardType string + +type MatchByCode struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByCode"` + + *MatchByCodeType +} + +type MatchByCodeResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByCodeResponse"` + + *MatchByCodeResponseType +} + +type MatchByName struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByName"` + + *MatchByNameType +} + +type MatchByNameResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByNameResponse"` + + *MatchByNameResponseType +} + +type GetDataXML struct { + XMLName xml.Name `xml:"http://rwsreftool/ getDataXML"` + + *GetDataXMLType +} + +type GetDataXMLResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ getDataXMLResponse"` + + *GetDataXMLResponseType +} + +type GetMutations struct { + XMLName xml.Name `xml:"http://rwsreftool/ getMutations"` + + *GetMutationsType +} + +type GetMutationsResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ getMutationsResponse"` + + *GetMutationsResponseType +} + +type GetRisDataXML struct { + XMLName xml.Name `xml:"http://rwsreftool/ getRisDataXML"` + + *GetRisDataXMLType +} + +type GetRisDataXMLResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ getRisDataXMLResponse"` + + *GetRisDataXMLResponseType +} + +type MutateDataXML struct { + XMLName xml.Name `xml:"http://rwsreftool/ mutateDataXML"` + + *MutateDataXMLType +} + +type MutateDataXMLResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ mutateDataXMLResponse"` + + *MutateDataXMLResponseType +} + +type RequestMutationXML struct { + XMLName xml.Name `xml:"http://rwsreftool/ requestMutationXML"` + + *RequestMutationXMLType +} + +type RequestMutationXMLResponse struct { + XMLName xml.Name `xml:"http://rwsreftool/ requestMutationXMLResponse"` + + *RequestMutationXMLResponseType +} + +type MatchByCodeType struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByCodeType"` + + Reftype *ReftypeType `xml:"reftype,omitempty"` + + Code string `xml:"code,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` +} + +type MatchByCodeResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByCodeResponseType"` + + // Indicates how many records are available/were found + // (due limitations less records can be returned/available in the resulting xml structure). + RecCount *RecCountType `xml:"recCount,omitempty"` + + RefdataReturn []*RefdataReturnType `xml:"refdataReturn,omitempty"` +} + +type MatchByNameType struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByNameType"` + + Reftype *ReftypeType `xml:"reftype,omitempty"` + + Lang *LangType `xml:"lang,omitempty"` + + Name string `xml:"name,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` +} + +type MatchByNameResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ matchByNameResponseType"` + + // Indicates how many records are available/were found + // (due limitations less records can be returned/available in the resulting xml structure). + RecCount *RecCountType `xml:"recCount,omitempty"` + + RefdataReturn []*RefdataReturnType `xml:"refdataReturn,omitempty"` +} + +type GetMutationsType struct { + XMLName xml.Name `xml:"http://rwsreftool/ getMutationsType"` + + Reftype *ReftypeType `xml:"reftype,omitempty"` + + // from, to in datetime format in UTC time + FromDate time.Time `xml:"fromDate,omitempty"` + + ToDate time.Time `xml:"toDate,omitempty"` +} + +type GetMutationsResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ getMutationsResponseType"` + + // Indicate how many records are available/were found. + RecCount *RecCountType `xml:"recCount,omitempty"` + + RefdataReturn []*RefdataReturnType `xml:"refdataReturn,omitempty"` +} + +type GetDataXMLType struct { + XMLName xml.Name `xml:"http://rwsreftool/ getDataXMLType"` + + Reftype *ReftypeType `xml:"reftype,omitempty"` + + Subcode string `xml:"subcode,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` +} + +type GetDataXMLResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ getDataXMLResponseType"` + + // Indicates how many records are available/were found. + RecCount *RecCountType `xml:"recCount,omitempty"` + + RefdataReturn []*RefdataReturnType `xml:"refdataReturn,omitempty"` +} + +type GetRisDataXMLType struct { + XMLName xml.Name `xml:"http://rwsreftool/ getRisDataXMLType"` + + Subcode string `xml:"subcode,omitempty"` + + Funcode string `xml:"funcode,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` +} + +type GetRisDataXMLResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ getRisDataXMLResponseType"` + + // Indicates how many records are available/were found. + RecCount *RecCountType `xml:"recCount,omitempty"` + + RisdataReturn []*Ris_idxType `xml:"risdataReturn,omitempty"` +} + +type MutateDataXMLType struct { + XMLName xml.Name `xml:"http://rwsreftool/ mutateDataXMLType"` + + Username *UserNameType `xml:"username,omitempty"` + + Password *PasswordType `xml:"password,omitempty"` + + Reftype *ReftypeType `xml:"reftype,omitempty"` + + RefData *RefdataReturnType `xml:"refData,omitempty"` +} + +type MutateDataXMLResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ mutateDataXMLResponseType"` + + *StatReturnType +} + +type RequestMutationXMLType struct { + XMLName xml.Name `xml:"http://rwsreftool/ requestMutationXMLType"` + + Username *UserNameType `xml:"username,omitempty"` + + Password *PasswordType `xml:"password,omitempty"` + + Reftype *ReftypeType `xml:"reftype,omitempty"` + + InUserList bool `xml:"inUserList,omitempty"` + + RefDataReqMut *RefdataReqMutType `xml:"refDataReqMut,omitempty"` +} + +type RequestMutationXMLResponseType struct { + XMLName xml.Name `xml:"http://rwsreftool/ requestMutationXMLResponseType"` + + *StatReturnType +} + +type ExceptionType struct { + XMLName xml.Name `xml:"http://rwsreftool/ exception"` + + // exception code + Exception int32 `xml:"exception,omitempty"` + + // exception message in English + Message string `xml:"message,omitempty"` +} + +type RefdataReturnType struct { + XMLName xml.Name `xml:"http://rwsreftool/ refdataReturnType"` + + Reftype struct { + Value *ReftypeType + } `xml:"reftype,omitempty"` + + Ris_idx *Ris_idxType `xml:"ris_idx,omitempty"` + + Eri_location *Eri_locationType `xml:"eri_location,omitempty"` + + Eri_hscode *Eri_hscodeType `xml:"eri_hscode,omitempty"` + + Eri_adncode struct { + *Eri_adncodeType + } `xml:"eri_adncode,omitempty"` + + Eri_conttype *Eri_conttypeType `xml:"eri_conttype,omitempty"` + + Eri_packtype *Eri_packtypeType `xml:"eri_packtype,omitempty"` + + Eri_shiptype *Eri_shiptypeType `xml:"eri_shiptype,omitempty"` + + Eri_country *Eri_countryType `xml:"eri_country,omitempty"` + + // NtS element is only available in the return refdata structure. + // It is not available in the Req Mutation datastructure because we can not mutate nts data (not allowed)! + Nts_data *Nts_dataType `xml:"nts_data,omitempty"` +} + +type RefdataReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ refdataReqMutType"` + + // BE SURE to edit the basic (simple)types, DONT edit any of the subelements from here! + Reftype *ReftypeType `xml:"reftype,omitempty"` + + Ris_idx *Ris_idxReqMutType `xml:"ris_idx,omitempty"` + + Eri_location *Eri_locationReqMutType `xml:"eri_location,omitempty"` + + Eri_hscode *Eri_hscodeReqMutType `xml:"eri_hscode,omitempty"` + + Eri_adncode *Eri_adncodeReqMutType `xml:"eri_adncode,omitempty"` + + Eri_conttype *Eri_conttypeReqMutType `xml:"eri_conttype,omitempty"` + + Eri_packtype *Eri_packtypeReqMutType `xml:"eri_packtype,omitempty"` + + Eri_shiptype *Eri_shiptypeReqMutType `xml:"eri_shiptype,omitempty"` + + Eri_country *Eri_countryReqMutType `xml:"eri_country,omitempty"` +} + +type StatReturnType struct { + XMLName xml.Name `xml:"http://rwsreftool/ statReturnType"` + + // 0=ok, 1=warning, 2=error + Err int32 `xml:"err,omitempty"` + + Message struct { + } `xml:"message,omitempty"` +} + +type Ris_idxType struct { + XMLName xml.Name `xml:"http://rwsreftool/ ris_idxType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + RisidxCode *RisCodeType `xml:"risidxCode,omitempty"` + + CodeOld *RisCodeType `xml:"codeOld,omitempty"` + + UnlocCC *UnlocCCType `xml:"unlocCC,omitempty"` + + UnlocLC *UnlocLCType `xml:"unlocLC,omitempty"` + + WwsectCode *FwCodeType `xml:"wwsectCode,omitempty"` + + Objcode *RisobjCodeType `xml:"objcode,omitempty"` + + Hectomt *KmCodeType `xml:"hectomt,omitempty"` + + Objfunc *RisobjfuncType `xml:"objfunc,omitempty"` + + Objname struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"objname,omitempty"` + + Locname struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"locname,omitempty"` + + Wwname struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"wwname,omitempty"` + + Routename struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"routename,omitempty"` + + Relrisindex *RisCodeType `xml:"relrisindex,omitempty"` + + Sectionnode *RissectnodeType `xml:"sectionnode,omitempty"` + + Lat *LatlonType `xml:"lat,omitempty"` + + Lon *LatlonType `xml:"lon,omitempty"` + + Relenc *RisrelencType `xml:"relenc,omitempty"` + + Comminfo *RiscomminfoType `xml:"comminfo,omitempty"` + + Gaugeid *RisgaugeidType `xml:"gaugeid,omitempty"` + + Vesselconvlength *DimType `xml:"vesselconvlength,omitempty"` + + Vesselconvbreadth *DimType `xml:"vesselconvbreadth,omitempty"` + + Vesselconvdraught *DimType `xml:"vesselconvdraught,omitempty"` + + Vesselconvairdraught *DimType `xml:"vesselconvairdraught,omitempty"` + + Availablelength *DimType `xml:"availablelength,omitempty"` + + Clearancewidth *DimType `xml:"clearancewidth,omitempty"` + + Availabledepth *DimType `xml:"availabledepth,omitempty"` + + Clearanceheight *DimType `xml:"clearanceheight,omitempty"` + + Applicabilityfromkm *RisapplikmType `xml:"applicabilityfromkm,omitempty"` + + Applicabilitytokm *RisapplikmType `xml:"applicabilitytokm,omitempty"` + + Reflevel1code *RisreflevelcodeType `xml:"reflevel1code,omitempty"` + + Reflevel1value *RisreflevelvalueType `xml:"reflevel1value,omitempty"` + + Reflevel2code *RisreflevelcodeType `xml:"reflevel2code,omitempty"` + + Reflevel2value *RisreflevelvalueType `xml:"reflevel2value,omitempty"` + + Reflevel3code *RisreflevelcodeType `xml:"reflevel3code,omitempty"` + + Reflevel3value *RisreflevelvalueType `xml:"reflevel3value,omitempty"` + + Zeropoint *RiszeropointType `xml:"zeropoint,omitempty"` + + Geodref *RisgeodrefType `xml:"geodref,omitempty"` + + Catoftimesched *RiscatoftimeschedType `xml:"catoftimesched,omitempty"` + + Forshiptype *RisforshiptypeType `xml:"forshiptype,omitempty"` + + Foruseofship *RisuseofshipType `xml:"foruseofship,omitempty"` + + LnktoextXMfiletimesched *RislinkextxmlType `xml:"lnktoextXMfiletimesched,omitempty"` + + LnktoextXMfilepastime *RislinkextxmlType `xml:"lnktoextXMfilepastime,omitempty"` + + Vesselconvlength2 *DimType `xml:"vesselconvlength2,omitempty"` + + Vesselconvbreadth2 *DimType `xml:"vesselconvbreadth2,omitempty"` + + Vesselconvdraught2 *DimType `xml:"vesselconvdraught2,omitempty"` + + Vesselconvairdraught2 *DimType `xml:"vesselconvairdraught2,omitempty"` + + Availablelength2 *DimType `xml:"availablelength2,omitempty"` + + Clearancewidth2 *DimType `xml:"clearancewidth2,omitempty"` + + Availabledepth2 *DimType `xml:"availabledepth2,omitempty"` + + Clearanceheight2 *DimType `xml:"clearanceheight2,omitempty"` + + Vesselconvlength3 *DimType `xml:"vesselconvlength3,omitempty"` + + Vesselconvbreadth3 *DimType `xml:"vesselconvbreadth3,omitempty"` + + Vesselconvdraught3 *DimType `xml:"vesselconvdraught3,omitempty"` + + Vesselconvairdraught3 *DimType `xml:"vesselconvairdraught3,omitempty"` + + Availablelength3 *DimType `xml:"availablelength3,omitempty"` + + Clearancewidth3 *DimType `xml:"clearancewidth3,omitempty"` + + Availabledepth3 *DimType `xml:"availabledepth3,omitempty"` + + Clearanceheight3 *DimType `xml:"clearanceheight3,omitempty"` + + Catoftimesched2 *RiscatoftimeschedType `xml:"catoftimesched2,omitempty"` + + Forshiptype2 *RisforshiptypeType `xml:"forshiptype2,omitempty"` + + Foruseofship2 *RisuseofshipType `xml:"foruseofship2,omitempty"` + + LnktoextXMfiletimesched2 *RislinkextxmlType `xml:"lnktoextXMfiletimesched2,omitempty"` + + LnktoextXMfilepastime2 *RislinkextxmlType `xml:"lnktoextXMfilepastime2,omitempty"` + + Catoftimesched3 *RiscatoftimeschedType `xml:"catoftimesched3,omitempty"` + + Forshiptype3 *RisforshiptypeType `xml:"forshiptype3,omitempty"` + + Foruseofship3 *RisuseofshipType `xml:"foruseofship3,omitempty"` + + LnktoextXMfiletimesched3 *RislinkextxmlType `xml:"lnktoextXMfiletimesched3,omitempty"` + + LnktoextXMfilepastime3 *RislinkextxmlType `xml:"lnktoextXMfilepastime3,omitempty"` + + Startdate *LastupdateType `xml:"startdate,omitempty"` + + Enddate *LastupdateType `xml:"enddate,omitempty"` + + Infodate *LastupdateType `xml:"infodate,omitempty"` + + // specific Ris idx remarks, reasons for change (descriptions as received with the ris idx) + RisidxRemarks *RemarksType `xml:"risidxRemarks,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + // remarks regarding the maintance of the ris idx + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_locationType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_locationType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + ErilocCode *ErilocCodeType `xml:"erilocCode,omitempty"` + + CodeOld *ErilocCodeType `xml:"codeOld,omitempty"` + + RisCode *RisCodeType `xml:"risCode,omitempty"` + + LoCode *LoCodeType `xml:"loCode,omitempty"` + + FwCode *FwCodeType `xml:"fwCode,omitempty"` + + TermCode *TermCodeType `xml:"termCode,omitempty"` + + KmCode *KmCodeType `xml:"kmCode,omitempty"` + + Name struct { + Loc *ErilocNameType `xml:"Loc,omitempty"` + + NL *ErilocNameType `xml:"NL,omitempty"` + + DE *ErilocNameType `xml:"DE,omitempty"` + + FR *ErilocNameType `xml:"FR,omitempty"` + + EN *ErilocNameType `xml:"EN,omitempty"` + } `xml:"name,omitempty"` + + LocType *LocTypeType `xml:"locType,omitempty"` + + Termname struct { + Loc *ErilocNameType `xml:"Loc,omitempty"` + + NL *ErilocNameType `xml:"NL,omitempty"` + + DE *ErilocNameType `xml:"DE,omitempty"` + + FR *ErilocNameType `xml:"FR,omitempty"` + + EN *ErilocNameType `xml:"EN,omitempty"` + } `xml:"termname,omitempty"` + + QuayFrom *QuayType `xml:"quayFrom,omitempty"` + + QuayTo *QuayType `xml:"quayTo,omitempty"` + + TermType *LocTypeType `xml:"termType,omitempty"` + + Lat *LatlonType `xml:"lat,omitempty"` + + Lon *LatlonType `xml:"lon,omitempty"` + + // VTS/IVS (code) acting as the default reporting (receiving) point (when departing from this llocation) + IvsVTSCode *LocVtsCodeType `xml:"ivsVTSCode,omitempty"` + + // Nr of possible exits (routes) when departing from this location >1 means a reporting or routepoint has to be specified. + Exits *LocExitsType `xml:"exits,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_hscodeType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_hscodeType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + HsCode *HsCodeType `xml:"hsCode,omitempty"` + + CodeOld *HsCodeType `xml:"codeOld,omitempty"` + + Name struct { + Loc *GoodNameType `xml:"Loc,omitempty"` + + NL *GoodNameType `xml:"NL,omitempty"` + + DE *GoodNameType `xml:"DE,omitempty"` + + FR *GoodNameType `xml:"FR,omitempty"` + + EN *GoodNameType `xml:"EN,omitempty"` + + BG *GoodNameType `xml:"BG,omitempty"` + + CS *GoodNameType `xml:"CS,omitempty"` + + DA *GoodNameType `xml:"DA,omitempty"` + + EL *GoodNameType `xml:"EL,omitempty"` + + ES *GoodNameType `xml:"ES,omitempty"` + + ET *GoodNameType `xml:"ET,omitempty"` + + FI *GoodNameType `xml:"FI,omitempty"` + + HU *GoodNameType `xml:"HU,omitempty"` + + IT *GoodNameType `xml:"IT,omitempty"` + + LT *GoodNameType `xml:"LT,omitempty"` + + LV *GoodNameType `xml:"LV,omitempty"` + + PL *GoodNameType `xml:"PL,omitempty"` + + PT *GoodNameType `xml:"PT,omitempty"` + + RO *GoodNameType `xml:"RO,omitempty"` + + SK *GoodNameType `xml:"SK,omitempty"` + + SL *GoodNameType `xml:"SL,omitempty"` + + SV *GoodNameType `xml:"SV,omitempty"` + + HR *GoodNameType `xml:"HR,omitempty"` + + RU *GoodNameType `xml:"RU,omitempty"` + + SR *GoodNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + NstCode *NstCodeType `xml:"nstCode,omitempty"` + + // English (NST2007) description. + NstName *GoodNameType `xml:"nstName,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_adncodeType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_adncodeType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + AdnCode *AdnCodeType `xml:"adnCode,omitempty"` + + CodeOld *AdnCodeType `xml:"codeOld,omitempty"` + + Unnr *UnnrType `xml:"unnr,omitempty"` + + AdnClass *AdnClassType `xml:"adnClass,omitempty"` + + AdnClassif *AdnClassType `xml:"adnClassif,omitempty"` + + ImoClass *AdnClassType `xml:"imoClass,omitempty"` + + PackGrp *PackGrpType `xml:"packGrp,omitempty"` + + Name struct { + Loc *GoodDgsNameType `xml:"Loc,omitempty"` + + NL *GoodDgsNameType `xml:"NL,omitempty"` + + DE *GoodDgsNameType `xml:"DE,omitempty"` + + FR *GoodDgsNameType `xml:"FR,omitempty"` + + EN *GoodDgsNameType `xml:"EN,omitempty"` + + BG *GoodDgsNameType `xml:"BG,omitempty"` + + CS *GoodDgsNameType `xml:"CS,omitempty"` + + DA *GoodDgsNameType `xml:"DA,omitempty"` + + EL *GoodDgsNameType `xml:"EL,omitempty"` + + ES *GoodDgsNameType `xml:"ES,omitempty"` + + ET *GoodDgsNameType `xml:"ET,omitempty"` + + FI *GoodDgsNameType `xml:"FI,omitempty"` + + HU *GoodDgsNameType `xml:"HU,omitempty"` + + IT *GoodDgsNameType `xml:"IT,omitempty"` + + LT *GoodDgsNameType `xml:"LT,omitempty"` + + LV *GoodDgsNameType `xml:"LV,omitempty"` + + PL *GoodDgsNameType `xml:"PL,omitempty"` + + PT *GoodDgsNameType `xml:"PT,omitempty"` + + RO *GoodDgsNameType `xml:"RO,omitempty"` + + SK *GoodDgsNameType `xml:"SK,omitempty"` + + SL *GoodDgsNameType `xml:"SL,omitempty"` + + SV *GoodDgsNameType `xml:"SV,omitempty"` + + HR *GoodDgsNameType `xml:"HR,omitempty"` + + RU *GoodDgsNameType `xml:"RU,omitempty"` + + SR *GoodDgsNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Syn struct { + Loc *GoodSynType `xml:"Loc,omitempty"` + + NL *GoodSynType `xml:"NL,omitempty"` + + DE *GoodSynType `xml:"DE,omitempty"` + + FR *GoodSynType `xml:"FR,omitempty"` + + EN *GoodSynType `xml:"EN,omitempty"` + + BG *GoodSynType `xml:"BG,omitempty"` + + CS *GoodSynType `xml:"CS,omitempty"` + + DA *GoodSynType `xml:"DA,omitempty"` + + EL *GoodSynType `xml:"EL,omitempty"` + + ES *GoodSynType `xml:"ES,omitempty"` + + ET *GoodSynType `xml:"ET,omitempty"` + + FI *GoodSynType `xml:"FI,omitempty"` + + HU *GoodSynType `xml:"HU,omitempty"` + + IT *GoodSynType `xml:"IT,omitempty"` + + LT *GoodSynType `xml:"LT,omitempty"` + + LV *GoodSynType `xml:"LV,omitempty"` + + PL *GoodSynType `xml:"PL,omitempty"` + + PT *GoodSynType `xml:"PT,omitempty"` + + RO *GoodSynType `xml:"RO,omitempty"` + + SK *GoodSynType `xml:"SK,omitempty"` + + SL *GoodSynType `xml:"SL,omitempty"` + + SV *GoodSynType `xml:"SV,omitempty"` + + HR *GoodSynType `xml:"HR,omitempty"` + + RU *GoodSynType `xml:"RU,omitempty"` + + SR *GoodSynType `xml:"SR,omitempty"` + } `xml:"syn,omitempty"` + + HsCode *HsCodeType `xml:"hsCode,omitempty"` + + NstCode *NstCodeType `xml:"nstCode,omitempty"` + + // 0, 1, 2, 3, V + ConesBulk *ConesType `xml:"conesBulk,omitempty"` + + ConesTank *ConesType `xml:"conesTank,omitempty"` + + ConesCont *ConesType `xml:"conesCont,omitempty"` + + // 0, B, V + FlagsBulk *FlagsType `xml:"flagsBulk,omitempty"` + + FlagsTank *FlagsType `xml:"flagsTank,omitempty"` + + WarnCard *WarnCardType `xml:"warnCard,omitempty"` + + WarnId *WarnIdType `xml:"warnId,omitempty"` + + Label1 *AdnLabelType `xml:"label1,omitempty"` + + Label2 *AdnLabelType `xml:"label2,omitempty"` + + Label3 *AdnLabelType `xml:"label3,omitempty"` + + MaxWeightCodeInlandBulkCont *SignalCodeType `xml:"maxWeightCodeInlandBulkCont,omitempty"` + + // max weight in kg + MaxWeightInlandBulkCont *WeightType `xml:"maxWeightInlandBulkCont,omitempty"` + + SignalCodeInlandCont *SignalCodeType `xml:"signalCodeInlandCont,omitempty"` + + // min weight in kg + MinWeightInlandCont *WeightType `xml:"minWeightInlandCont,omitempty"` + + SignalCodeInlandExcemp *SignalCodeType `xml:"signalCodeInlandExcemp,omitempty"` + + // min weight in kg + MinWeightInlandExcemp *WeightType `xml:"minWeightInlandExcemp,omitempty"` + + SignalCodeSeaBulk *SignalCodeType `xml:"signalCodeSeaBulk,omitempty"` + + // min weight in kg + MinWeightSeaBulk *WeightType `xml:"minWeightSeaBulk,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_conttypeType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_conttypeType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + ContId *ContIdType `xml:"contId,omitempty"` + + CodeOld *ContIdType `xml:"codeOld,omitempty"` + + // dimensions in cm + Length *DimType `xml:"length,omitempty"` + + Width *DimType `xml:"width,omitempty"` + + Height *DimType `xml:"height,omitempty"` + + // empty weight in kg + Weight *WeightType `xml:"weight,omitempty"` + + Name struct { + Loc *ContNameType `xml:"Loc,omitempty"` + + NL *ContNameType `xml:"NL,omitempty"` + + DE *ContNameType `xml:"DE,omitempty"` + + FR *ContNameType `xml:"FR,omitempty"` + + EN *ContNameType `xml:"EN,omitempty"` + + BG *ContNameType `xml:"BG,omitempty"` + + CS *ContNameType `xml:"CS,omitempty"` + + DA *ContNameType `xml:"DA,omitempty"` + + EL *ContNameType `xml:"EL,omitempty"` + + ES *ContNameType `xml:"ES,omitempty"` + + ET *ContNameType `xml:"ET,omitempty"` + + FI *ContNameType `xml:"FI,omitempty"` + + HU *ContNameType `xml:"HU,omitempty"` + + IT *ContNameType `xml:"IT,omitempty"` + + LT *ContNameType `xml:"LT,omitempty"` + + LV *ContNameType `xml:"LV,omitempty"` + + PL *ContNameType `xml:"PL,omitempty"` + + PT *ContNameType `xml:"PT,omitempty"` + + RO *ContNameType `xml:"RO,omitempty"` + + SK *ContNameType `xml:"SK,omitempty"` + + SL *ContNameType `xml:"SL,omitempty"` + + SV *ContNameType `xml:"SV,omitempty"` + + HR *ContNameType `xml:"HR,omitempty"` + + RU *ContNameType `xml:"RU,omitempty"` + + SR *ContNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_packtypeType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_packtypeType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + PackId *PackIdType `xml:"packId,omitempty"` + + CodeOld *PackIdType `xml:"codeOld,omitempty"` + + Name struct { + Loc *PackNameType `xml:"Loc,omitempty"` + + NL *PackNameType `xml:"NL,omitempty"` + + DE *PackNameType `xml:"DE,omitempty"` + + FR *PackNameType `xml:"FR,omitempty"` + + EN *PackNameType `xml:"EN,omitempty"` + + BG *PackNameType `xml:"BG,omitempty"` + + CS *PackNameType `xml:"CS,omitempty"` + + DA *PackNameType `xml:"DA,omitempty"` + + EL *PackNameType `xml:"EL,omitempty"` + + ES *PackNameType `xml:"ES,omitempty"` + + ET *PackNameType `xml:"ET,omitempty"` + + FI *PackNameType `xml:"FI,omitempty"` + + HU *PackNameType `xml:"HU,omitempty"` + + IT *PackNameType `xml:"IT,omitempty"` + + LT *PackNameType `xml:"LT,omitempty"` + + LV *PackNameType `xml:"LV,omitempty"` + + PL *PackNameType `xml:"PL,omitempty"` + + PT *PackNameType `xml:"PT,omitempty"` + + RO *PackNameType `xml:"RO,omitempty"` + + SK *PackNameType `xml:"SK,omitempty"` + + SL *PackNameType `xml:"SL,omitempty"` + + SV *PackNameType `xml:"SV,omitempty"` + + HR *PackNameType `xml:"HR,omitempty"` + + RU *PackNameType `xml:"RU,omitempty"` + + SR *PackNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_shiptypeType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_shiptypeType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + ShipType *ShipTypeType `xml:"shipType,omitempty"` + + CodeOld *ShipTypeType `xml:"codeOld,omitempty"` + + DvkType *DvkTypeType `xml:"dvkType,omitempty"` + + CraftType *CraftTypeType `xml:"craftType,omitempty"` + + // True = transport cominination type, false=single ship + Combination *CombinationFlagType `xml:"combination,omitempty"` + + Name struct { + Loc *ShiptypeNameType `xml:"Loc,omitempty"` + + NL *ShiptypeNameType `xml:"NL,omitempty"` + + DE *ShiptypeNameType `xml:"DE,omitempty"` + + FR *ShiptypeNameType `xml:"FR,omitempty"` + + EN *ShiptypeNameType `xml:"EN,omitempty"` + + BG *ShiptypeNameType `xml:"BG,omitempty"` + + CS *ShiptypeNameType `xml:"CS,omitempty"` + + DA *ShiptypeNameType `xml:"DA,omitempty"` + + EL *ShiptypeNameType `xml:"EL,omitempty"` + + ES *ShiptypeNameType `xml:"ES,omitempty"` + + ET *ShiptypeNameType `xml:"ET,omitempty"` + + FI *ShiptypeNameType `xml:"FI,omitempty"` + + HU *ShiptypeNameType `xml:"HU,omitempty"` + + IT *ShiptypeNameType `xml:"IT,omitempty"` + + LT *ShiptypeNameType `xml:"LT,omitempty"` + + LV *ShiptypeNameType `xml:"LV,omitempty"` + + PL *ShiptypeNameType `xml:"PL,omitempty"` + + PT *ShiptypeNameType `xml:"PT,omitempty"` + + RO *ShiptypeNameType `xml:"RO,omitempty"` + + SK *ShiptypeNameType `xml:"SK,omitempty"` + + SL *ShiptypeNameType `xml:"SL,omitempty"` + + SV *ShiptypeNameType `xml:"SV,omitempty"` + + HR *ShiptypeNameType `xml:"HR,omitempty"` + + RU *ShiptypeNameType `xml:"RU,omitempty"` + + SR *ShiptypeNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_countryType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_countryType"` + + // UN country ISO code 2 chars. Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + CountryCode *CountryCodeType `xml:"countryCode,omitempty"` + + CodeOld *CountryCodeType `xml:"codeOld,omitempty"` + + CbsCode *CbscountryCodeType `xml:"cbsCode,omitempty"` + + CountryIsoCode3 *CountryCode3Type `xml:"countryIsoCode3,omitempty"` + + CountryIsoNum *CountryIsoNumType `xml:"countryIsoNum,omitempty"` + + LloydsFlag *LloydsflagType `xml:"lloydsFlag,omitempty"` + + Name struct { + Loc *CountryNameType `xml:"Loc,omitempty"` + + NL *CountryNameType `xml:"NL,omitempty"` + + DE *CountryNameType `xml:"DE,omitempty"` + + FR *CountryNameType `xml:"FR,omitempty"` + + EN *CountryNameType `xml:"EN,omitempty"` + + BG *CountryNameType `xml:"BG,omitempty"` + + CS *CountryNameType `xml:"CS,omitempty"` + + DA *CountryNameType `xml:"DA,omitempty"` + + EL *CountryNameType `xml:"EL,omitempty"` + + ES *CountryNameType `xml:"ES,omitempty"` + + ET *CountryNameType `xml:"ET,omitempty"` + + FI *CountryNameType `xml:"FI,omitempty"` + + HU *CountryNameType `xml:"HU,omitempty"` + + IT *CountryNameType `xml:"IT,omitempty"` + + LT *CountryNameType `xml:"LT,omitempty"` + + LV *CountryNameType `xml:"LV,omitempty"` + + PL *CountryNameType `xml:"PL,omitempty"` + + PT *CountryNameType `xml:"PT,omitempty"` + + RO *CountryNameType `xml:"RO,omitempty"` + + SK *CountryNameType `xml:"SK,omitempty"` + + SL *CountryNameType `xml:"SL,omitempty"` + + SV *CountryNameType `xml:"SV,omitempty"` + + HR *CountryNameType `xml:"HR,omitempty"` + + RU *CountryNameType `xml:"RU,omitempty"` + + SR *CountryNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Nts_dataType struct { + XMLName xml.Name `xml:"http://rwsreftool/ nts_dataType"` + + // Just a string (no enum) to return nts data type of the nts_data returned! + // reftype already contains datatype. + Ntstype *NtstypeType `xml:"ntstype,omitempty"` + + Ntscode *NtsCodeType `xml:"ntscode,omitempty"` + + CodeOld *NtsCodeType `xml:"codeOld,omitempty"` + + Name struct { + Loc *NtsNameType `xml:"Loc,omitempty"` + + NL *NtsNameType `xml:"NL,omitempty"` + + DE *NtsNameType `xml:"DE,omitempty"` + + EN *NtsNameType `xml:"EN,omitempty"` + + FR *NtsNameType `xml:"FR,omitempty"` + + BG *NtsNameType `xml:"BG,omitempty"` + + CS *NtsNameType `xml:"CS,omitempty"` + + DA *NtsNameType `xml:"DA,omitempty"` + + EL *NtsNameType `xml:"EL,omitempty"` + + ES *NtsNameType `xml:"ES,omitempty"` + + ET *NtsNameType `xml:"ET,omitempty"` + + FI *NtsNameType `xml:"FI,omitempty"` + + HU *NtsNameType `xml:"HU,omitempty"` + + IT *NtsNameType `xml:"IT,omitempty"` + + LT *NtsNameType `xml:"LT,omitempty"` + + LV *NtsNameType `xml:"LV,omitempty"` + + PL *NtsNameType `xml:"PL,omitempty"` + + PT *NtsNameType `xml:"PT,omitempty"` + + RO *NtsNameType `xml:"RO,omitempty"` + + SK *NtsNameType `xml:"SK,omitempty"` + + SL *NtsNameType `xml:"SL,omitempty"` + + SV *NtsNameType `xml:"SV,omitempty"` + + HR *NtsNameType `xml:"HR,omitempty"` + + RU *NtsNameType `xml:"RU,omitempty"` + + SR *NtsNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Ris_idxReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ ris_idxReqMutType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + RisidxCode *RisCodeType `xml:"risidxCode,omitempty"` + + CodeOld *RisCodeType `xml:"codeOld,omitempty"` + + UnlocCC *UnlocCCType `xml:"unlocCC,omitempty"` + + UnlocLC *UnlocLCType `xml:"unlocLC,omitempty"` + + WwsectCode *FwCodeType `xml:"wwsectCode,omitempty"` + + Objcode *RisobjCodeType `xml:"objcode,omitempty"` + + Hectomt *KmCodeType `xml:"hectomt,omitempty"` + + Objfunc *RisobjfuncType `xml:"objfunc,omitempty"` + + Objname struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"objname,omitempty"` + + Locname struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"locname,omitempty"` + + Wwname struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"wwname,omitempty"` + + Routename struct { + Loc *RislocNameType `xml:"Loc,omitempty"` + } `xml:"routename,omitempty"` + + Relrisindex *RisCodeType `xml:"relrisindex,omitempty"` + + Sectionnode *RissectnodeType `xml:"sectionnode,omitempty"` + + Lat *LatlonType `xml:"lat,omitempty"` + + Lon *LatlonType `xml:"lon,omitempty"` + + Relenc *RisrelencType `xml:"relenc,omitempty"` + + Comminfo *RiscomminfoType `xml:"comminfo,omitempty"` + + Gaugeid *RisgaugeidType `xml:"gaugeid,omitempty"` + + Vesselconvlength *DimType `xml:"vesselconvlength,omitempty"` + + Vesselconvbreadth *DimType `xml:"vesselconvbreadth,omitempty"` + + Vesselconvdraught *DimType `xml:"vesselconvdraught,omitempty"` + + Vesselconvairdraught *DimType `xml:"vesselconvairdraught,omitempty"` + + Availablelength *DimType `xml:"availablelength,omitempty"` + + Clearancewidth *DimType `xml:"clearancewidth,omitempty"` + + Availabledepth *DimType `xml:"availabledepth,omitempty"` + + Clearanceheight *DimType `xml:"clearanceheight,omitempty"` + + Applicabilityfromkm *RisapplikmType `xml:"applicabilityfromkm,omitempty"` + + Applicabilitytokm *RisapplikmType `xml:"applicabilitytokm,omitempty"` + + Reflevel1code *RisreflevelcodeType `xml:"reflevel1code,omitempty"` + + Reflevel1value *RisreflevelvalueType `xml:"reflevel1value,omitempty"` + + Reflevel2code *RisreflevelcodeType `xml:"reflevel2code,omitempty"` + + Reflevel2value *RisreflevelvalueType `xml:"reflevel2value,omitempty"` + + Reflevel3code *RisreflevelcodeType `xml:"reflevel3code,omitempty"` + + Reflevel3value *RisreflevelvalueType `xml:"reflevel3value,omitempty"` + + Zeropoint *RiszeropointType `xml:"zeropoint,omitempty"` + + Geodref *RisgeodrefType `xml:"geodref,omitempty"` + + Catoftimesched *RiscatoftimeschedType `xml:"catoftimesched,omitempty"` + + Forshiptype *RisforshiptypeType `xml:"forshiptype,omitempty"` + + Foruseofship *RisuseofshipType `xml:"foruseofship,omitempty"` + + LnktoextXMfiletimesched *RislinkextxmlType `xml:"lnktoextXMfiletimesched,omitempty"` + + LnktoextXMfilepastime *RislinkextxmlType `xml:"lnktoextXMfilepastime,omitempty"` + + Vesselconvlength2 *DimType `xml:"vesselconvlength2,omitempty"` + + Vesselconvbreadth2 *DimType `xml:"vesselconvbreadth2,omitempty"` + + Vesselconvdraught2 *DimType `xml:"vesselconvdraught2,omitempty"` + + Vesselconvairdraught2 *DimType `xml:"vesselconvairdraught2,omitempty"` + + Availablelength2 *DimType `xml:"availablelength2,omitempty"` + + Clearancewidth2 *DimType `xml:"clearancewidth2,omitempty"` + + Availabledepth2 *DimType `xml:"availabledepth2,omitempty"` + + Clearanceheight2 *DimType `xml:"clearanceheight2,omitempty"` + + Vesselconvlength3 *DimType `xml:"vesselconvlength3,omitempty"` + + Vesselconvbreadth3 *DimType `xml:"vesselconvbreadth3,omitempty"` + + Vesselconvdraught3 *DimType `xml:"vesselconvdraught3,omitempty"` + + Vesselconvairdraught3 *DimType `xml:"vesselconvairdraught3,omitempty"` + + Availablelength3 *DimType `xml:"availablelength3,omitempty"` + + Clearancewidth3 *DimType `xml:"clearancewidth3,omitempty"` + + Availabledepth3 *DimType `xml:"availabledepth3,omitempty"` + + Clearanceheight3 *DimType `xml:"clearanceheight3,omitempty"` + + Catoftimesched2 *RiscatoftimeschedType `xml:"catoftimesched2,omitempty"` + + Forshiptype2 *RisforshiptypeType `xml:"forshiptype2,omitempty"` + + Foruseofship2 *RisuseofshipType `xml:"foruseofship2,omitempty"` + + LnktoextXMfiletimesched2 *RislinkextxmlType `xml:"lnktoextXMfiletimesched2,omitempty"` + + LnktoextXMfilepastime2 *RislinkextxmlType `xml:"lnktoextXMfilepastime2,omitempty"` + + Catoftimesched3 *RiscatoftimeschedType `xml:"catoftimesched3,omitempty"` + + Forshiptype3 *RisforshiptypeType `xml:"forshiptype3,omitempty"` + + Foruseofship3 *RisuseofshipType `xml:"foruseofship3,omitempty"` + + LnktoextXMfiletimesched3 *RislinkextxmlType `xml:"lnktoextXMfiletimesched3,omitempty"` + + LnktoextXMfilepastime3 *RislinkextxmlType `xml:"lnktoextXMfilepastime3,omitempty"` + + Startdate *LastupdateType `xml:"startdate,omitempty"` + + Enddate *LastupdateType `xml:"enddate,omitempty"` + + Infodate *LastupdateType `xml:"infodate,omitempty"` + + // specific Ris idx remarks/reasons for change, as received with the ris idx + RisidxRemarks *RemarksType `xml:"risidxRemarks,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + // remarks regarding the maintance of the ris idx + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_locationReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_locationReqMutType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + ErilocCode *ErilocCodeType `xml:"erilocCode,omitempty"` + + CodeOld *ErilocCodeType `xml:"codeOld,omitempty"` + + RisCode *RisCodeType `xml:"risCode,omitempty"` + + LoCode *LoCodeType `xml:"loCode,omitempty"` + + FwCode *FwCodeType `xml:"fwCode,omitempty"` + + TermCode *TermCodeType `xml:"termCode,omitempty"` + + KmCode *KmCodeType `xml:"kmCode,omitempty"` + + Name struct { + Loc *ErilocNameType `xml:"Loc,omitempty"` + + NL *ErilocNameType `xml:"NL,omitempty"` + + DE *ErilocNameType `xml:"DE,omitempty"` + + FR *ErilocNameType `xml:"FR,omitempty"` + + EN *ErilocNameType `xml:"EN,omitempty"` + } `xml:"name,omitempty"` + + LocType *LocTypeType `xml:"locType,omitempty"` + + Termname struct { + Loc *ErilocNameType `xml:"Loc,omitempty"` + + NL *ErilocNameType `xml:"NL,omitempty"` + + DE *ErilocNameType `xml:"DE,omitempty"` + + FR *ErilocNameType `xml:"FR,omitempty"` + + EN *ErilocNameType `xml:"EN,omitempty"` + } `xml:"termname,omitempty"` + + QuayFrom *QuayType `xml:"quayFrom,omitempty"` + + QuayTo *QuayType `xml:"quayTo,omitempty"` + + TermType *LocTypeType `xml:"termType,omitempty"` + + Lat *LatlonType `xml:"lat,omitempty"` + + Lon *LatlonType `xml:"lon,omitempty"` + + IvsVTSCode *LocVtsCodeType `xml:"ivsVTSCode,omitempty"` + + Exits *LocExitsType `xml:"exits,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_hscodeReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_hscodeReqMutType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + HsCode *HsCodeType `xml:"hsCode,omitempty"` + + CodeOld *HsCodeType `xml:"codeOld,omitempty"` + + Name struct { + Loc *GoodNameType `xml:"Loc,omitempty"` + + NL *GoodNameType `xml:"NL,omitempty"` + + DE *GoodNameType `xml:"DE,omitempty"` + + FR *GoodNameType `xml:"FR,omitempty"` + + EN *GoodNameType `xml:"EN,omitempty"` + + BG *GoodNameType `xml:"BG,omitempty"` + + CS *GoodNameType `xml:"CS,omitempty"` + + DA *GoodNameType `xml:"DA,omitempty"` + + EL *GoodNameType `xml:"EL,omitempty"` + + ES *GoodNameType `xml:"ES,omitempty"` + + ET *GoodNameType `xml:"ET,omitempty"` + + FI *GoodNameType `xml:"FI,omitempty"` + + HU *GoodNameType `xml:"HU,omitempty"` + + IT *GoodNameType `xml:"IT,omitempty"` + + LT *GoodNameType `xml:"LT,omitempty"` + + LV *GoodNameType `xml:"LV,omitempty"` + + PL *GoodNameType `xml:"PL,omitempty"` + + PT *GoodNameType `xml:"PT,omitempty"` + + RO *GoodNameType `xml:"RO,omitempty"` + + SK *GoodNameType `xml:"SK,omitempty"` + + SL *GoodNameType `xml:"SL,omitempty"` + + SV *GoodNameType `xml:"SV,omitempty"` + + HR *GoodNameType `xml:"HR,omitempty"` + + RU *GoodNameType `xml:"RU,omitempty"` + + SR *GoodNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + NstCode *NstCodeType `xml:"nstCode,omitempty"` + + NstName *GoodNameType `xml:"nstName,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_adncodeReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_adncodeReqMutType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + AdnCode *AdnCodeType `xml:"adnCode,omitempty"` + + CodeOld *AdnCodeType `xml:"codeOld,omitempty"` + + Unnr *UnnrType `xml:"unnr,omitempty"` + + AdnClass *AdnClassType `xml:"adnClass,omitempty"` + + AdnClassif *AdnClassType `xml:"adnClassif,omitempty"` + + ImoClass *AdnClassType `xml:"imoClass,omitempty"` + + PackGrp *PackGrpType `xml:"packGrp,omitempty"` + + Name struct { + Loc *GoodDgsNameType `xml:"Loc,omitempty"` + + NL *GoodDgsNameType `xml:"NL,omitempty"` + + DE *GoodDgsNameType `xml:"DE,omitempty"` + + FR *GoodDgsNameType `xml:"FR,omitempty"` + + EN *GoodDgsNameType `xml:"EN,omitempty"` + + BG *GoodDgsNameType `xml:"BG,omitempty"` + + CS *GoodDgsNameType `xml:"CS,omitempty"` + + DA *GoodDgsNameType `xml:"DA,omitempty"` + + EL *GoodDgsNameType `xml:"EL,omitempty"` + + ES *GoodDgsNameType `xml:"ES,omitempty"` + + ET *GoodDgsNameType `xml:"ET,omitempty"` + + FI *GoodDgsNameType `xml:"FI,omitempty"` + + HU *GoodDgsNameType `xml:"HU,omitempty"` + + IT *GoodDgsNameType `xml:"IT,omitempty"` + + LT *GoodDgsNameType `xml:"LT,omitempty"` + + LV *GoodDgsNameType `xml:"LV,omitempty"` + + PL *GoodDgsNameType `xml:"PL,omitempty"` + + PT *GoodDgsNameType `xml:"PT,omitempty"` + + RO *GoodDgsNameType `xml:"RO,omitempty"` + + SK *GoodDgsNameType `xml:"SK,omitempty"` + + SL *GoodDgsNameType `xml:"SL,omitempty"` + + SV *GoodDgsNameType `xml:"SV,omitempty"` + + HR *GoodDgsNameType `xml:"HR,omitempty"` + + RU *GoodDgsNameType `xml:"RU,omitempty"` + + SR *GoodDgsNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Syn struct { + Loc *GoodSynType `xml:"Loc,omitempty"` + + NL *GoodSynType `xml:"NL,omitempty"` + + DE *GoodSynType `xml:"DE,omitempty"` + + FR *GoodSynType `xml:"FR,omitempty"` + + EN *GoodSynType `xml:"EN,omitempty"` + + BG *GoodSynType `xml:"BG,omitempty"` + + CS *GoodSynType `xml:"CS,omitempty"` + + DA *GoodSynType `xml:"DA,omitempty"` + + EL *GoodSynType `xml:"EL,omitempty"` + + ES *GoodSynType `xml:"ES,omitempty"` + + ET *GoodSynType `xml:"ET,omitempty"` + + FI *GoodSynType `xml:"FI,omitempty"` + + HU *GoodSynType `xml:"HU,omitempty"` + + IT *GoodSynType `xml:"IT,omitempty"` + + LT *GoodSynType `xml:"LT,omitempty"` + + LV *GoodSynType `xml:"LV,omitempty"` + + PL *GoodSynType `xml:"PL,omitempty"` + + PT *GoodSynType `xml:"PT,omitempty"` + + RO *GoodSynType `xml:"RO,omitempty"` + + SK *GoodSynType `xml:"SK,omitempty"` + + SL *GoodSynType `xml:"SL,omitempty"` + + SV *GoodSynType `xml:"SV,omitempty"` + + HR *GoodSynType `xml:"HR,omitempty"` + + RU *GoodSynType `xml:"RU,omitempty"` + + SR *GoodSynType `xml:"SR,omitempty"` + } `xml:"syn,omitempty"` + + HsCode *HsCodeType `xml:"hsCode,omitempty"` + + NstCode *NstCodeType `xml:"nstCode,omitempty"` + + // 0, 1, 2, 3, V (Inland) + ConesBulk *ConesType `xml:"conesBulk,omitempty"` + + ConesTank *ConesType `xml:"conesTank,omitempty"` + + ConesCont *ConesType `xml:"conesCont,omitempty"` + + // 0, B, V (Seagoing) + FlagsBulk *FlagsType `xml:"flagsBulk,omitempty"` + + FlagsTank *FlagsType `xml:"flagsTank,omitempty"` + + WarnCard *WarnCardType `xml:"warnCard,omitempty"` + + WarnId *WarnIdType `xml:"warnId,omitempty"` + + Label1 *AdnLabelType `xml:"label1,omitempty"` + + Label2 *AdnLabelType `xml:"label2,omitempty"` + + Label3 *AdnLabelType `xml:"label3,omitempty"` + + MaxWeightCodeInlandBulkCont *SignalCodeType `xml:"maxWeightCodeInlandBulkCont,omitempty"` + + // max weight in kg + MaxWeightInlandBulkCont *WeightType `xml:"maxWeightInlandBulkCont,omitempty"` + + SignalCodeInlandCont *SignalCodeType `xml:"signalCodeInlandCont,omitempty"` + + // min weight in kg + MinWeightInlandCont *WeightType `xml:"minWeightInlandCont,omitempty"` + + SignalCodeInlandExcemp *SignalCodeType `xml:"signalCodeInlandExcemp,omitempty"` + + // min weight in kg + MinWeightInlandExcemp *WeightType `xml:"minWeightInlandExcemp,omitempty"` + + SignalCodeSeaBulk *SignalCodeType `xml:"signalCodeSeaBulk,omitempty"` + + // min weight in kg + MinWeightSeaBulk *WeightType `xml:"minWeightSeaBulk,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_conttypeReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_conttypeReqMutType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + ContId *ContIdType `xml:"contId,omitempty"` + + CodeOld *ContIdType `xml:"codeOld,omitempty"` + + // dimensions in cm + Length *DimType `xml:"length,omitempty"` + + Width *DimType `xml:"width,omitempty"` + + Height *DimType `xml:"height,omitempty"` + + // empty weight in kg + Weight *DimType `xml:"weight,omitempty"` + + Name struct { + Loc *ContNameType `xml:"Loc,omitempty"` + + NL *ContNameType `xml:"NL,omitempty"` + + DE *ContNameType `xml:"DE,omitempty"` + + FR *ContNameType `xml:"FR,omitempty"` + + EN *ContNameType `xml:"EN,omitempty"` + + BG *ContNameType `xml:"BG,omitempty"` + + CS *ContNameType `xml:"CS,omitempty"` + + DA *ContNameType `xml:"DA,omitempty"` + + EL *ContNameType `xml:"EL,omitempty"` + + ES *ContNameType `xml:"ES,omitempty"` + + ET *ContNameType `xml:"ET,omitempty"` + + FI *ContNameType `xml:"FI,omitempty"` + + HU *ContNameType `xml:"HU,omitempty"` + + IT *ContNameType `xml:"IT,omitempty"` + + LT *ContNameType `xml:"LT,omitempty"` + + LV *ContNameType `xml:"LV,omitempty"` + + PL *ContNameType `xml:"PL,omitempty"` + + PT *ContNameType `xml:"PT,omitempty"` + + RO *ContNameType `xml:"RO,omitempty"` + + SK *ContNameType `xml:"SK,omitempty"` + + SL *ContNameType `xml:"SL,omitempty"` + + SV *ContNameType `xml:"SV,omitempty"` + + HR *ContNameType `xml:"HR,omitempty"` + + RU *ContNameType `xml:"RU,omitempty"` + + SR *ContNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_packtypeReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_packtypeReqMutType"` + + // Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + PackId *PackIdType `xml:"packId,omitempty"` + + CodeOld *PackIdType `xml:"codeOld,omitempty"` + + Name struct { + Loc *PackNameType `xml:"Loc,omitempty"` + + NL *PackNameType `xml:"NL,omitempty"` + + DE *PackNameType `xml:"DE,omitempty"` + + FR *PackNameType `xml:"FR,omitempty"` + + EN *PackNameType `xml:"EN,omitempty"` + + BG *PackNameType `xml:"BG,omitempty"` + + CS *PackNameType `xml:"CS,omitempty"` + + DA *PackNameType `xml:"DA,omitempty"` + + EL *PackNameType `xml:"EL,omitempty"` + + ES *PackNameType `xml:"ES,omitempty"` + + ET *PackNameType `xml:"ET,omitempty"` + + FI *PackNameType `xml:"FI,omitempty"` + + HU *PackNameType `xml:"HU,omitempty"` + + IT *PackNameType `xml:"IT,omitempty"` + + LT *PackNameType `xml:"LT,omitempty"` + + LV *PackNameType `xml:"LV,omitempty"` + + PL *PackNameType `xml:"PL,omitempty"` + + PT *PackNameType `xml:"PT,omitempty"` + + RO *PackNameType `xml:"RO,omitempty"` + + SK *PackNameType `xml:"SK,omitempty"` + + SL *PackNameType `xml:"SL,omitempty"` + + SV *PackNameType `xml:"SV,omitempty"` + + HR *PackNameType `xml:"HR,omitempty"` + + RU *PackNameType `xml:"RU,omitempty"` + + SR *PackNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_shiptypeReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_shiptypeReqMutType"` + + // UN rec 28. Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + ShipType *ShipTypeType `xml:"shipType,omitempty"` + + CodeOld *ShipTypeType `xml:"codeOld,omitempty"` + + DvkType *DvkTypeType `xml:"dvkType,omitempty"` + + CraftType *CraftTypeType `xml:"craftType,omitempty"` + + // True = transport cominination type, false=single ship + Combination *CombinationFlagType `xml:"combination,omitempty"` + + Name struct { + Loc *ShiptypeNameType `xml:"Loc,omitempty"` + + NL *ShiptypeNameType `xml:"NL,omitempty"` + + DE *ShiptypeNameType `xml:"DE,omitempty"` + + FR *ShiptypeNameType `xml:"FR,omitempty"` + + EN *ShiptypeNameType `xml:"EN,omitempty"` + + BG *ShiptypeNameType `xml:"BG,omitempty"` + + CS *ShiptypeNameType `xml:"CS,omitempty"` + + DA *ShiptypeNameType `xml:"DA,omitempty"` + + EL *ShiptypeNameType `xml:"EL,omitempty"` + + ES *ShiptypeNameType `xml:"ES,omitempty"` + + ET *ShiptypeNameType `xml:"ET,omitempty"` + + FI *ShiptypeNameType `xml:"FI,omitempty"` + + HU *ShiptypeNameType `xml:"HU,omitempty"` + + IT *ShiptypeNameType `xml:"IT,omitempty"` + + LT *ShiptypeNameType `xml:"LT,omitempty"` + + LV *ShiptypeNameType `xml:"LV,omitempty"` + + PL *ShiptypeNameType `xml:"PL,omitempty"` + + PT *ShiptypeNameType `xml:"PT,omitempty"` + + RO *ShiptypeNameType `xml:"RO,omitempty"` + + SK *ShiptypeNameType `xml:"SK,omitempty"` + + SL *ShiptypeNameType `xml:"SL,omitempty"` + + SV *ShiptypeNameType `xml:"SV,omitempty"` + + HR *ShiptypeNameType `xml:"HR,omitempty"` + + RU *ShiptypeNameType `xml:"RU,omitempty"` + + SR *ShiptypeNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type Eri_countryReqMutType struct { + XMLName xml.Name `xml:"http://rwsreftool/ eri_countryReqMutType"` + + // UN country ISO code 2 chars. Be sure to edit, change the basic Simpletypes, dont edit any of the (sub)elements from here!!! + CountryCode *CountryCodeType `xml:"countryCode,omitempty"` + + CodeOld *CountryCodeType `xml:"codeOld,omitempty"` + + CbsCode *CbscountryCodeType `xml:"cbsCode,omitempty"` + + CountryIsoCode3 *CountryCode3Type `xml:"countryIsoCode3,omitempty"` + + CountryIsoNum *CountryIsoNumType `xml:"countryIsoNum,omitempty"` + + LloydsFlag *LloydsflagType `xml:"lloydsFlag,omitempty"` + + Name struct { + Loc *CountryNameType `xml:"Loc,omitempty"` + + NL *CountryNameType `xml:"NL,omitempty"` + + DE *CountryNameType `xml:"DE,omitempty"` + + FR *CountryNameType `xml:"FR,omitempty"` + + EN *CountryNameType `xml:"EN,omitempty"` + + BG *CountryNameType `xml:"BG,omitempty"` + + CS *CountryNameType `xml:"CS,omitempty"` + + DA *CountryNameType `xml:"DA,omitempty"` + + EL *CountryNameType `xml:"EL,omitempty"` + + ES *CountryNameType `xml:"ES,omitempty"` + + ET *CountryNameType `xml:"ET,omitempty"` + + FI *CountryNameType `xml:"FI,omitempty"` + + HU *CountryNameType `xml:"HU,omitempty"` + + IT *CountryNameType `xml:"IT,omitempty"` + + LT *CountryNameType `xml:"LT,omitempty"` + + LV *CountryNameType `xml:"LV,omitempty"` + + PL *CountryNameType `xml:"PL,omitempty"` + + PT *CountryNameType `xml:"PT,omitempty"` + + RO *CountryNameType `xml:"RO,omitempty"` + + SK *CountryNameType `xml:"SK,omitempty"` + + SL *CountryNameType `xml:"SL,omitempty"` + + SV *CountryNameType `xml:"SV,omitempty"` + + HR *CountryNameType `xml:"HR,omitempty"` + + RU *CountryNameType `xml:"RU,omitempty"` + + SR *CountryNameType `xml:"SR,omitempty"` + } `xml:"name,omitempty"` + + Source *SourceType `xml:"source,omitempty"` + + Remarks *RemarksType `xml:"remarks,omitempty"` + + Version *RefrecVersionType `xml:"version,omitempty"` + + Erased *ErasedType `xml:"erased,omitempty"` + + Lastupdate *LastupdateType `xml:"lastupdate,omitempty"` +} + +type RefWeb interface { + + // Error can be either of the following types: + // + // - exception + + MatchByCode(request *MatchByCode) (*MatchByCodeResponse, error) + + // Error can be either of the following types: + // + // - exception + + MatchByName(request *MatchByName) (*MatchByNameResponse, error) + + // Error can be either of the following types: + // + // - exception + + GetMutations(request *GetMutations) (*GetMutationsResponse, error) + + // Error can be either of the following types: + // + // - exception + + GetDataXML(request *GetDataXML) (*GetDataXMLResponse, error) + + // Error can be either of the following types: + // + // - exception + + GetRisDataXML(request *GetRisDataXML) (*GetRisDataXMLResponse, error) + + // Error can be either of the following types: + // + // - exception + + MutateDataXML(request *MutateDataXML) (*MutateDataXMLResponse, error) + + // Error can be either of the following types: + // + // - exception + + RequestMutationXML(request *RequestMutationXML) (*RequestMutationXMLResponse, error) +} + +type refWeb struct { + client *soap.SOAPClient +} + +func NewRefWeb(client *soap.SOAPClient) RefWeb { + return &refWeb{ + client: client, + } +} + +func (service *refWeb) MatchByCode(request *MatchByCode) (*MatchByCodeResponse, error) { + response := new(MatchByCodeResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *refWeb) MatchByName(request *MatchByName) (*MatchByNameResponse, error) { + response := new(MatchByNameResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *refWeb) GetMutations(request *GetMutations) (*GetMutationsResponse, error) { + response := new(GetMutationsResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *refWeb) GetDataXML(request *GetDataXML) (*GetDataXMLResponse, error) { + response := new(GetDataXMLResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *refWeb) GetRisDataXML(request *GetRisDataXML) (*GetRisDataXMLResponse, error) { + response := new(GetRisDataXMLResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *refWeb) MutateDataXML(request *MutateDataXML) (*MutateDataXMLResponse, error) { + response := new(MutateDataXMLResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *refWeb) RequestMutationXML(request *RequestMutationXML) (*RequestMutationXMLResponse, error) { + response := new(RequestMutationXMLResponse) + err := service.client.Call("", request, response) + if err != nil { + return nil, err + } + + return response, nil +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/soap/ifaf/service.go Tue Jan 15 10:07:10 2019 +0100 @@ -0,0 +1,935 @@ +// This is Free Software under GNU Affero General Public License v >= 3.0 +// without warranty, see README.md and license for details. +// +// SPDX-License-Identifier: AGPL-3.0-or-later +// License-Filename: LICENSES/AGPL-3.0.txt +// +// Copyright (C) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Raimund Renkert <raimund.renkert@intevation.de> + +package ifaf + +import ( + "crypto/tls" + "encoding/xml" + "time" + + "github.com/jackc/pgx/pgtype" + + "gemma.intevation.de/gemma/pkg/soap" +) + +// against "unused imports" +var _ time.Time +var _ xml.Name + +type Get_bottleneck_fa struct { + XMLName xml.Name `xml:"http://www.ris.eu/fairwayavailability/3.0 get_bottleneck_fa"` + + Bottleneck_id *ArrayOfString `xml:"bottleneck_id,omitempty"` + + Period *RequestedPeriod `xml:"period,omitempty"` +} + +type Get_bottleneck_faResponse struct { + XMLName xml.Name `xml:"http://www.ris.eu/fairwayavailability/3.0 get_bottleneck_faResponse"` + + Get_bottleneck_faResult *ArrayOfFairwayAvailability `xml:"get_bottleneck_faResult,omitempty"` +} + +type Get_stretch_fa struct { + XMLName xml.Name `xml:"http://www.ris.eu/fairwayavailability/3.0 get_stretch_fa"` + + ISRS *ArrayOfISRSPair `xml:"ISRS,omitempty"` + + Period *RequestedPeriod `xml:"period,omitempty"` +} + +type Get_stretch_faResponse struct { + XMLName xml.Name `xml:"http://www.ris.eu/fairwayavailability/3.0 get_stretch_faResponse"` + + Get_stretch_faResult *ArrayOfFairwayAvailability `xml:"get_stretch_faResult,omitempty"` +} + +type ArrayOfFairwayAvailability struct { + FairwayAvailability []*FairwayAvailability `xml:"FairwayAvailability,omitempty"` +} + +type FairwayAvailability struct { + Bottleneck_id string `xml:"bottleneck_id,omitempty"` + + SURDAT time.Time `xml:"SURDAT,omitempty"` + + POSITION *PositionEnum `xml:"POSITION,omitempty"` + + Reference_values *ArrayOfReferenceValue `xml:"Reference_values,omitempty"` + + AdditionalData *ArrayOfKeyValuePair `xml:"AdditionalData,omitempty"` + + Critical bool `xml:"Critical,omitempty"` + + Bottleneck_PDFs *ArrayOfPdfInfo `xml:"Bottleneck_PDFs,omitempty"` + + Effective_fairway_availability *ArrayOfEffectiveFairwayAvailability `xml:"Effective_fairway_availability,omitempty"` + + Date_Info time.Time `xml:"Date_Info,omitempty"` + + Source string `xml:"Source,omitempty"` +} + +type ArrayOfPdfInfo struct { + PdfInfo []*PdfInfo `xml:"PdfInfo,omitempty"` +} + +type PdfInfo struct { + ProfilePdfFilename string `xml:"ProfilePdfFilename,omitempty"` + + ProfilePdfURL string `xml:"ProfilePdfURL,omitempty"` + + PDF_Generation_Date time.Time `xml:"PDF_Generation_Date,omitempty"` + + Source string `xml:"Source,omitempty"` +} + +type ArrayOfEffectiveFairwayAvailability struct { + EffectiveFairwayAvailability []*EffectiveFairwayAvailability `xml:"EffectiveFairwayAvailability,omitempty"` +} + +type EffectiveFairwayAvailability struct { + Available_depth_value int32 `xml:"Available_depth_value,omitempty"` + + Available_width_value int32 `xml:"Available_width_value,omitempty"` + + Water_level_value int32 `xml:"Water_level_value,omitempty"` + + Measure_date time.Time `xml:"Measure_date,omitempty"` + + Measure_type *MeasureType `xml:"Measure_type,omitempty"` + + Source string `xml:"Source,omitempty"` + + Level_of_Service *LosEnum `xml:"Level_of_Service,omitempty"` + + Forecast_generation_time pgtype.Timestamp `xml:"Forecast_generation_time,omitempty"` + + Value_lifetime time.Time `xml:"Value_lifetime,omitempty"` +} + +type ArrayOfReferenceValue struct { + ReferenceValue []*ReferenceValue `xml:"ReferenceValue,omitempty"` +} + +type ReferenceValue struct { + Fairway_depth int32 `xml:"fairway_depth,omitempty"` + + Fairway_width int32 `xml:"fairway_width,omitempty"` + + Fairway_radius int32 `xml:"fairway_radius,omitempty"` + + Shallowest_spot_Lat float64 `xml:"Shallowest_spot_Lat,omitempty"` + + Shallowest_spot_Lon float64 `xml:"Shallowest_spot_Lon,omitempty"` + + Level_of_Service *LosEnum `xml:"Level_of_Service,omitempty"` +} + +type ErrorCode string + +const ( + + // <summary> Description: message type not supported, Explanation: + // web service does not support the requested message type + // </summary> + ErrorCodeE010 ErrorCode = "e010" + + // <summary> Description: syntax error in request, Explanation: + // request violates the schema for requests </summary> + ErrorCodeE100 ErrorCode = "e100" + + // <summary> Description: incorrect message type, Explanation: given + // message type is not known </summary> + ErrorCodeE110 ErrorCode = "e110" + + // <summary> Description: incorrect type-specific parameters, + // Explanation: type-specific parameters are erroneous </summary> + ErrorCodeE120 ErrorCode = "e120" + + // <summary> Description: operation not known, Explanation: the + // requested operation is unknown </summary> + ErrorCodeE200 ErrorCode = "e200" + + // <summary> Description: requested method or operation is not + // implemented </summary> + ErrorCodeE210 ErrorCode = "e210" + + // <summary> Description: data source unavailable, Explanation: data + // source of the web service for is temporarily unavailable + // </summary> + ErrorCodeE300 ErrorCode = "e300" + + // <summary> Description: too many results for request, Explanation: + // server is unable to handle number of results </summary> + ErrorCodeE310 ErrorCode = "e310" + + // <summary> Description: unexpected or other error + // </summary> + ErrorCodeE999 ErrorCode = "e999" +) + +type CountryCode string + +const ( + CountryCodeAF CountryCode = "AF" + + CountryCodeAX CountryCode = "AX" + + CountryCodeAL CountryCode = "AL" + + CountryCodeDZ CountryCode = "DZ" + + CountryCodeAS CountryCode = "AS" + + CountryCodeAD CountryCode = "AD" + + CountryCodeAO CountryCode = "AO" + + CountryCodeAI CountryCode = "AI" + + CountryCodeAQ CountryCode = "AQ" + + CountryCodeAG CountryCode = "AG" + + CountryCodeAR CountryCode = "AR" + + CountryCodeAM CountryCode = "AM" + + CountryCodeAW CountryCode = "AW" + + CountryCodeAU CountryCode = "AU" + + CountryCodeAT CountryCode = "AT" + + CountryCodeAZ CountryCode = "AZ" + + CountryCodeBS CountryCode = "BS" + + CountryCodeBH CountryCode = "BH" + + CountryCodeBD CountryCode = "BD" + + CountryCodeBB CountryCode = "BB" + + CountryCodeBY CountryCode = "BY" + + CountryCodeBE CountryCode = "BE" + + CountryCodeBZ CountryCode = "BZ" + + CountryCodeBJ CountryCode = "BJ" + + CountryCodeBM CountryCode = "BM" + + CountryCodeBT CountryCode = "BT" + + CountryCodeBO CountryCode = "BO" + + CountryCodeBQ CountryCode = "BQ" + + CountryCodeBA CountryCode = "BA" + + CountryCodeBW CountryCode = "BW" + + CountryCodeBV CountryCode = "BV" + + CountryCodeBR CountryCode = "BR" + + CountryCodeIO CountryCode = "IO" + + CountryCodeBN CountryCode = "BN" + + CountryCodeBG CountryCode = "BG" + + CountryCodeBF CountryCode = "BF" + + CountryCodeBI CountryCode = "BI" + + CountryCodeCV CountryCode = "CV" + + CountryCodeKH CountryCode = "KH" + + CountryCodeCM CountryCode = "CM" + + CountryCodeCA CountryCode = "CA" + + CountryCodeKY CountryCode = "KY" + + CountryCodeCF CountryCode = "CF" + + CountryCodeTD CountryCode = "TD" + + CountryCodeCL CountryCode = "CL" + + CountryCodeCN CountryCode = "CN" + + CountryCodeCX CountryCode = "CX" + + CountryCodeCC CountryCode = "CC" + + CountryCodeCO CountryCode = "CO" + + CountryCodeKM CountryCode = "KM" + + CountryCodeCG CountryCode = "CG" + + CountryCodeCD CountryCode = "CD" + + CountryCodeCK CountryCode = "CK" + + CountryCodeCR CountryCode = "CR" + + CountryCodeCI CountryCode = "CI" + + CountryCodeHR CountryCode = "HR" + + CountryCodeCU CountryCode = "CU" + + CountryCodeCW CountryCode = "CW" + + CountryCodeCY CountryCode = "CY" + + CountryCodeCZ CountryCode = "CZ" + + CountryCodeDK CountryCode = "DK" + + CountryCodeDJ CountryCode = "DJ" + + CountryCodeDM CountryCode = "DM" + + CountryCodeDO CountryCode = "DO" + + CountryCodeEC CountryCode = "EC" + + CountryCodeEG CountryCode = "EG" + + CountryCodeSV CountryCode = "SV" + + CountryCodeGQ CountryCode = "GQ" + + CountryCodeER CountryCode = "ER" + + CountryCodeEE CountryCode = "EE" + + CountryCodeET CountryCode = "ET" + + CountryCodeFK CountryCode = "FK" + + CountryCodeFO CountryCode = "FO" + + CountryCodeFJ CountryCode = "FJ" + + CountryCodeFI CountryCode = "FI" + + CountryCodeFR CountryCode = "FR" + + CountryCodeGF CountryCode = "GF" + + CountryCodePF CountryCode = "PF" + + CountryCodeTF CountryCode = "TF" + + CountryCodeGA CountryCode = "GA" + + CountryCodeGM CountryCode = "GM" + + CountryCodeGE CountryCode = "GE" + + CountryCodeDE CountryCode = "DE" + + CountryCodeGH CountryCode = "GH" + + CountryCodeGI CountryCode = "GI" + + CountryCodeGR CountryCode = "GR" + + CountryCodeGL CountryCode = "GL" + + CountryCodeGD CountryCode = "GD" + + CountryCodeGP CountryCode = "GP" + + CountryCodeGU CountryCode = "GU" + + CountryCodeGT CountryCode = "GT" + + CountryCodeGG CountryCode = "GG" + + CountryCodeGN CountryCode = "GN" + + CountryCodeGW CountryCode = "GW" + + CountryCodeGY CountryCode = "GY" + + CountryCodeHT CountryCode = "HT" + + CountryCodeHM CountryCode = "HM" + + CountryCodeVA CountryCode = "VA" + + CountryCodeHN CountryCode = "HN" + + CountryCodeHK CountryCode = "HK" + + CountryCodeHU CountryCode = "HU" + + CountryCodeIS CountryCode = "IS" + + CountryCodeIN CountryCode = "IN" + + CountryCodeID CountryCode = "ID" + + CountryCodeIR CountryCode = "IR" + + CountryCodeIQ CountryCode = "IQ" + + CountryCodeIE CountryCode = "IE" + + CountryCodeIM CountryCode = "IM" + + CountryCodeIL CountryCode = "IL" + + CountryCodeIT CountryCode = "IT" + + CountryCodeJM CountryCode = "JM" + + CountryCodeJP CountryCode = "JP" + + CountryCodeJE CountryCode = "JE" + + CountryCodeJO CountryCode = "JO" + + CountryCodeKZ CountryCode = "KZ" + + CountryCodeKE CountryCode = "KE" + + CountryCodeKI CountryCode = "KI" + + CountryCodeKP CountryCode = "KP" + + CountryCodeKR CountryCode = "KR" + + CountryCodeKW CountryCode = "KW" + + CountryCodeKG CountryCode = "KG" + + CountryCodeLA CountryCode = "LA" + + CountryCodeLV CountryCode = "LV" + + CountryCodeLB CountryCode = "LB" + + CountryCodeLS CountryCode = "LS" + + CountryCodeLR CountryCode = "LR" + + CountryCodeLY CountryCode = "LY" + + CountryCodeLI CountryCode = "LI" + + CountryCodeLT CountryCode = "LT" + + CountryCodeLU CountryCode = "LU" + + CountryCodeMO CountryCode = "MO" + + CountryCodeMK CountryCode = "MK" + + CountryCodeMG CountryCode = "MG" + + CountryCodeMW CountryCode = "MW" + + CountryCodeMY CountryCode = "MY" + + CountryCodeMV CountryCode = "MV" + + CountryCodeML CountryCode = "ML" + + CountryCodeMT CountryCode = "MT" + + CountryCodeMH CountryCode = "MH" + + CountryCodeMQ CountryCode = "MQ" + + CountryCodeMR CountryCode = "MR" + + CountryCodeMU CountryCode = "MU" + + CountryCodeYT CountryCode = "YT" + + CountryCodeMX CountryCode = "MX" + + CountryCodeFM CountryCode = "FM" + + CountryCodeMD CountryCode = "MD" + + CountryCodeMC CountryCode = "MC" + + CountryCodeMN CountryCode = "MN" + + CountryCodeME CountryCode = "ME" + + CountryCodeMS CountryCode = "MS" + + CountryCodeMA CountryCode = "MA" + + CountryCodeMZ CountryCode = "MZ" + + CountryCodeMM CountryCode = "MM" + + CountryCodeNA CountryCode = "NA" + + CountryCodeNR CountryCode = "NR" + + CountryCodeNP CountryCode = "NP" + + CountryCodeNL CountryCode = "NL" + + CountryCodeNC CountryCode = "NC" + + CountryCodeNZ CountryCode = "NZ" + + CountryCodeNI CountryCode = "NI" + + CountryCodeNE CountryCode = "NE" + + CountryCodeNG CountryCode = "NG" + + CountryCodeNU CountryCode = "NU" + + CountryCodeNF CountryCode = "NF" + + CountryCodeMP CountryCode = "MP" + + CountryCodeNO CountryCode = "NO" + + CountryCodeOM CountryCode = "OM" + + CountryCodePK CountryCode = "PK" + + CountryCodePW CountryCode = "PW" + + CountryCodePS CountryCode = "PS" + + CountryCodePA CountryCode = "PA" + + CountryCodePG CountryCode = "PG" + + CountryCodePY CountryCode = "PY" + + CountryCodePE CountryCode = "PE" + + CountryCodePH CountryCode = "PH" + + CountryCodePN CountryCode = "PN" + + CountryCodePL CountryCode = "PL" + + CountryCodePT CountryCode = "PT" + + CountryCodePR CountryCode = "PR" + + CountryCodeQA CountryCode = "QA" + + CountryCodeRE CountryCode = "RE" + + CountryCodeRO CountryCode = "RO" + + CountryCodeRU CountryCode = "RU" + + CountryCodeRW CountryCode = "RW" + + CountryCodeBL CountryCode = "BL" + + CountryCodeSH CountryCode = "SH" + + CountryCodeKN CountryCode = "KN" + + CountryCodeLC CountryCode = "LC" + + CountryCodeMF CountryCode = "MF" + + CountryCodePM CountryCode = "PM" + + CountryCodeVC CountryCode = "VC" + + CountryCodeWS CountryCode = "WS" + + CountryCodeSM CountryCode = "SM" + + CountryCodeST CountryCode = "ST" + + CountryCodeSA CountryCode = "SA" + + CountryCodeSN CountryCode = "SN" + + CountryCodeRS CountryCode = "RS" + + CountryCodeSC CountryCode = "SC" + + CountryCodeSL CountryCode = "SL" + + CountryCodeSG CountryCode = "SG" + + CountryCodeSX CountryCode = "SX" + + CountryCodeSK CountryCode = "SK" + + CountryCodeSI CountryCode = "SI" + + CountryCodeSB CountryCode = "SB" + + CountryCodeSO CountryCode = "SO" + + CountryCodeZA CountryCode = "ZA" + + CountryCodeGS CountryCode = "GS" + + CountryCodeSS CountryCode = "SS" + + CountryCodeES CountryCode = "ES" + + CountryCodeLK CountryCode = "LK" + + CountryCodeSD CountryCode = "SD" + + CountryCodeSR CountryCode = "SR" + + CountryCodeSJ CountryCode = "SJ" + + CountryCodeSZ CountryCode = "SZ" + + CountryCodeSE CountryCode = "SE" + + CountryCodeCH CountryCode = "CH" + + CountryCodeSY CountryCode = "SY" + + CountryCodeTW CountryCode = "TW" + + CountryCodeTJ CountryCode = "TJ" + + CountryCodeTZ CountryCode = "TZ" + + CountryCodeTH CountryCode = "TH" + + CountryCodeTL CountryCode = "TL" + + CountryCodeTG CountryCode = "TG" + + CountryCodeTK CountryCode = "TK" + + CountryCodeTO CountryCode = "TO" + + CountryCodeTT CountryCode = "TT" + + CountryCodeTN CountryCode = "TN" + + CountryCodeTR CountryCode = "TR" + + CountryCodeTM CountryCode = "TM" + + CountryCodeTC CountryCode = "TC" + + CountryCodeTV CountryCode = "TV" + + CountryCodeUG CountryCode = "UG" + + CountryCodeUA CountryCode = "UA" + + CountryCodeAE CountryCode = "AE" + + CountryCodeGB CountryCode = "GB" + + CountryCodeUS CountryCode = "US" + + CountryCodeUM CountryCode = "UM" + + CountryCodeUY CountryCode = "UY" + + CountryCodeUZ CountryCode = "UZ" + + CountryCodeVU CountryCode = "VU" + + CountryCodeVE CountryCode = "VE" + + CountryCodeVN CountryCode = "VN" + + CountryCodeVG CountryCode = "VG" + + CountryCodeVI CountryCode = "VI" + + CountryCodeWF CountryCode = "WF" + + CountryCodeEH CountryCode = "EH" + + CountryCodeYE CountryCode = "YE" + + CountryCodeZM CountryCode = "ZM" + + CountryCodeZW CountryCode = "ZW" +) + +type CoverageEnum string + +const ( + CoverageEnumCrossProfiles CoverageEnum = "CrossProfiles" + + CoverageEnumLongitudinalProfiles CoverageEnum = "LongitudinalProfiles" + + CoverageEnumFairway CoverageEnum = "Fairway" + + CoverageEnumRiver CoverageEnum = "River" + + CoverageEnumRiverBanks CoverageEnum = "RiverBanks" +) + +type DepthReferenceEnum string + +const ( + DepthReferenceEnumNAP DepthReferenceEnum = "NAP" + + DepthReferenceEnumKP DepthReferenceEnum = "KP" + + DepthReferenceEnumFZP DepthReferenceEnum = "FZP" + + DepthReferenceEnumADR DepthReferenceEnum = "ADR" + + DepthReferenceEnumTAW DepthReferenceEnum = "TAW" + + DepthReferenceEnumPUL DepthReferenceEnum = "PUL" + + DepthReferenceEnumNGM DepthReferenceEnum = "NGM" + + DepthReferenceEnumETRS DepthReferenceEnum = "ETRS" + + DepthReferenceEnumPOT DepthReferenceEnum = "POT" + + DepthReferenceEnumLDC DepthReferenceEnum = "LDC" + + DepthReferenceEnumHDC DepthReferenceEnum = "HDC" + + DepthReferenceEnumZPG DepthReferenceEnum = "ZPG" + + DepthReferenceEnumGLW DepthReferenceEnum = "GLW" + + DepthReferenceEnumHSW DepthReferenceEnum = "HSW" + + DepthReferenceEnumLNW DepthReferenceEnum = "LNW" + + DepthReferenceEnumHNW DepthReferenceEnum = "HNW" + + DepthReferenceEnumIGN DepthReferenceEnum = "IGN" + + DepthReferenceEnumWGS DepthReferenceEnum = "WGS" + + DepthReferenceEnumRN DepthReferenceEnum = "RN" + + DepthReferenceEnumHBO DepthReferenceEnum = "HBO" +) + +type LimitingFactorEnum string + +const ( + LimitingFactorEnumDepth LimitingFactorEnum = "depth" + + LimitingFactorEnumWidth LimitingFactorEnum = "width" + + LimitingFactorEnumCurveRadius LimitingFactorEnum = "curveRadius" +) + +type LosEnum string + +const ( + LosEnumNotAvailable LosEnum = "NotAvailable" + + LosEnumLOS1 LosEnum = "LOS1" + + LosEnumLOS2 LosEnum = "LOS2" + + LosEnumLOS3 LosEnum = "LOS3" +) + +type MaterialEnum string + +const ( + MaterialEnumGravel MaterialEnum = "Gravel" + + MaterialEnumRocky MaterialEnum = "Rocky" + + MaterialEnumStone MaterialEnum = "Stone" + + MaterialEnumAndesite MaterialEnum = "Andesite" + + MaterialEnumSleazyAndesite MaterialEnum = "SleazyAndesite" + + MaterialEnumSandyGravel MaterialEnum = "SandyGravel" + + MaterialEnumMarl MaterialEnum = "Marl" + + MaterialEnumSand MaterialEnum = "Sand" + + MaterialEnumSarmatianLimestone MaterialEnum = "SarmatianLimestone" + + MaterialEnumSandstonePeaks MaterialEnum = "SandstonePeaks" + + MaterialEnumRoughSandyGravel MaterialEnum = "RoughSandyGravel" +) + +type MeasureType string + +const ( + MeasureTypeMeasured MeasureType = "Measured" + + MeasureTypeForecasted MeasureType = "Forecasted" + + MeasureTypeMinimumGuaranteed MeasureType = "MinimumGuaranteed" +) + +type PositionEnum string + +const ( + PositionEnumRedBuoy PositionEnum = "RedBuoy" + + PositionEnumGreenBuoy PositionEnum = "GreenBuoy" + + PositionEnumRightBank PositionEnum = "RightBank" + + PositionEnumLeftBank PositionEnum = "LeftBank" + + PositionEnumMiddle PositionEnum = "Middle" + + PositionEnumAll PositionEnum = "All" +) + +type SurtypEnum string + +const ( + SurtypEnumMultibeam SurtypEnum = "Multibeam" + + SurtypEnumSinglebeam SurtypEnum = "Singlebeam" + + SurtypEnumADCP SurtypEnum = "ADCP" + + SurtypEnumInspectionTour SurtypEnum = "InspectionTour" +) + +type Error struct { + Detail string `xml:"detail,omitempty"` + + Error_code *ErrorCode `xml:"error_code,omitempty"` +} + +type ArrayOfMaterial struct { + Material []*MaterialEnum `xml:"Material,omitempty"` +} + +type ArrayOfKeyValuePair struct { + KeyValuePair []*KeyValuePair `xml:"KeyValuePair,omitempty"` +} + +type KeyValuePair struct { + Key string `xml:"Key,omitempty"` + + Value string `xml:"Value,omitempty"` +} + +type ArrayOfISRSPair struct { + ISRSPair []*ISRSPair `xml:"ISRSPair,omitempty"` +} + +type ISRSPair struct { + FromISRS string `xml:"fromISRS,omitempty"` + + ToISRS string `xml:"toISRS,omitempty"` +} + +type RequestedPeriod struct { + Date_start time.Time `xml:"http://www.ris.eu/wamos/common/3.0 Date_start,omitempty"` + + Date_end time.Time `xml:"http://www.ris.eu/wamos/common/3.0 Date_end,omitempty"` + + Value_interval int32 `xml:"http://www.ris.eu/wamos/common/3.0 Value_interval,omitempty"` +} + +type ArrayOfString struct { + String []string `xml:"http://www.ris.eu/wamos/common/3.0 string,omitempty"` +} + +type Char int32 + +type Duration *Duration + +type Guid string + +type IFairwayAvailabilityService interface { + + // Error can be either of the following types: + // + // - ErrorFault + + Get_bottleneck_fa(request *Get_bottleneck_fa) (*Get_bottleneck_faResponse, error) + + // Error can be either of the following types: + // + // - ErrorFault + + Get_stretch_fa(request *Get_stretch_fa) (*Get_stretch_faResponse, error) +} + +type FairwayAvailabilityService struct { + client *soap.SOAPClient +} + +func NewFairwayAvailabilityService(url string, tls bool, auth *soap.BasicAuth) *FairwayAvailabilityService { + if url == "" { + url = "" + } + client := soap.NewSOAPClient(url, tls, auth) + return &FairwayAvailabilityService{ + client: client, + } +} + +func NewFairwayAvailabilityServiceWithTLS(url string, tlsCfg *tls.Config, auth *soap.BasicAuth) *FairwayAvailabilityService { + if url == "" { + url = "" + } + client := soap.NewSOAPClientWithTLSConfig(url, tlsCfg, auth) + + return &FairwayAvailabilityService{ + client: client, + } +} + +func (service *FairwayAvailabilityService) Get_bottleneck_fa(request *Get_bottleneck_fa) (*Get_bottleneck_faResponse, error) { + response := new(Get_bottleneck_faResponse) + err := service.client.Call("http://www.ris.eu/fairwayavailability/3.0/IFairwayAvailabilityService/get_bottleneck_fa", request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (service *FairwayAvailabilityService) Get_stretch_fa(request *Get_stretch_fa) (*Get_stretch_faResponse, error) { + response := new(Get_stretch_faResponse) + err := service.client.Call("http://www.ris.eu/fairwayavailability/3.0/IFairwayAvailabilityService/get_stretch_fa", request, response) + if err != nil { + return nil, err + } + + return response, nil +}
--- a/pkg/wfs/capabilities.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/wfs/capabilities.go Tue Jan 15 10:07:10 2019 +0100 @@ -15,6 +15,7 @@ import ( "encoding/xml" + "errors" "io" "regexp" "strconv" @@ -22,15 +23,19 @@ "golang.org/x/net/html/charset" ) +// Keyword stores a value. type Keyword struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keyword"` Value string `xml:",cdata"` } + +// Keywords stores a list of keywords. type Keywords struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keywords"` Keywords []Keyword `xml:"Keyword"` } +// ServiceIdentification contains meta informations about a WFS. type ServiceIdentification struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 ServiceIdentification"` Title string @@ -40,48 +45,58 @@ ServiceTypeVersion string } +// Get stores the link to the GET method type Get struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Get"` HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` } +// Post stores the link to the POST method. type Post struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Post"` HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` } +// HTTP is a container for HTTP methods. type HTTP struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 HTTP"` Get *Get `xml:"Get"` Post *Post `xml:"Post"` } +// DCP wraps the HTTP container. type DCP struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DCP"` HTTP HTTP `xml:"HTTP"` } +// Value is a simple string value. type Value struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Value"` Value string `xml:",cdata"` } +// AllowedValues is list positive list of values. type AllowedValues struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 AllowedValues"` Values []Value `xml:"Value"` } +// Parameter is a named parameter with a list of allowed values. type Parameter struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Parameter"` Name string `xml:"name,attr"` AllowedValues AllowedValues `xml:"AllowedValues"` } +// DefaultValue is the default value of a constraint. type DefaultValue struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DefaultValue"` Value string `xml:",cdata"` } +// Constraint is a named constraint with a list of allowed values +// and a default value. type Constraint struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Constraint"` Name string `xml:"name,attr"` @@ -89,6 +104,7 @@ DefaultValue *DefaultValue `xml:"DefaultValue"` } +// Operation contains informations of a WFS operation. type Operation struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Operation"` Name string `xml:"name,attr"` @@ -97,18 +113,21 @@ Constraints []*Constraint `xml:"Constraint"` } +// OperationsMetadata is list of operations and constraints. type OperationsMetadata struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 OperationsMetadata"` Operations []*Operation `xml:"Operation"` Constraints []*Constraint `xml:"Constraint"` } +// WGS84BoundingBox is a bounding box feature type in WGS84. type WGS84BoundingBox struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 WGS84BoundingBox"` LowerCorner string `xml:"LowerCorner"` UpperCorner string `xml:"UpperCorner"` } +// FeatureType is layer served by the WFS: type FeatureType struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureType"` Name string `xml:"Name"` @@ -134,6 +153,7 @@ Namespaces []xml.Name `xml:"-"` } +// UnmarshalXML implements xml.Unmarshaler for better namespace handling. func (ft *FeatureType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // Filter out the namespaces for this feature type. var ns []xml.Name @@ -151,11 +171,13 @@ return nil } +// FeatureTypeList is the list of layers served by the WFS. type FeatureTypeList struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureTypeList"` FeatureTypes []*FeatureType `xml:"FeatureType"` } +// Capabilities is the top level metadata struct. type Capabilities struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 WFS_Capabilities"` @@ -166,6 +188,8 @@ FeatureTypeList FeatureTypeList } +// FindOperation searches the capabilities for a specifc operation. +// Returns nil if not found. func (c *Capabilities) FindOperation(name string) *Operation { for _, op := range c.OperationsMetadata.Operations { if op.Name == name { @@ -175,8 +199,9 @@ return nil } -func (o *Operation) SupportsHits() bool { - for _, p := range o.Parameters { +// SupportsHits checks if a operation supports the hits request. +func (op *Operation) SupportsHits() bool { + for _, p := range op.Parameters { if p.Name == "resultType" { for _, av := range p.AllowedValues.Values { if av.Value == "hits" { @@ -188,8 +213,9 @@ return false } -func (o *Operation) SupportsOutputFormat(formats ...string) bool { - for _, p := range o.Parameters { +// SupportsOutputFormat checks if one of the given formats is supported. +func (op *Operation) SupportsOutputFormat(formats ...string) bool { + for _, p := range op.Parameters { if p.Name == "outputFormat" { for _, av := range p.AllowedValues.Values { for _, f := range formats { @@ -203,8 +229,10 @@ return false } -func (o *Operation) FeaturesPerPage() (int, bool) { - for _, c := range o.Constraints { +// FeaturesPerPage returns the number of features per page. +// Returns if paging is not supported by the operation. +func (op *Operation) FeaturesPerPage() (int, bool) { + for _, c := range op.Constraints { if c.Name == "CountDefault" { if c.DefaultValue != nil { if v, err := strconv.Atoi(c.DefaultValue.Value); err == nil { @@ -222,6 +250,8 @@ return 0, false } +// FindFeatureType searches the layers for a given name. +// Returns nil if not found. func (c *Capabilities) FindFeatureType(name string) *FeatureType { for _, ft := range c.FeatureTypeList.FeatureTypes { if ft.Name == name { @@ -231,6 +261,8 @@ return nil } +// FindParameter searches for named parameter. Returns nil +// if not found. func (op *Operation) FindParameter(name string) *Parameter { for _, p := range op.Parameters { if p.Name == name { @@ -240,7 +272,8 @@ return nil } -const WFS2_0_0 = "2.0.0" +// WFS200 is dotted version string of version 2.0.0. +const WFS200 = "2.0.0" var versionRe = regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)`) @@ -294,6 +327,8 @@ return a } +// HighestWFSVersion figures out the highest supported WFS version. +// Defaults to def. func (c *Capabilities) HighestWFSVersion(def string) string { op := c.FindOperation("GetCapabilities") if op == nil { @@ -315,6 +350,22 @@ return max } +var ( + // ErrInvalidCRS is returned if a given string is not valid CRS URN. + ErrInvalidCRS = errors.New("Invalid CRS string") + crsRe = regexp.MustCompile(`urn:ogc:def:crs:EPSG:[^:]*:(\d+)`) +) + +// CRSToEPSG extracts the EPSG code from a given CRS URN string. +func CRSToEPSG(s string) (int, error) { + m := crsRe.FindStringSubmatch(s) + if m == nil { + return 0, ErrInvalidCRS + } + return strconv.Atoi(m[1]) +} + +// ParseCapabilities constructs a capabilities document from an io.Reader. func ParseCapabilities(r io.Reader) (*Capabilities, error) { decoder := xml.NewDecoder(r)
--- a/pkg/wfs/download.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/wfs/download.go Tue Jan 15 10:07:10 2019 +0100 @@ -1,4 +1,4 @@ -// This is Free Software under GNU Affero General Public License v >= 3.0 +// This is Free Software under GNU Affero General Public License v >= 3.0.Reader. // without warranty, see README.md and license for details. // // SPDX-License-Identifier: AGPL-3.0-or-later @@ -28,13 +28,20 @@ ) var ( - ErrNoSuchFeatureType = errors.New("No such feature type") - ErrGetFeatureNotSupported = errors.New("GetFeature not supported") - ErrMethodGetNotSupported = errors.New("GET not supported") - ErrNoNumberMatchedFound = errors.New("No numberMatched attribute found") + // ErrNoSuchFeatureType is returned when a feature is not supported. + ErrNoSuchFeatureType = errors.New("No such feature type") + // ErrGetFeatureNotSupported is returned when GetFeature is not supported. + ErrGetFeatureNotSupported = errors.New("GetFeature not supported") + // ErrMethodGetNotSupported is returned when the GET is not supported. + ErrMethodGetNotSupported = errors.New("GET not supported") + // ErrNoNumberMatchedFound is returned if feature count cannot be extracted. + ErrNoNumberMatchedFound = errors.New("No numberMatched attribute found") + // ErrOutputFormatNotSupported is returned if a output format is + // not supported. ErrOutputFormatNotSupported = errors.New("Output format not supported") ) +// GetCapabilities downloads a capabilities document for a given URL. func GetCapabilities(capURL string) (*Capabilities, error) { base, err := url.Parse(capURL) @@ -95,6 +102,8 @@ return *result.NumberMatched, nil } +// GetFeaturesGET constructs a list of URLs to get features +// for a given feature type from a WFS servers. func GetFeaturesGET( caps *Capabilities, featureTypeName, @@ -133,7 +142,7 @@ return nil, ErrOutputFormatNotSupported } - wfsVersion := caps.HighestWFSVersion(WFS2_0_0) + wfsVersion := caps.HighestWFSVersion(WFS200) featuresPerPage, supportsPaging := op.FeaturesPerPage() @@ -157,7 +166,7 @@ } var downloadURLs []string - wfs2 := !versionIsLess(wfsVersion, WFS2_0_0) + wfs2 := !versionIsLess(wfsVersion, WFS200) addNS := func(v url.Values) { if len(feature.Namespaces) == 0 { @@ -250,6 +259,8 @@ return handler(resp.Body) } +// DownloadURLs does the actual GetFeature requests downloads +// and hands the resulting io.Readers over to the given handler. func DownloadURLs(urls []string, handler func(io.Reader) error) error { for _, url := range urls { if err := downloadURL(url, handler); err != nil {
--- a/pkg/wfs/rawfeaturecollection.go Tue Jan 15 09:54:46 2019 +0100 +++ b/pkg/wfs/rawfeaturecollection.go Tue Jan 15 10:07:10 2019 +0100 @@ -18,6 +18,8 @@ "io" ) +// RawFeatureCollection is a template for a feature collection +// returned by a WFS server. type RawFeatureCollection struct { CRS *struct { Properties struct { @@ -33,6 +35,7 @@ } `json:"features"` } +// ParseRawFeatureCollection turns a io.Reader into raw feature collection. func ParseRawFeatureCollection(r io.Reader) (*RawFeatureCollection, error) { rfc := new(RawFeatureCollection) if err := json.NewDecoder(r).Decode(rfc); err != nil {
--- a/schema/auth.sql Tue Jan 15 09:54:46 2019 +0100 +++ b/schema/auth.sql Tue Jan 15 10:07:10 2019 +0100 @@ -77,8 +77,7 @@ DECLARE the_table varchar; BEGIN FOREACH the_table IN ARRAY ARRAY[ - -- 'gauge_measurements', XXX Removed since this table has currently no - -- staging + 'gauge_measurements', 'sections_stretches', 'waterway_profiles', 'fairway_dimensions', @@ -143,6 +142,10 @@ -- Staging area -- TODO: add all relevant tables here +CREATE POLICY same_country ON waterway.gauge_measurements + FOR ALL TO waterway_admin + USING ((fk_gauge_id).country_code = users.current_user_country()); + CREATE POLICY responsibility_area ON waterway.bottlenecks FOR ALL TO waterway_admin USING (utm_covers(area)); @@ -151,6 +154,34 @@ FOR ALL TO waterway_admin USING (utm_covers(area)); +-- Imports and import config + +CREATE POLICY same_country ON waterway.imports + FOR ALL TO waterway_admin + USING (users.current_user_country() = ( + SELECT country FROM users.list_users lu + WHERE lu.username = imports.username)); +ALTER table waterway.imports ENABLE ROW LEVEL SECURITY; + +-- The job running the import queue is running as sys_admin and login users +-- with that role should see all imports anyhow +CREATE POLICY read_all ON waterway.imports + FOR SELECT TO sys_admin + USING (true); +CREATE POLICY update_all ON waterway.imports + FOR UPDATE TO sys_admin + USING (true); + +CREATE POLICY parent_allowed ON waterway.import_logs + FOR ALL TO waterway_admin + USING (import_id IN (SELECT id FROM waterway.imports)); +ALTER table waterway.import_logs ENABLE ROW LEVEL SECURITY; + +CREATE POLICY parent_allowed ON waterway.track_imports + FOR ALL TO waterway_admin + USING (import_id IN (SELECT id FROM waterway.imports)); +ALTER table waterway.track_imports ENABLE ROW LEVEL SECURITY; + CREATE POLICY import_configuration_policy ON waterway.import_configuration FOR ALL TO waterway_admin USING ( @@ -158,6 +189,10 @@ SELECT country FROM users.list_users lu WHERE lu.username = waterway.import_configuration.username)); +CREATE POLICY import_configuration_policy_sys_admin ON waterway.import_configuration + FOR ALL TO sys_admin + USING (true); + ALTER table waterway.import_configuration ENABLE ROW LEVEL SECURITY; COMMIT;
--- a/schema/demo-data/published_services.sql Tue Jan 15 09:54:46 2019 +0100 +++ b/schema/demo-data/published_services.sql Tue Jan 15 10:07:10 2019 +0100 @@ -16,4 +16,6 @@ ('waterway.distance_marks_geoserver'), ('waterway.sounding_results_contour_lines_geoserver'), ('waterway.bottlenecks'), - ('waterway.bottleneck_overview') + ('waterway.bottleneck_overview'), + ('waterway.waterway_axis'), + ('waterway.waterway_area')
--- a/schema/gemma.sql Tue Jan 15 09:54:46 2019 +0100 +++ b/schema/gemma.sql Tue Jan 15 10:07:10 2019 +0100 @@ -139,9 +139,13 @@ ); CREATE TABLE levels_of_service ( - level_of_service smallint PRIMARY KEY + level_of_service smallint PRIMARY KEY, + name varchar(4) ); -INSERT INTO levels_of_service VALUES (1), (2), (3); +INSERT INTO levels_of_service ( + level_of_service, + name +) VALUES (1, 'LOS1'), (2, 'LOS2'), (3, 'LOS3'); CREATE TABLE riverbed_materials ( material varchar PRIMARY KEY @@ -264,14 +268,12 @@ id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, fk_gauge_id isrs NOT NULL REFERENCES gauges, measure_date timestamp with time zone NOT NULL, - -- PRIMARY KEY (fk_gauge_id, measure_date), country_code char(2) NOT NULL REFERENCES countries, -- TODO: add relations to stuff provided as enumerations sender varchar NOT NULL, -- "from" attribute from DRC language_code varchar NOT NULL REFERENCES language_codes, date_issue timestamp with time zone NOT NULL, - -- reference_code varchar(4) NOT NULL REFERENCES depth_references, - -- XXX: Always ZPG? + reference_code varchar(4) NOT NULL REFERENCES depth_references, water_level double precision NOT NULL, predicted boolean NOT NULL, is_waterlevel boolean NOT NULL, @@ -281,11 +283,11 @@ value_max double precision, -- XXX: NOT NULL if predicted? --- TODO: Add a double range type for checking? date_info timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, - source_organization varchar NOT NULL -- "originator" - -- XXX removed staging done temporarily. Currently imported raw data is - -- not staged. When importing approved gauge measurements uncomment this - -- and add policy to allow select on this table for waterway_admin - -- staging_done boolean NOT NULL DEFAULT false + source_organization varchar NOT NULL, -- "originator" + staging_done boolean NOT NULL DEFAULT false, + -- So we can have a staged and + -- a non-staged fk_gauge_id/measure_date pair. + UNIQUE (fk_gauge_id, measure_date, staging_done) ) CREATE TRIGGER gauge_measurements_date_info BEFORE UPDATE ON gauge_measurements @@ -298,7 +300,8 @@ objnam varchar NOT NULL, nobjnam varchar ) - CREATE UNIQUE INDEX ON waterway_axis ((ST_GeoHash(wtwaxs, 23))) + -- TODO: @tom: Why did you choose this index kind? + -- CREATE UNIQUE INDEX ON waterway_axis ((ST_GeoHash(wtwaxs, 23))) -- This table allows linkage between 1D ISRS location codes and 2D space -- e.g. for cutting bottleneck area out of waterway area based on virtual @@ -554,10 +557,19 @@ ON UPDATE CASCADE, kind varchar NOT NULL, send_email boolean NOT NULL DEFAULT false, - auto_accept boolean NOT NULL DEFAULT false, cron varchar, url varchar ) + + CREATE TABLE import_configuration_attributes ( + import_configuration_id int NOT NULL + REFERENCES import_configuration(id) + ON DELETE CASCADE + ON UPDATE CASCADE, + k VARCHAR NOT NULL, + v TEXT NOT NULL, + UNIQUE (import_configuration_id, k) + ) ; -- Configure primary keys for geoserver views @@ -575,11 +587,13 @@ ); CREATE TABLE waterway.imports ( - id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, - state waterway.import_state NOT NULL DEFAULT 'queued', - enqueued timestamp NOT NULL DEFAULT now(), - kind varchar NOT NULL, - username varchar NOT NULL + id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + state waterway.import_state NOT NULL DEFAULT 'queued', + kind varchar NOT NULL, + enqueued timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + due timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + trys_left int, + username varchar NOT NULL REFERENCES internal.user_profiles(username) ON DELETE CASCADE ON UPDATE CASCADE, @@ -588,9 +602,8 @@ ON DELETE SET NULL ON UPDATE CASCADE, send_email boolean NOT NULL DEFAULT false, - auto_accept boolean NOT NULL DEFAULT false, - data TEXT, - summary TEXT + data TEXT, + summary TEXT ); CREATE INDEX enqueued_idx ON waterway.imports(enqueued, state);
--- a/schema/isrs_functions.sql Tue Jan 15 09:54:46 2019 +0100 +++ b/schema/isrs_functions.sql Tue Jan 15 10:07:10 2019 +0100 @@ -1,3 +1,17 @@ +-- This is Free Software under GNU Affero General Public License v >= 3.0 +-- without warranty, see README.md and license for details. + +-- SPDX-License-Identifier: AGPL-3.0-or-later +-- License-Filename: LICENSES/AGPL-3.0.txt + +-- Copyright (C) 2018 by via donau +-- – Österreichische Wasserstraßen-Gesellschaft mbH +-- Software engineering by Intevation GmbH + +-- Author(s): +-- * Tom Gottfried <tom@intevation.de> +-- * Sascha Wilde <wilde@intevation.de> + -- Clip an area to a stretch given by a pair of ISRS location codes. -- Uses the table waterway.distance_marks_virtual to map ISRS location codes -- to their geo-location and the table waterway.waterway_axis to retrieve @@ -49,7 +63,7 @@ FROM axis) AS lines, (SELECT ST_Collect(from_point.geom, to_point.geom) AS pts FROM from_point, to_point) AS points - WHERE ST_Covers(lines.line, points.pts)), + WHERE ST_Covers(ST_Buffer(lines.line, 0.0001), points.pts)), axis_substring AS ( -- Use linear referencing to clip axis between distance marks SELECT ST_LineSubstring(