changeset 1338:5bb1e3c1f364

merges
author Fadi Abbud <fadi.abbud@intevation.de>
date Mon, 26 Nov 2018 09:01:21 +0100
parents 53057ab27ff2 (current diff) 813342f2e927 (diff)
children 1d1fc92fc3ea 3fee649d3d5d
files pkg/controllers/search.go pkg/imports/sr.go schema/gemma.sql schema/install-db.sh schema/manage_users.sql schema/std_login_roles.sql
diffstat 41 files changed, 341 insertions(+), 218 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/map/Main.vue	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/components/map/Main.vue	Mon Nov 26 09:01:21 2018 +0100
@@ -1,10 +1,8 @@
 <template>
     <div class="main d-flex flex-column">
-        <Maplayer :split="showSplitscreen" :lat="6155376" :long="1819178" :zoom="11"></Maplayer>
+        <Maplayer :split="showSplitscreen"></Maplayer>
         <FairwayProfile
             :additionalSurveys="additionalSurveys"
-            :height="height"
-            :width="width"
             :xScale="xAxis"
             :yScaleLeft="yAxisLeft"
             :yScaleRight="yAxisRight"
@@ -32,7 +30,6 @@
 import Maplayer from "./Maplayer";
 import FairwayProfile from "./fairway/Fairwayprofile";
 import { mapState } from "vuex";
-import debounce from "debounce";
 
 export default {
   name: "mainview",
@@ -97,30 +94,6 @@
         y: this.totalLength
       };
     }
-  },
-  created() {
-    window.addEventListener("resize", debounce(this.scaleFairwayProfile), 100);
-    window.addEventListener("onbeforeprint", this.test);
-  },
-  updated() {
-    this.scaleFairwayProfile();
-  },
-  destroyed() {
-    window.removeEventListener("resize", debounce(this.scaleFairwayProfile));
-  },
-  methods: {
-    test(evt) {
-      console.log("test: ", evt);
-    },
-    scaleFairwayProfile() {
-      if (!document.querySelector(".fairwayprofile")) return;
-      const clientHeight = document.querySelector(".fairwayprofile")
-        .clientHeight;
-      const clientWidth = document.querySelector(".fairwayprofile").clientWidth;
-      if (!clientHeight || !clientWidth) return;
-      this.height = clientHeight;
-      this.width = clientWidth;
-    }
   }
 };
 </script>
--- a/client/src/components/map/Maplayer.vue	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/components/map/Maplayer.vue	Mon Nov 26 09:01:21 2018 +0100
@@ -50,7 +50,7 @@
 /* eslint-disable no-console */
 export default {
   name: "maplayer",
-  props: ["lat", "long", "zoom", "split"],
+  props: ["split"],
   data() {
     return {
       projection: "EPSG:3857"
@@ -59,6 +59,7 @@
   computed: {
     ...mapGetters("map", ["getLayerByName"]),
     ...mapState("map", [
+      "extent",
       "layers",
       "openLayersMap",
       "lineTool",
@@ -261,11 +262,19 @@
       target: "map",
       controls: [],
       view: new View({
-        center: [this.long, this.lat],
-        zoom: this.zoom,
+        center: [this.extent.lon, this.extent.lat],
+        zoom: this.extent.zoom,
         projection: this.projection
       })
     });
+    map.on("moveend", event => {
+      const center = event.map.getView().getCenter();
+      this.$store.commit("map/extent", {
+        lat: center[1],
+        lon: center[0],
+        zoom: event.map.getView().getZoom()
+      });
+    });
     this.$store.dispatch("map/openLayersMap", map);
 
     // TODO make display of layers more dynamic, e.g. from a list
--- a/client/src/components/map/contextbox/ImportSoundingresults.vue	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/components/map/contextbox/ImportSoundingresults.vue	Mon Nov 26 09:01:21 2018 +0100
@@ -1,8 +1,7 @@
 <template>
     <div>
         <h6 class="mb-0 py-2 px-3 border-bottom d-flex align-items-center">
-            <font-awesome-icon icon="upload" class="mr-2"></font-awesome-icon>
-            Import Soundingresults
+            <font-awesome-icon icon="upload" class="mr-2"></font-awesome-icon>Import Soundingresults
         </h6>
         <div v-if="editState" class="ml-auto mr-auto mt-4 w-95">
             <div class="d-flex flex-row input-group mb-4">
@@ -195,9 +194,12 @@
     confirm() {
       let formData = new FormData();
       formData.append("token", this.token);
-      ["bottleneck", "importDate", "depthReference"].forEach(x => {
-        if (this[x]) formData.append(x, this[x]);
-      });
+      if (this.bottleneck) formData.append("bottleneck", this.bottleneck);
+      if (this.importDate)
+        formData.append("date", this.importDate.split("T")[0]);
+      if (this.depthReference)
+        formData.append("depth-reference", this.depthReference);
+
       HTTP.post("/imports/soundingresult", formData, {
         headers: {
           "X-Gemma-Auth": localStorage.getItem("token"),
--- a/client/src/components/map/contextbox/Staging.vue	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/components/map/contextbox/Staging.vue	Mon Nov 26 09:01:21 2018 +0100
@@ -1,8 +1,7 @@
 <template>
     <div class="w-90">
         <h6 class="mb-0 py-2 px-3 border-bottom d-flex align-items-center">
-            <font-awesome-icon icon="clipboard-check" class="mr-2"></font-awesome-icon>
-            Staging Area
+            <font-awesome-icon icon="clipboard-check" class="mr-2"></font-awesome-icon>Staging Area
         </h6>
         <table class="table mb-0">
             <thead>
@@ -48,7 +47,7 @@
             </tbody>
         </table>
         <div class="p-3" v-if="filteredData.length">
-            <button class="btn btn-info">Confirm</button>
+            <button @click="confirmReview" class="btn btn-info">Confirm</button>
         </div>
     </div>
 </template>
@@ -69,6 +68,8 @@
  */
 import { mapState } from "vuex";
 
+import { displayError, displayInfo } from "../../../lib/errors.js";
+
 export default {
   STATES: {
     NEEDSAPPROVAL: "NEEDSAPPROVAL",
@@ -117,6 +118,15 @@
       ]
     };
   },
+  mounted() {
+    this.$store.dispatch("imports/getStaging").catch(error => {
+      const { status, data } = error.response;
+      displayError({
+        title: "Backend Error",
+        message: `${status}: ${data.message || data}`
+      });
+    });
+  },
   computed: {
     ...mapState("application", ["searchQuery"]),
     filteredData() {
@@ -155,6 +165,17 @@
     }
   },
   methods: {
+    confirmReview() {
+      const message = this.demodata
+        .map(x => {
+          return x.name + ": " + x.status;
+        })
+        .join("\n");
+      displayInfo({
+        title: "Staging Area",
+        message: message
+      });
+    },
     needsApproval(item) {
       return item.status === this.$options.STATES.NEEDSAPPROVAL;
     },
--- a/client/src/components/map/fairway/Fairwayprofile.vue	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/components/map/fairway/Fairwayprofile.vue	Mon Nov 26 09:01:21 2018 +0100
@@ -155,27 +155,22 @@
 import { displayError, displayInfo } from "../../../lib/errors.js";
 import Feature from "ol/Feature";
 import LineString from "ol/geom/LineString";
+import debounce from "debounce";
 
 const GROUND_COLOR = "#4A2F06";
 
 export default {
   name: "fairwayprofile",
-  props: [
-    "width",
-    "height",
-    "xScale",
-    "yScaleLeft",
-    "yScaleRight",
-    "margin",
-    "additionalSurveys"
-  ],
+  props: ["xScale", "yScaleLeft", "yScaleRight", "margin", "additionalSurveys"],
   data() {
     return {
       wait: false,
       coordinatesInput: "",
       coordinatesSelect: null,
       cutLabel: "",
-      showLabelInput: false
+      showLabelInput: false,
+      width: null,
+      height: null
     };
   },
   computed: {
@@ -304,6 +299,7 @@
       this.coordinatesSelect = null;
       const chartDiv = document.querySelector(".fairwayprofile");
       d3.select(".fairwayprofile svg").remove();
+      this.scaleFairwayProfile();
       let svg = d3.select(chartDiv).append("svg");
       svg.attr("width", this.width);
       svg.attr("height", this.height);
@@ -514,6 +510,15 @@
           .attr("d", profileLine);
       }
     },
+    scaleFairwayProfile() {
+      if (!document.querySelector(".fairwayprofile")) return;
+      const clientHeight = document.querySelector(".fairwayprofile")
+        .clientHeight;
+      const clientWidth = document.querySelector(".fairwayprofile").clientWidth;
+      if (!clientHeight || !clientWidth) return;
+      this.height = clientHeight;
+      this.width = clientWidth;
+    },
     onCopyCoordinates() {
       displayInfo({
         title: "Success",
@@ -580,8 +585,17 @@
       });
     }
   },
+  created() {
+    window.addEventListener("resize", debounce(this.drawDiagram), 100);
+  },
   mounted() {
     this.drawDiagram();
+  },
+  updated() {
+    this.scaleFairwayProfile();
+  },
+  destroyed() {
+    window.removeEventListener("resize", debounce(this.drawDiagram));
   }
 };
 </script>
--- a/client/src/store/imports.js	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/store/imports.js	Mon Nov 26 09:01:21 2018 +0100
@@ -15,10 +15,19 @@
 
 import { HTTP } from "../lib/http";
 
+/* eslint-disable no-unused-vars */
+/* eslint-disable no-unreachable */
+const STATES = {
+  NEEDSAPPROVAL: "NEEDSAPPROVAL",
+  APPROVED: "APPROVED",
+  REJECTED: "REJECTED"
+};
+
 // initial state
 const init = () => {
   return {
-    imports: {}
+    imports: [],
+    staging: []
   };
 };
 
@@ -29,6 +38,22 @@
   mutations: {
     setImports: (state, imports) => {
       state.imports = imports;
+    },
+    setStaging: (state, staging) => {
+      state.staging = staging;
+    },
+
+    toggleApproval: (state, change) => {
+      throw "Not implemented!";
+      const { id, newState } = change;
+      const stagedResult = this.state.staging.find(e => {
+        return e.id === id;
+      });
+      if (stagedResult.status === newState) {
+        stagedResult.status = this.$options.STATES.NEEDSAPPROVAL;
+      } else {
+        stagedResult.status = newState;
+      }
     }
   },
   actions: {
@@ -45,6 +70,35 @@
             reject(error);
           });
       });
+    },
+    getStaging({ commit }) {
+      return new Promise((resolve, reject) => {
+        HTTP.get("/imports?states=pending", {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            commit("setStaging", response.data.imports);
+            resolve(response);
+          })
+          .catch(error => {
+            reject(error);
+          });
+      });
+    },
+    setStaging({ commit }, results) {
+      return new Promise((resolve, reject) => {
+        throw "Not implemented!";
+        HTTP.get("/imports?states=pending", {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            commit("setStaging", response.data.imports);
+            resolve(response);
+          })
+          .catch(error => {
+            reject(error);
+          });
+      });
     }
   }
 };
--- a/client/src/store/map.js	Fri Nov 23 15:46:07 2018 +0100
+++ b/client/src/store/map.js	Mon Nov 26 09:01:21 2018 +0100
@@ -31,6 +31,11 @@
 const init = () => {
   return {
     openLayersMap: null,
+    extent: {
+      lat: 6155376,
+      lon: 1819178,
+      zoom: 11
+    },
     identifiedFeatures: [], // map features identified by clicking on the map
     currentMeasurement: null, // distance or area from line-/polygon-/cutTool
     lineTool: null, // open layers interaction object (Draw)
@@ -347,6 +352,9 @@
     }
   },
   mutations: {
+    extent: (state, extent) => {
+      state.extent = extent;
+    },
     toggleVisibility: (state, layer) => {
       state.layers[layer].isVisible = !state.layers[layer].isVisible;
       state.layers[layer].data.setVisible(state.layers[layer].isVisible);
--- a/pkg/auth/middleware.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/auth/middleware.go	Mon Nov 26 09:01:21 2018 +0100
@@ -65,8 +65,7 @@
 
 func SessionChecker(next http.Handler, check func(*Session) bool) http.Handler {
 	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
-		claims, ok := GetSession(req)
-		if !ok || !check(claims) {
+		if claims, ok := GetSession(req); !ok || !check(claims) {
 			http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
 			return
 		}
--- a/pkg/auth/opendb.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/auth/opendb.go	Mon Nov 26 09:01:21 2018 +0100
@@ -126,7 +126,7 @@
 	return roles, rows.Err()
 }
 
-func RunAs(role string, ctx context.Context, fn func(*sql.Conn) error) error {
+func RunAs(ctx context.Context, role string, fn func(*sql.Conn) error) error {
 	conn, err := MetamorphConn(ctx, role)
 	if err != nil {
 		return err
@@ -144,5 +144,5 @@
 	if session == nil {
 		return ErrNotLoggedIn
 	}
-	return RunAs(session.User, req.Context(), fn)
+	return RunAs(req.Context(), session.User, fn)
 }
--- a/pkg/auth/session.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/auth/session.go	Mon Nov 26 09:01:21 2018 +0100
@@ -76,7 +76,7 @@
 		return err
 	}
 
-	wr := misc.BinWriter{w, nil}
+	wr := misc.BinWriter{Writer: w, Err: nil}
 	wr.WriteBin(s.ExpiresAt)
 	wr.WriteString(s.User)
 	wr.WriteBin(uint32(len(s.Roles)))
@@ -90,17 +90,15 @@
 
 func (s *Session) deserialize(r io.Reader) error {
 
-	var session Session
-
 	var n uint32
-	rd := misc.BinReader{r, nil}
-	rd.ReadBin(&session.ExpiresAt)
-	rd.ReadString(&session.User)
+	rd := misc.BinReader{Reader: r, Err: nil}
+	rd.ReadBin(&s.ExpiresAt)
+	rd.ReadString(&s.User)
 	rd.ReadBin(&n)
-	session.Roles = make(Roles, n)
+	s.Roles = make(Roles, n)
 
 	for i := uint32(0); n > 0 && i < n; i++ {
-		rd.ReadString(&session.Roles[i])
+		rd.ReadString(&s.Roles[i])
 	}
 
 	if rd.Err != nil {
@@ -121,23 +119,21 @@
 		return err
 	}
 
-	session.access = t
-
-	*s = session
+	s.access = t
 
 	return nil
 }
 
-func (c *Session) touch() {
-	c.mu.Lock()
-	c.access = time.Now()
-	c.mu.Unlock()
+func (s *Session) touch() {
+	s.mu.Lock()
+	s.access = time.Now()
+	s.mu.Unlock()
 }
 
-func (c *Session) last() time.Time {
-	c.mu.Lock()
-	access := c.access
-	c.mu.Unlock()
+func (s *Session) last() time.Time {
+	s.mu.Lock()
+	access := s.access
+	s.mu.Unlock()
 	return access
 }
 
--- a/pkg/common/random.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/common/random.go	Mon Nov 26 09:01:21 2018 +0100
@@ -51,7 +51,7 @@
 			out[i] = alphabet[v.Int64()]
 		}
 		// Ensure at least one special char.
-		if bytes.IndexAny(out, special) >= 0 {
+		if bytes.ContainsAny(out, special) {
 			return string(out)
 		}
 	}
--- a/pkg/config/config.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/config/config.go	Mon Nov 26 09:01:21 2018 +0100
@@ -10,6 +10,7 @@
 //
 // Author(s):
 //  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
+//  * Bernhard E. Reiter <bernhard.reiter@intevation.de>
 
 package config
 
@@ -29,8 +30,6 @@
 // This is not part of the persistent config.
 var configFile string
 
-func ConfigFile() string { return configFile }
-
 func DBHost() string     { return viper.GetString("db-host") }
 func DBPort() uint       { return uint(viper.GetInt32("db-port")) }
 func DBName() string     { return viper.GetString("db-name") }
--- a/pkg/controllers/cross.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/cross.go	Mon Nov 26 09:01:21 2018 +0100
@@ -28,16 +28,17 @@
 const WGS84 = 4326
 
 func reproject(
+	ctx context.Context,
 	rp *models.Reprojector,
 	src models.GeoJSONLineCoordinates,
-	ctx context.Context,
 ) (models.GeoJSONLineCoordinates, error) {
 
 	dst := make(models.GeoJSONLineCoordinates, len(src))
 	for i, s := range src {
 		var err error
 		if dst[i].Lat, dst[i].Lon, err = rp.Reproject(
-			s.Lat, s.Lon, ctx,
+			ctx,
+			s.Lat, s.Lon,
 		); err != nil {
 			return nil, err
 		}
@@ -50,10 +51,10 @@
   ST_Transform(ST_GeomFromWKB($2, $1::integer), 4326))`
 
 func projectBack(
+	ctx context.Context,
 	line octree.MultiLineStringZ,
 	epsg uint32,
 	conn *sql.Conn,
-	ctx context.Context,
 ) (models.GeoJSONMultiLineCoordinatesZ, error) {
 
 	var mls models.GeoJSONMultiLineCoordinatesZ
@@ -74,10 +75,11 @@
 	csi := input.(*models.CrossSectionInput)
 
 	start := time.Now()
+	ctx := req.Context()
 
-	tree, err := octree.Cache.Get(
-		csi.Properties.Bottleneck, csi.Properties.Date.Time,
-		conn, req.Context())
+	tree, err := octree.FromCache(
+		ctx, conn,
+		csi.Properties.Bottleneck, csi.Properties.Date.Time)
 
 	log.Printf("loading octree took: %s\n", time.Since(start))
 	if err != nil {
@@ -102,14 +104,14 @@
 
 	var rp *models.Reprojector
 	if rp, err = models.NewReprojector(
-		conn, req.Context(),
+		ctx, conn,
 		WGS84, tree.EPSG,
 	); err != nil {
 		return
 	}
 	defer rp.Close()
 
-	coords, err := reproject(rp, csi.Geometry.Coordinates, req.Context())
+	coords, err := reproject(ctx, rp, csi.Geometry.Coordinates)
 
 	log.Printf("transforming input coords took: %s\n", time.Since(start))
 	if err != nil {
@@ -149,8 +151,9 @@
 
 	var joined models.GeoJSONMultiLineCoordinatesZ
 	joined, err = projectBack(
+		ctx,
 		segments, tree.EPSG,
-		conn, req.Context(),
+		conn,
 	)
 
 	log.Printf("projecting back took: %s\n", time.Since(start))
--- a/pkg/controllers/importqueue.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/importqueue.go	Mon Nov 26 09:01:21 2018 +0100
@@ -14,7 +14,6 @@
 package controllers
 
 import (
-	"context"
 	"database/sql"
 	"fmt"
 	"log"
@@ -91,8 +90,7 @@
 
 func queryImportListStmt(
 	conn *sql.Conn,
-	ctx context.Context,
-	vars map[string]string,
+	req *http.Request,
 ) (*sql.Rows, error) {
 
 	var (
@@ -102,11 +100,16 @@
 		kinds  *pgtype.TextArray
 	)
 
-	if st, found := vars["states"]; found {
+	arg := func(format string, v interface{}) {
+		fmt.Fprintf(&stmt, format, len(args)+1)
+		args = append(args, v)
+	}
+
+	if st := req.FormValue("states"); st != "" {
 		states = toTextArray(st, imports.ImportStateNames)
 	}
 
-	if ks, found := vars["kinds"]; found {
+	if ks := req.FormValue("kinds"); ks != "" {
 		kinds = toTextArray(ks, imports.ImportKindNames)
 	}
 
@@ -116,8 +119,7 @@
 	}
 
 	if states != nil {
-		fmt.Fprintf(&stmt, " states = ANY($%d) ", len(args)+1)
-		args = append(args, states)
+		arg(" state = ANY($%d) ", states)
 	}
 
 	if states != nil && kinds != nil {
@@ -125,25 +127,28 @@
 	}
 
 	if kinds != nil {
-		fmt.Fprintf(&stmt, " kind = ANY($%d) ", len(args)+1)
-		args = append(args, kinds)
+		arg(" kind = ANY($%d) ", kinds)
 	}
 
 	stmt.WriteString(" ORDER BY enqueued DESC ")
 
-	if lim, found := vars["limit"]; found {
-		fmt.Fprintf(&stmt, " LIMIT $%d ", len(args)+1)
-		limit, _ := strconv.ParseInt(lim, 10, 64)
-		args = append(args, limit)
+	if lim := req.FormValue("limit"); lim != "" {
+		limit, err := strconv.ParseInt(lim, 10, 64)
+		if err != nil {
+			return nil, err
+		}
+		arg(" LIMIT $%d ", limit)
 	}
 
-	if ofs, found := vars["offset"]; found {
-		fmt.Fprintf(&stmt, " OFFSET $%d ", len(args)+1)
-		offset, _ := strconv.ParseInt(ofs, 10, 64)
-		args = append(args, offset)
+	if ofs := req.FormValue("offset"); ofs != "" {
+		offset, err := strconv.ParseInt(ofs, 10, 64)
+		if err != nil {
+			return nil, err
+		}
+		arg(" OFFSET $%d ", offset)
 	}
 
-	return conn.QueryContext(ctx, stmt.String(), args...)
+	return conn.QueryContext(req.Context(), stmt.String(), args...)
 }
 
 func listImports(
@@ -153,7 +158,7 @@
 ) (jr JSONResult, err error) {
 
 	var rows *sql.Rows
-	rows, err = queryImportListStmt(conn, req.Context(), mux.Vars(req))
+	rows, err = queryImportListStmt(conn, req)
 	if err != nil {
 		return
 	}
@@ -357,7 +362,7 @@
 
 	if state == "accepted" {
 		if jc := imports.FindJobCreator(imports.JobKind(kind)); jc != nil {
-			if err = jc.StageDone(tx, ctx, id); err != nil {
+			if err = jc.StageDone(ctx, tx, id); err != nil {
 				return
 			}
 		}
--- a/pkg/controllers/json.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/json.go	Mon Nov 26 09:01:21 2018 +0100
@@ -63,7 +63,7 @@
 
 	if token, ok := auth.GetToken(req); ok && !j.NoConn {
 		if session := auth.Sessions.Session(token); session != nil {
-			err = auth.RunAs(session.User, req.Context(), func(conn *sql.Conn) error {
+			err = auth.RunAs(req.Context(), session.User, func(conn *sql.Conn) error {
 				jr, err = j.Handle(input, req, conn)
 				return err
 			})
--- a/pkg/controllers/proxy.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/proxy.go	Mon Nov 26 09:01:21 2018 +0100
@@ -203,6 +203,7 @@
 	"text/xml",
 	"application/gml+xml",
 	"application/vnd.ogc.wms_xml",
+	"application/vnd.ogc.se_xml",
 }
 
 func isXML(h http.Header) bool {
--- a/pkg/controllers/pwreset.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/pwreset.go	Mon Nov 26 09:01:21 2018 +0100
@@ -11,6 +11,7 @@
 // Author(s):
 //  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
 //  * Bernhard E. Reiter <bernhard.reiter@intevation.de>
+//  * Tom Gottfried <tom.gottfried.intevation.de>
 
 package controllers
 
@@ -121,12 +122,13 @@
 	config.WaitReady()
 	for {
 		time.Sleep(cleanupPause)
+		ctx := context.Background()
 		err := auth.RunAs(
-			pwResetRole, context.Background(),
+			ctx, pwResetRole,
 			func(conn *sql.Conn) error {
 				good := time.Now().Add(-passwordResetValid)
 				_, err := conn.ExecContext(
-					context.Background(), cleanupRequestsSQL, good)
+					ctx, cleanupRequestsSQL, good)
 				return err
 			})
 		if err != nil {
@@ -207,7 +209,7 @@
 	ctx := context.Background()
 
 	if err := auth.RunAs(
-		pwResetRole, ctx,
+		ctx, pwResetRole,
 		func(conn *sql.Conn) error {
 
 			var count int64
@@ -303,7 +305,7 @@
 	ctx := req.Context()
 
 	if err = auth.RunAs(
-		pwResetRole, ctx, func(conn *sql.Conn) error {
+		ctx, pwResetRole, func(conn *sql.Conn) error {
 			err := conn.QueryRowContext(ctx, findRequestSQL, hash).Scan(&email, &user)
 			switch {
 			case err == sql.ErrNoRows:
--- a/pkg/controllers/routes.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/routes.go	Mon Nov 26 09:01:21 2018 +0100
@@ -176,14 +176,7 @@
 	})
 
 	api.Handle("/imports", lsImports).
-		Methods(http.MethodGet).
-		Queries(
-			"offset", "{offset:[0-9]+}",
-			"limit", "{limit:[0-9]+}",
-			"states", "",
-			"kinds", "")
-
-	api.Handle("/imports", lsImports).Methods(http.MethodGet)
+		Methods(http.MethodGet)
 
 	api.Handle("/imports/{id:[0-9]+}", waterwayAdmin(&JSONHandler{
 		Handle: importLogs,
--- a/pkg/controllers/srimports.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/srimports.go	Mon Nov 26 09:01:21 2018 +0100
@@ -120,7 +120,7 @@
 		if err != nil {
 			return err
 		}
-		sr.Date = &models.SoundingResultDate{date}
+		sr.Date = &models.SoundingResultDate{Time: date}
 	}
 
 	if v := req.FormValue("depth-reference"); v != "" {
@@ -229,7 +229,7 @@
 			messages = append(messages,
 				fmt.Sprintf("'meta.json' found but invalid: %v", err))
 		} else {
-			errs := meta.Validate(conn, req.Context())
+			errs := meta.Validate(req.Context(), conn)
 			for _, err := range errs {
 				messages = append(messages,
 					fmt.Sprintf("invalid 'meta.json': %v", err))
--- a/pkg/controllers/surveys.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/surveys.go	Mon Nov 26 09:01:21 2018 +0100
@@ -10,6 +10,7 @@
 //
 // Author(s):
 //  * Sascha Wilde <sascha.wilde@intevation.de>
+//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
 
 package controllers
 
@@ -49,7 +50,7 @@
 	for rows.Next() {
 		var survey models.Survey
 		if err = rows.Scan(
-			&survey.BottleneckId,
+			&survey.BottleneckID,
 			&survey.DateInfo,
 		); err != nil {
 			return
--- a/pkg/controllers/user.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/controllers/user.go	Mon Nov 26 09:01:21 2018 +0100
@@ -11,6 +11,7 @@
 // Author(s):
 //  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
 //  * Tom Gottfried <tom.gottfried@intevation.de>
+//  * Sascha Wilde <sascha.wilde@intevation.de>
 
 package controllers
 
--- a/pkg/geoserver/boot.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/geoserver/boot.go	Mon Nov 26 09:01:21 2018 +0100
@@ -371,9 +371,8 @@
 	defer resp.Body.Close()
 
 	// Fetch all styles
-	if err := json.NewDecoder(resp.Body).Decode(s); err != nil {
-		// XXX: Same quirk as with featuretypes.
-	}
+	// XXX: Avoid error checking due to quirks with featuretypes.
+	json.NewDecoder(resp.Body).Decode(s)
 	return nil
 }
 
--- a/pkg/imports/queue.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/imports/queue.go	Mon Nov 26 09:01:21 2018 +0100
@@ -36,7 +36,7 @@
 	}
 
 	Job interface {
-		Do(int64, context.Context, *sql.Conn, Feedback) error
+		Do(context.Context, int64, *sql.Conn, Feedback) error
 		CleanUp() error
 	}
 
@@ -45,7 +45,7 @@
 	JobCreator interface {
 		Create(kind JobKind, data string) (Job, error)
 		Depends() []string
-		StageDone(*sql.Tx, context.Context, int64) error
+		StageDone(context.Context, *sql.Tx, int64) error
 	}
 
 	idJob struct {
@@ -160,7 +160,7 @@
 func (q *importQueue) addJob(kind JobKind, user, data string) (int64, error) {
 	ctx := context.Background()
 	var id int64
-	err := auth.RunAs(queueUser, ctx, func(conn *sql.Conn) error {
+	err := auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error {
 		return conn.QueryRowContext(ctx, insertJobSQL, string(kind), user, data).Scan(&id)
 	})
 	if err == nil {
@@ -180,7 +180,7 @@
 
 func (lf logFeedback) log(kind, format string, args ...interface{}) {
 	ctx := context.Background()
-	err := auth.RunAs(queueUser, ctx, func(conn *sql.Conn) error {
+	err := auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error {
 		_, err := conn.ExecContext(
 			ctx, logMessageSQL, int64(lf), kind, fmt.Sprintf(format, args...))
 		return err
@@ -215,7 +215,7 @@
 
 func reEnqueueRunning() error {
 	ctx := context.Background()
-	return auth.RunAs(queueUser, ctx, func(conn *sql.Conn) error {
+	return auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error {
 		_, err := conn.ExecContext(ctx, reEnqueueRunningSQL)
 		return err
 	})
@@ -248,7 +248,7 @@
 
 	var ji idJob
 	ctx := context.Background()
-	err := auth.RunAs(queueUser, ctx, func(conn *sql.Conn) error {
+	err := auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error {
 		tx, err := conn.BeginTx(ctx, nil)
 		if err != nil {
 			return err
@@ -275,7 +275,7 @@
 
 func updateState(id int64, state string) error {
 	ctx := context.Background()
-	return auth.RunAs(queueUser, ctx, func(conn *sql.Conn) error {
+	return auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error {
 		_, err := conn.ExecContext(ctx, updateStateSQL, state, id)
 		return err
 	})
@@ -283,7 +283,7 @@
 
 func errorAndFail(id int64, format string, args ...interface{}) error {
 	ctx := context.Background()
-	err := auth.RunAs(queueUser, ctx, func(conn *sql.Conn) error {
+	err := auth.RunAs(ctx, queueUser, func(conn *sql.Conn) error {
 		tx, err := conn.BeginTx(ctx, nil)
 		if err != nil {
 			return err
@@ -372,9 +372,9 @@
 
 			errDo := survive(func() error {
 				ctx := context.Background()
-				return auth.RunAs(idj.user, ctx,
+				return auth.RunAs(ctx, idj.user,
 					func(conn *sql.Conn) error {
-						return job.Do(idj.id, ctx, conn, feedback)
+						return job.Do(ctx, idj.id, conn, feedback)
 					})
 			})()
 			if errDo != nil {
--- a/pkg/imports/sr.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/imports/sr.go	Mon Nov 26 09:01:21 2018 +0100
@@ -80,8 +80,8 @@
 }
 
 func (srJobCreator) StageDone(
+	ctx context.Context,
 	tx *sql.Tx,
-	ctx context.Context,
 	id int64,
 ) error {
 	_, err := tx.ExecContext(ctx, srStageDoneSQL, id)
@@ -165,8 +165,8 @@
 }
 
 func (sr *SoundingResult) Do(
+	ctx context.Context,
 	importID int64,
-	ctx context.Context,
 	conn *sql.Conn,
 	feedback Feedback,
 ) error {
@@ -188,7 +188,7 @@
 		return err
 	}
 
-	if err := m.Validate(conn, ctx); err != nil {
+	if err := m.Validate(ctx, conn); err != nil {
 		return common.ToError(err)
 	}
 
@@ -204,7 +204,7 @@
 	}
 
 	if len(xyz) == 0 {
-		return errors.New("XYZ does not contain any vertices.")
+		return errors.New("XYZ does not contain any vertices")
 	}
 
 	// Is there a boundary shapefile in the ZIP archive?
@@ -241,7 +241,7 @@
 
 	feedback.Info("Triangulate...")
 	start = time.Now()
-	tin, err := octree.GenerateTinByID(conn, ctx, id, epsg)
+	tin, err := octree.GenerateTinByID(ctx, conn, id, epsg)
 	feedback.Info("triangulation took %s", time.Since(start))
 	if err != nil {
 		return err
@@ -285,7 +285,7 @@
 	}
 
 	// Store for potential later removal.
-	if err = track(tx, ctx, importID, "waterway.sounding_results", id); err != nil {
+	if err = track(ctx, tx, importID, "waterway.sounding_results", id); err != nil {
 		return err
 	}
 
--- a/pkg/imports/track.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/imports/track.go	Mon Nov 26 09:01:21 2018 +0100
@@ -24,7 +24,7 @@
 	VALUES ($1, $2::regclass, $3)`
 )
 
-func track(tx *sql.Tx, ctx context.Context, importID int64, relation string, key int64) error {
+func track(ctx context.Context, tx *sql.Tx, importID int64, relation string, key int64) error {
 	_, err := tx.ExecContext(ctx, trackImportSQL, importID, relation, key)
 	return err
 }
--- a/pkg/models/cross.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/models/cross.go	Mon Nov 26 09:01:21 2018 +0100
@@ -246,14 +246,6 @@
 		math.Abs(cz.Z-other.Z) < zEps
 }
 
-func (cz GeoJSONCoordinateZ) mid(other GeoJSONCoordinateZ) GeoJSONCoordinateZ {
-	return GeoJSONCoordinateZ{
-		Lon: (cz.Lon + other.Lon) * 0.5,
-		Lat: (cz.Lat + other.Lat) * 0.5,
-		Z:   (cz.Z + other.Z) * 0.5,
-	}
-}
-
 func deg2rad(d float64) float64 { return d * math.Pi / 180.0 }
 
 func (cz GeoJSONCoordinateZ) Distance(other GeoJSONCoordinateZ) float64 {
--- a/pkg/models/extservices.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/models/extservices.go	Mon Nov 26 09:01:21 2018 +0100
@@ -61,7 +61,7 @@
 func (es *ExtServices) load() error {
 	// make empty slice to prevent retry if slice is empty.
 	es.entries = []ExtEntry{}
-	return auth.RunAs("sys_admin", context.Background(),
+	return auth.RunAs(context.Background(), "sys_admin",
 		func(conn *sql.Conn) error {
 			rows, err := conn.QueryContext(
 				context.Background(), selectExternalServices)
--- a/pkg/models/intservices.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/models/intservices.go	Mon Nov 26 09:01:21 2018 +0100
@@ -59,10 +59,11 @@
 
 func (e *IntEntry) LoadStyle() (string, error) {
 	var style string
-	err := auth.RunAs("sys_admin", context.Background(),
+	ctx := context.Background()
+	err := auth.RunAs(ctx, "sys_admin",
 		func(conn *sql.Conn) error {
 			return conn.QueryRowContext(
-				context.Background(),
+				ctx,
 				selectStyleSQL,
 				e.Name).Scan(&style)
 		})
@@ -119,10 +120,11 @@
 func (ps *IntServices) load() error {
 	// make empty slice to prevent retry if slice is empty.
 	ps.entries = []IntEntry{}
-	return auth.RunAs("sys_admin", context.Background(),
+	ctx := context.Background()
+	return auth.RunAs(ctx, "sys_admin",
 		func(conn *sql.Conn) error {
 			rows, err := conn.QueryContext(
-				context.Background(), selectServicesSQL)
+				ctx, selectServicesSQL)
 			if err != nil {
 				return err
 			}
--- a/pkg/models/reproject.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/models/reproject.go	Mon Nov 26 09:01:21 2018 +0100
@@ -29,8 +29,8 @@
 }
 
 func NewReprojector(
+	ctx context.Context,
 	conn *sql.Conn,
-	ctx context.Context,
 	fromEPSG, toEPSG uint32,
 ) (*Reprojector, error) {
 	stmt, err := conn.PrepareContext(ctx, reprojectSQL)
@@ -53,8 +53,8 @@
 }
 
 func (r *Reprojector) Reproject(
+	ctx context.Context,
 	x, y float64,
-	ctx context.Context,
 ) (v, w float64, err error) {
 	err = r.stmt.QueryRowContext(ctx, x, y, r.FromEPSG, r.ToEPSG).Scan(&v, &w)
 	return
--- a/pkg/models/sr.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/models/sr.go	Mon Nov 26 09:01:21 2018 +0100
@@ -73,7 +73,7 @@
 	return err
 }
 
-func (m *SoundingResultMeta) Validate(conn *sql.Conn, ctx context.Context) []error {
+func (m *SoundingResultMeta) Validate(ctx context.Context, conn *sql.Conn) []error {
 
 	var errs []error
 
@@ -83,11 +83,11 @@
 		m.DepthReference).Scan(&b)
 	switch {
 	case err == sql.ErrNoRows:
-		errs = append(errs, fmt.Errorf("Unknown depth reference '%s'\n", m.DepthReference))
+		errs = append(errs, fmt.Errorf("unknown depth reference '%s'", m.DepthReference))
 	case err != nil:
 		errs = append(errs, err)
 	case !b:
-		errs = append(errs, errors.New("Unexpected depth reference"))
+		errs = append(errs, errors.New("unexpected depth reference"))
 	}
 
 	err = conn.QueryRowContext(ctx,
@@ -95,11 +95,11 @@
 		m.Bottleneck).Scan(&b)
 	switch {
 	case err == sql.ErrNoRows:
-		errs = append(errs, fmt.Errorf("Unknown bottleneck '%s'\n", m.Bottleneck))
+		errs = append(errs, fmt.Errorf("unknown bottleneck '%s'", m.Bottleneck))
 	case err != nil:
 		errs = append(errs, err)
 	case !b:
-		errs = append(errs, errors.New("Unexpected bottleneck"))
+		errs = append(errs, errors.New("unexpected bottleneck"))
 	}
 
 	err = conn.QueryRowContext(ctx,
@@ -111,7 +111,7 @@
 		errs = append(errs, err)
 	case b:
 		errs = append(errs,
-			errors.New("Sounding result for this date already exists."))
+			errors.New("sounding result for this date already exists"))
 	}
 
 	return errs
--- a/pkg/models/surveys.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/models/surveys.go	Mon Nov 26 09:01:21 2018 +0100
@@ -15,7 +15,7 @@
 
 type (
 	Survey struct {
-		BottleneckId string `json:"bottleneck_id"`
+		BottleneckID string `json:"bottleneck_id"`
 		DateInfo     string `json:"date_info"`
 	}
 )
--- a/pkg/octree/builder.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/octree/builder.go	Mon Nov 26 09:01:21 2018 +0100
@@ -173,7 +173,7 @@
 	return nil
 }
 
-func (tb *Builder) WriteTo(w io.Writer) error {
+func (tb *Builder) writeTo(w io.Writer) error {
 	out := snappy.NewBufferedWriter(w)
 	if err := tb.t.Serialize(out); err != nil {
 		return err
@@ -186,7 +186,7 @@
 
 func (tb *Builder) Bytes() ([]byte, error) {
 	var buf bytes.Buffer
-	if err := tb.WriteTo(&buf); err != nil {
+	if err := tb.writeTo(&buf); err != nil {
 		return nil, err
 	}
 	return buf.Bytes(), nil
--- a/pkg/octree/cache.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/octree/cache.go	Mon Nov 26 09:01:21 2018 +0100
@@ -31,7 +31,7 @@
 		tree     *Tree
 		access   time.Time
 	}
-	OctreeCache struct {
+	Cache struct {
 		sync.Mutex
 		entries map[cacheKey]*cacheEntry
 	}
@@ -61,41 +61,50 @@
 `
 )
 
-var Cache = OctreeCache{
+var cache = Cache{
 	entries: map[cacheKey]*cacheEntry{},
 }
 
 func init() {
-	go Cache.background()
+	go cache.background()
 }
 
-func (oc *OctreeCache) background() {
+func (c *Cache) background() {
 	for {
 		time.Sleep(cleanupCacheSleep)
-		oc.cleanup()
+		c.cleanup()
 	}
 }
 
-func (oc *OctreeCache) cleanup() {
-	oc.Lock()
-	defer oc.Unlock()
+func (c *Cache) cleanup() {
+	c.Lock()
+	defer c.Unlock()
 	good := time.Now().Add(-maxCacheAge)
-	for k, v := range oc.entries {
+	for k, v := range c.entries {
 		if v.access.Before(good) {
-			delete(oc.entries, k)
+			delete(c.entries, k)
 		}
 	}
 }
 
-func (oc *OctreeCache) Get(
+func FromCache(
+	ctx context.Context,
+	conn *sql.Conn,
 	bottleneck string, date time.Time,
-	conn *sql.Conn, ctx context.Context,
 ) (*Tree, error) {
-	oc.Lock()
-	defer oc.Unlock()
+	return cache.get(ctx, conn, bottleneck, date)
+}
+
+func (c *Cache) get(
+	ctx context.Context,
+	conn *sql.Conn,
+	bottleneck string, date time.Time,
+) (*Tree, error) {
+	c.Lock()
+	defer c.Unlock()
 
 	key := cacheKey{date, bottleneck}
-	entry := oc.entries[key]
+	entry := c.entries[key]
 
 	var data []byte
 	var checksum string
@@ -139,21 +148,21 @@
 		return tree, nil
 	}
 
-	for len(oc.entries) >= maxCacheEntries {
+	for len(c.entries) >= maxCacheEntries {
 		// Evict the entry that is accessed the longest time ago.
 		var oldestKey cacheKey
 		oldest := now
 
-		for k, v := range oc.entries {
+		for k, v := range c.entries {
 			if v.access.Before(oldest) {
 				oldest = v.access
 				oldestKey = k
 			}
 		}
-		delete(oc.entries, oldestKey)
+		delete(c.entries, oldestKey)
 	}
 
-	oc.entries[key] = &cacheEntry{
+	c.entries[key] = &cacheEntry{
 		checksum: checksum,
 		tree:     tree,
 		access:   now,
--- a/pkg/octree/contours.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/octree/contours.go	Mon Nov 26 09:01:21 2018 +0100
@@ -10,6 +10,7 @@
 //
 // Author(s):
 //  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
+//  * Tom Gottfried <tom.gottfried@intevation.de>
 
 package octree
 
--- a/pkg/octree/tin.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/octree/tin.go	Mon Nov 26 09:01:21 2018 +0100
@@ -23,7 +23,6 @@
 	"io"
 	"log"
 	"math"
-	"time"
 )
 
 var (
@@ -199,32 +198,12 @@
 SELECT ST_AsBinary(ST_Collect(triangles.geom)) FROM triangles, trans
 WHERE ST_Covers(trans.area, triangles.poly)`
 
-	loadTinSQL     = tinSQLPrefix + `WHERE bottleneck_id = $2 AND date_info = $3` + tinSQLSuffix
 	loadTinByIDSQL = tinSQLPrefix + `WHERE id = $2` + tinSQLSuffix
 )
 
-func GenerateTin(
-	conn *sql.Conn,
+func GenerateTinByID(
 	ctx context.Context,
-	bottleneck string,
-	date time.Time,
-	epsg uint32,
-) (*Tin, error) {
-	var tin Tin
-	err := conn.QueryRowContext(ctx, loadTinSQL, epsg, bottleneck, date).Scan(&tin)
-	switch {
-	case err == sql.ErrNoRows:
-		return nil, nil
-	case err != nil:
-		return nil, err
-	}
-	tin.EPSG = epsg
-	return &tin, nil
-}
-
-func GenerateTinByID(
 	conn *sql.Conn,
-	ctx context.Context,
 	id int64,
 	epsg uint32,
 ) (*Tin, error) {
--- a/pkg/octree/vertex.go	Fri Nov 23 15:46:07 2018 +0100
+++ b/pkg/octree/vertex.go	Mon Nov 26 09:01:21 2018 +0100
@@ -110,8 +110,8 @@
 	}
 }
 
-func (a Vertex) Less(b Vertex) bool {
-	return a.X < b.X || a.Y < b.Y || a.Z < b.Z
+func (v Vertex) Less(w Vertex) bool {
+	return v.X < w.X || v.Y < w.Y || v.Z < w.Z
 }
 
 func NewLine(p1, p2 Vertex) Line {
@@ -537,26 +537,26 @@
 	return false
 }
 
-func (a Vertex) Cross(b Vertex) Vertex {
+func (v Vertex) Cross(w Vertex) Vertex {
 	return Vertex{
-		a.Y*b.Z - a.Z*b.Y,
-		a.Z*b.X - a.X*b.Z,
-		a.X*b.Y - a.Y*b.X,
+		v.Y*w.Z - v.Z*w.Y,
+		v.Z*w.X - v.X*w.Z,
+		v.X*w.Y - v.Y*w.X,
 	}
 }
 
-func (p1 Plane2D) Intersection(p2 Plane2D) (float64, float64, bool) {
+func (p Plane2D) Intersection(o Plane2D) (float64, float64, bool) {
 
-	u1 := Vertex{p1.A, p1.B, p1.C}
-	u2 := Vertex{p2.A, p2.B, p2.C}
+	u1 := Vertex{p.A, p.B, p.C}
+	u2 := Vertex{o.A, o.B, o.C}
 
-	p := u1.Cross(u2)
+	plane := u1.Cross(u2)
 
-	if p.Z == 0 {
+	if plane.Z == 0 {
 		return 0, 0, false
 	}
 
-	return p.X / p.Z, p.Y / p.Z, true
+	return plane.X / plane.Z, plane.Y / plane.Z, true
 }
 
 type VerticalLine struct {
--- a/schema/auth.sql	Fri Nov 23 15:46:07 2018 +0100
+++ b/schema/auth.sql	Mon Nov 26 09:01:21 2018 +0100
@@ -10,6 +10,8 @@
 
 -- Author(s):
 --  * Tom Gottried <tom@intevation.de>
+--  * Sascha Wilde <sascha.wilde@intevation.de>
+--  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
 
 BEGIN;
 
--- a/schema/demo-data/published_services.sql	Fri Nov 23 15:46:07 2018 +0100
+++ b/schema/demo-data/published_services.sql	Mon Nov 26 09:01:21 2018 +0100
@@ -1,3 +1,16 @@
+-- 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>
+
 INSERT INTO sys_admin.published_services (name) VALUES
     ('waterway.fairway_dimensions'),
     ('waterway.distance_marks_geoserver'),
--- a/schema/demo-data/responsibility_areas.sql	Fri Nov 23 15:46:07 2018 +0100
+++ b/schema/demo-data/responsibility_areas.sql	Mon Nov 26 09:01:21 2018 +0100
@@ -1,3 +1,18 @@
+-- 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 Wilde <sascha.wilde@intevation.de>
+--  * Tom Gottfried <tom@intevation.de>
+
+
 -- Setup Country Codes
 COPY countries (country_code) FROM stdin;
 RO
--- a/schema/demo-data/roles.sql	Fri Nov 23 15:46:07 2018 +0100
+++ b/schema/demo-data/roles.sql	Mon Nov 26 09:01:21 2018 +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):
+--  * Christine Tschuprine <christine.tschuprine@intevation.de>
+--  * Tom Gottfried <tom@intevation.de>
+
 -- System Administrator
 CREATE ROLE sophie
     IN ROLE sys_admin ROLE metamorph LOGIN PASSWORD 'so2Phie4';
--- a/schema/demo-data/users.sql	Fri Nov 23 15:46:07 2018 +0100
+++ b/schema/demo-data/users.sql	Mon Nov 26 09:01:21 2018 +0100
@@ -1,3 +1,19 @@
+-- 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 Wilde <sascha.wilde@intevation.de>
+--  * Christine Tschuprine <christine.tschuprine@intevation.de>
+--  * Thomas Junk <thomas.junk@intevation.de>
+--  * Tom Gottfried <tom@intevation.de>
+
 BEGIN;
 
 -- PREREQUISITES: