changeset 3541:d26a0ce526cb import_review

merge with default
author Thomas Junk <thomas.junk@intevation.de>
date Fri, 31 May 2019 09:35:09 +0200
parents c71f1b15c5d9 (current diff) b268cae2df39 (diff)
children 603055f52834
files
diffstat 17 files changed, 208 insertions(+), 149 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/fairway/Fairwayprofile.vue	Wed May 29 15:42:32 2019 +0200
+++ b/client/src/components/fairway/Fairwayprofile.vue	Fri May 31 09:35:09 2019 +0200
@@ -17,9 +17,15 @@
         </div>
         <div class="legend">
           <span
-            style="background-color: #4a2f06; width: 20px; height: 20px;"
+            style="width: 14px; height: 14px; background-color: #4a2f06; border: solid 3px black; background-clip: padding-box; box-sizing: content-box;"
           ></span>
-          Ground
+          Sediment
+        </div>
+        <div class="legend">
+          <span
+            style="width: 14px; height: 14px; background-color: rgba(74, 47, 6, 0.6); border: solid 3px #943007; background-clip: padding-box; box-sizing: content-box;"
+          ></span>
+          Sediment (Compare)
         </div>
         <div>
           <select
@@ -96,9 +102,9 @@
       height: null,
       margin: {
         top: 20,
-        right: 40,
-        bottom: 30,
-        left: 40
+        right: 80,
+        bottom: 60,
+        left: 80
       },
       form: {
         template: null
@@ -141,6 +147,7 @@
     };
   },
   computed: {
+    ...mapGetters("map", ["openLayersMap"]),
     ...mapGetters("fairwayprofile", ["totalLength"]),
     ...mapState("fairwayprofile", [
       "additionalSurvey",
@@ -149,32 +156,21 @@
       "endPoint",
       "fairwayData",
       "maxAlt",
-      "referenceWaterLevel",
-      "selectedWaterLevel",
-      "waterLevels"
+      "selectedWaterLevel"
     ]),
+    ...mapState("bottlenecks", ["selectedSurvey", "selectedBottleneck"]),
     ...mapState("application", ["paneSetup"]),
     title() {
       let dates = [this.selectedSurvey.date_info];
+      let waterlevelLabel =
+        this.selectedWaterLevel === "ref"
+          ? this.selectedSurvey.depth_reference
+          : "Current";
       if (this.additionalSurvey) dates.push(this.additionalSurvey.date_info);
       dates.map(d => this.$options.filters.dateTime(d, true));
       return `${this.$gettext("Fairwayprofile")}: ${
         this.selectedBottleneck
-      } (${dates.join(", ")})`;
-    },
-    selectedSurvey: {
-      get() {
-        return this.$store.state.bottlenecks.selectedSurvey;
-      }
-    },
-    selectedBottleneck: {
-      get() {
-        return this.$store.state.bottlenecks.selectedBottleneck;
-      }
-    },
-    ...mapState("bottlenecks", ["selectedSurvey"]),
-    relativeWaterLevelDelta() {
-      return this.selectedWaterLevel.value - this.referenceWaterLevel;
+      } (${dates.join(", ")}) WL: ${waterlevelLabel} (${this.waterlevel} cm)`;
     },
     currentData() {
       if (
@@ -192,15 +188,20 @@
         return [];
       return this.currentProfile[this.additionalSurvey.date_info].points;
     },
-    xScale() {
-      return [0, this.totalLength];
+    bottleneck() {
+      return this.openLayersMap()
+        .getLayer("BOTTLENECKS")
+        .getSource()
+        .getFeatures()
+        .find(f => f.get("objnam") === this.selectedBottleneck);
     },
-    yScaleRight() {
-      //ToDO calcReleativeDepth(this.maxAlt) to get the
-      // maximum depth according to the actual waterlevel
-      // additionally: take the one which is higher reference or current waterlevel
-      const DELTA = this.maxAlt * 1.1 - this.maxAlt;
-      return [this.maxAlt * 1 + DELTA, -DELTA];
+    waterlevel() {
+      return this.selectedWaterLevel === "ref"
+        ? this.refWaterlevel
+        : this.bottleneck.get("gm_waterlevel");
+    },
+    refWaterlevel() {
+      return this.selectedSurvey.waterlevel_value;
     }
   },
   watch: {
@@ -371,7 +372,7 @@
           }
         });
       }
-      this.pdf.doc.save("Fairwayprofile diagram");
+      this.pdf.doc.save(this.title.replace(/\s/g, "_").replace(/[():,]/g, ""));
     },
     addDiagram(position, offset, width, height) {
       let x = offset.x,
@@ -429,36 +430,17 @@
       this.pdf.doc.circle(x, y + 10, 2, "FD");
       this.pdf.doc.text(x + 3, y + 11, "Ground");
     },
-    calcRelativeDepth(depth) {
-      /* takes a depth value and substracts the delta of the relative waterlevel
-       * say the reference level is above the current level, the ground is nearer,
-       * thus, the depth is lower.
-       *
-       * E.g.:
-       *
-       * Reference waterlevel 5m, current 4m => delta = -1m
-       * If the distance to the ground was 3m from the 5m mark
-       * it is now only 2m from the current waterlevel.
-       *
-       *  Vice versa:
-       *
-       *  If the reference level is 5m and the current 6m => delta = +1m
-       *  The ground is one meter farer away from the current waterlevel
-       *
-       */
-      return depth - this.relativeWaterLevelDelta;
-    },
     drawDiagram() {
       d3.select(".diagram-container svg").remove();
       this.scaleFairwayProfile();
       let svg = d3.select(".diagram-container").append("svg");
       svg.attr("width", "100%");
       svg.attr("height", "100%");
-      const width = this.width - this.margin.right - 1.5 * this.margin.left;
-      const height = this.height - this.margin.top - 2 * this.margin.bottom;
+      const width = this.width - this.margin.right - this.margin.left;
+      const height = this.height - this.margin.top - this.margin.bottom;
       const currentData = this.currentData;
       const additionalData = this.additionalData;
-      const { xScale, yScaleRight, graph } = this.generateCoordinates(
+      const { xScale, yScaleRight, graph } = this.generateScalesAndGraph(
         svg,
         height,
         width
@@ -498,7 +480,6 @@
       }
       for (let data of this.fairwayData) {
         const [startPoint, endPoint, depth] = data.coordinates[0];
-        const style = data.style();
         let fairwayArea = d3
           .area()
           .x(function(d) {
@@ -513,7 +494,7 @@
           .datum([{ x: startPoint, y: depth }, { x: endPoint, y: depth }])
           .attr("fill", "#002AFF")
           .attr("fill-opacity", 0.65)
-          .attr("stroke", style[0].getStroke().getColor())
+          .attr("stroke", "transparent")
           .attr("d", fairwayArea);
       }
     },
@@ -521,14 +502,21 @@
       graph
         .append("text")
         .attr("transform", ["rotate(-90)"])
-        .attr("y", this.width - 70)
+        .attr("y", this.width - 100)
         .attr("x", -(this.height - this.margin.top - this.margin.bottom) / 2)
-        .attr("dy", "1em")
         .attr("fill", "black")
         .style("text-anchor", "middle")
         .text("Depth [m]");
       graph
         .append("text")
+        .attr("transform", ["rotate(-90)"])
+        .attr("y", -50)
+        .attr("x", -(this.height - this.margin.top - this.margin.bottom) / 2)
+        .attr("fill", "black")
+        .style("text-anchor", "middle")
+        .text("Waterlevel [cm]");
+      graph
+        .append("text")
         .attr("y", -50)
         .attr("x", -(height / 4))
         .attr("dy", "1em")
@@ -540,21 +528,35 @@
         ])
         .text("Width [m]");
     },
-    generateCoordinates(svg, height, width) {
+    generateScalesAndGraph(svg, height, width) {
       let xScale = d3
         .scaleLinear()
-        .domain(this.xScale)
+        .domain([0, this.totalLength])
         .rangeRound([0, width]);
 
-      xScale.ticks(5);
-
       let yScaleRight = d3
         .scaleLinear()
-        .domain(this.yScaleRight)
+        .domain([
+          this.maxAlt * 1.1 +
+            Math.abs(this.waterlevel - this.refWaterlevel) / 100,
+          -(this.maxAlt * 0.1)
+        ])
         .rangeRound([height, 0]);
 
-      let xAxis = d3.axisBottom(xScale);
-      let yAxis2 = d3.axisRight(yScaleRight);
+      let yScaleLeft = d3
+        .scaleLinear()
+        .domain([
+          this.waterlevel -
+            (this.maxAlt * 100 +
+              Math.abs(this.waterlevel - this.refWaterlevel)),
+          this.waterlevel + this.maxAlt * 0.1 * 100
+        ])
+        .rangeRound([height, 0]);
+
+      let xAxis = d3.axisBottom(xScale).ticks(5);
+      let yAxisRight = d3.axisRight(yScaleRight);
+      let yAxisLeft = d3.axisLeft(yScaleLeft);
+
       let graph = svg
         .append("g")
         .attr(
@@ -564,7 +566,7 @@
       graph
         .append("g")
         .attr("transform", "translate(0," + height + ")")
-        .call(xAxis.ticks(5))
+        .call(xAxis)
         .selectAll(".tick text")
         .attr("fill", "black")
         .select(function() {
@@ -575,7 +577,7 @@
       graph
         .append("g")
         .attr("transform", "translate(" + width + ",0)")
-        .call(yAxis2)
+        .call(yAxisRight)
         .selectAll(".tick text")
         .attr("fill", "black")
         .select(function() {
@@ -583,6 +585,18 @@
         })
         .selectAll(".tick line")
         .attr("stroke", "black");
+      graph
+        .append("g")
+        .attr("transform", "translate(0 0)")
+        .call(yAxisLeft)
+        .selectAll(".tick text")
+        .attr("fill", "black")
+        .select(function() {
+          return this.parentNode;
+        })
+        .selectAll(".tick line")
+        .attr("stroke", "black");
+
       graph.selectAll(".domain").attr("stroke", "black");
       return { xScale, yScaleRight, graph };
     },
@@ -601,7 +615,7 @@
         .datum([{ x: 0, y: 0 }, { x: this.totalLength, y: 0 }])
         .attr("fill-opacity", 0.65)
         .attr("fill", WATER_COLOR)
-        .attr("stroke", WATER_COLOR)
+        .attr("stroke", "transparent")
         .attr("d", waterArea);
     },
     drawProfile({
@@ -620,25 +634,27 @@
           .x(d => {
             return xScale(d.x);
           })
-          .y(d => {
-            return yScaleRight(d.y);
-          });
+          .y(d =>
+            yScaleRight(
+              d.y + Math.abs(this.waterlevel - this.refWaterlevel) / 100
+            )
+          );
         let profileArea = d3
           .area()
           .x(function(d) {
             return xScale(d.x);
           })
           .y0(height)
-          .y1(function(d) {
-            return yScaleRight(d.y);
-          });
+          .y1(d =>
+            yScaleRight(
+              d.y + Math.abs(this.waterlevel - this.refWaterlevel) / 100
+            )
+          );
         graph
           .append("path")
           .datum(part)
           .attr("fill", color)
-          .attr("stroke", color)
-          .attr("stroke-width", 3)
-          .attr("stroke-opacity", opacity)
+          .attr("stroke", "transparent")
           .attr("fill-opacity", opacity)
           .attr("d", profileArea);
         graph
--- a/client/src/components/fairway/Profiles.vue	Wed May 29 15:42:32 2019 +0200
+++ b/client/src/components/fairway/Profiles.vue	Fri May 31 09:35:09 2019 +0200
@@ -37,7 +37,7 @@
         </select>
         <div v-if="selectedBottleneck">
           <div class="d-flex mt-2">
-            <div class="flex-fill">
+            <div class="flex-fill" style="max-width: 75px;">
               <small class="text-muted">
                 <translate>Waterlevel</translate>:
               </small>
@@ -45,15 +45,11 @@
                 v-model="selectedWaterLevel"
                 class="form-control form-control-sm small"
               >
-                <option value="" v-if="Object.keys(waterLevels).length === 0">
-                  <translate>Current</translate>
+                <option value="ref">
+                  <translate>Depth Reference</translate>
                 </option>
-                <option
-                  v-for="wl in Object.keys(waterLevels)"
-                  :key="wl"
-                  :value="wl"
-                >
-                  {{ wl | surveyDate }}
+                <option value="current">
+                  <translate>Current Waterlevel</translate>
                 </option>
               </select>
             </div>
@@ -327,7 +323,7 @@
     },
     selectedWaterLevel: {
       get() {
-        return this.$store.state.fairwayprofile.selectedWaterLevel.date || "";
+        return this.$store.state.fairwayprofile.selectedWaterLevel;
       },
       set(value) {
         this.$store.commit("fairwayprofile/setSelectedWaterLevel", value);
--- a/client/src/components/importconfiguration/ScheduledImports.vue	Wed May 29 15:42:32 2019 +0200
+++ b/client/src/components/importconfiguration/ScheduledImports.vue	Fri May 31 09:35:09 2019 +0200
@@ -393,6 +393,7 @@
       </button>
       <div>
         <button
+          v-if="!currentSchedule.id"
           @click="triggerManualImport"
           type="button"
           class="btn btn-sm btn-outline-info"
@@ -568,6 +569,22 @@
           return false;
       }
     },
+    usernamePasswordFilled() {
+      if (
+        this.isCredentialsRequired &&
+        this.currentSchedule.id &&
+        this.username
+      )
+        return true;
+      if (
+        this.isCredentialsRequired &&
+        !this.currentSchedule.id &&
+        this.username &&
+        this.password
+      )
+        return true;
+      return false;
+    },
     isValid() {
       if (!this.import_) return false;
       if (this.isToleranceRequired && !this.tolerance) return false;
@@ -576,8 +593,7 @@
         if (this.isURLRequired && !this.url) return false;
         if (this.isSortbyRequired && !this.sortBy) return false;
         if (this.isFeatureTypeRequired && !this.featureType) return false;
-        if (this.isCredentialsRequired && (!this.username || !this.password))
-          return false;
+        if (!this.usernamePasswordFilled) return false;
         if (this.import_ == this.$options.IMPORTTYPES.FAIRWAYDIMENSION) {
           if (
             !this.LOS ||
@@ -856,12 +872,12 @@
         config["tolerance"] = parseFloat(this.tolerance);
       }
       if (this.isCredentialsRequired) {
-        if (!this.username || !this.password) return;
+        if (!this.usernamePasswordFilled) return;
         config = {
           ...config,
-          user: this.username,
-          password: this.password
+          user: this.username
         };
+        if (!this.currentSchedule.id) config["password"] = this.password;
       }
       if (this.import_ == this.$options.IMPORTTYPES.FAIRWAYDIMENSION) {
         if (
--- a/client/src/components/importconfiguration/types/Distancemarksvirtual.vue	Wed May 29 15:42:32 2019 +0200
+++ b/client/src/components/importconfiguration/types/Distancemarksvirtual.vue	Fri May 31 09:35:09 2019 +0200
@@ -59,7 +59,10 @@
             <font-awesome-icon :icon="passwordVisible ? 'eye-slash' : 'eye'" />
           </span>
         </div>
-        <div v-if="!password" class="d-flex flex-row">
+        <div
+          v-if="!password && !this.currentSchedule.id"
+          class="d-flex flex-row"
+        >
           <small
             ><translate class="text-danger"
               >Please enter a Password</translate
@@ -85,6 +88,8 @@
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
+
+import { mapState } from "vuex";
 export default {
   name: "distancemarksvirtual",
   props: ["url", "username", "password"],
@@ -94,6 +99,10 @@
     };
   },
   computed: {
+    ...mapState("importschedule", [
+      "importScheduleDetailVisible",
+      "currentSchedule"
+    ]),
     showPassword() {
       if (this.passwordVisible) return "text";
       return "password";
--- a/client/src/components/importconfiguration/types/Waterwaygauges.vue	Wed May 29 15:42:32 2019 +0200
+++ b/client/src/components/importconfiguration/types/Waterwaygauges.vue	Fri May 31 09:35:09 2019 +0200
@@ -59,7 +59,10 @@
             <font-awesome-icon :icon="passwordVisible ? 'eye-slash' : 'eye'" />
           </span>
         </div>
-        <div v-if="!password" class="d-flex flex-row">
+        <div
+          v-if="!password && !this.currentSchedule.id"
+          class="d-flex flex-row"
+        >
           <small
             ><translate class="text-danger"
               >Please enter a Password</translate
@@ -85,6 +88,9 @@
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
+
+import { mapState } from "vuex";
+
 export default {
   name: "waterwaygauges",
   props: ["username", "password", "url"],
@@ -94,6 +100,10 @@
     };
   },
   computed: {
+    ...mapState("importschedule", [
+      "importScheduleDetailVisible",
+      "currentSchedule"
+    ]),
     showPassword() {
       if (this.passwordVisible) return "text";
       return "password";
--- a/client/src/store/fairwayprofile.js	Wed May 29 15:42:32 2019 +0200
+++ b/client/src/store/fairwayprofile.js	Fri May 31 09:35:09 2019 +0200
@@ -28,9 +28,7 @@
     minAlt: 0,
     maxAlt: 0,
     currentProfile: {},
-    referenceWaterLevel: null,
-    waterLevels: {},
-    selectedWaterLevel: "",
+    selectedWaterLevel: "ref",
     fairwayData: [],
     startPoint: null,
     endPoint: null,
@@ -61,7 +59,7 @@
       state.additionalSurvey = additionalSurvey;
     },
     setSelectedWaterLevel: (state, level) => {
-      state.selectedWaterLevel = state.waterLevels[level];
+      state.selectedWaterLevel = level;
     },
     setDifferencesLoading: (state, value) => {
       state.differencesLoading = value;
@@ -69,8 +67,6 @@
     profileLoaded: (state, answer) => {
       const { response, surveyDate } = answer;
       const { data } = response;
-      const { waterlevel } = response.data.properties;
-      const { value, when } = waterlevel;
       const coordinates = data.geometry.coordinates;
       if (!coordinates) return;
       const startPoint = state.startPoint;
@@ -79,12 +75,6 @@
       const result = prepareProfile({ geoJSON, startPoint, endPoint });
       // Use Vue.set() to make new object properties rective
       // https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
-      const entry = {
-        date: when,
-        value: value
-      };
-      state.waterLevels = { [when]: entry };
-      state.selectedWaterLevel = entry;
       Vue.set(state.currentProfile, surveyDate, {
         points: result.points,
         length: result.lengthPolyLine
@@ -116,9 +106,7 @@
       state.fairwayData = [];
       state.startPoint = null;
       state.endPoint = null;
-      state.referenceWaterLevel = null;
-      state.waterLevels = {};
-      state.selectedWaterLevel = "";
+      state.selectedWaterLevel = "ref";
     },
     previousCuts: (state, previousCuts) => {
       state.previousCuts = previousCuts;
--- a/pkg/controllers/cross.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/controllers/cross.go	Fri May 31 09:35:09 2019 +0200
@@ -21,7 +21,6 @@
 	"net/http"
 	"time"
 
-	"gemma.intevation.de/gemma/pkg/common"
 	"gemma.intevation.de/gemma/pkg/models"
 	"gemma.intevation.de/gemma/pkg/octree"
 )
@@ -167,13 +166,6 @@
 				Type:        "MultiLineString",
 				Coordinates: joined,
 			},
-			Properties: map[string]interface{}{
-				"waterlevel": map[string]interface{}{
-					// TODO: Fetch values from database.
-					"value": float64(50),
-					"when":  start.Format(common.TimeFormat),
-				},
-			},
 		},
 	}
 
--- a/pkg/controllers/importconfig.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/controllers/importconfig.go	Fri May 31 09:35:09 2019 +0200
@@ -177,9 +177,19 @@
 		return
 	}
 
-	what := ctor()
+	// Remove `password` from the attributes to be delivered to the client.
+	// Even a priviledged user shall not be able to see the password.
+	// (See config.ListAllPersistentConfigurationsContext() for the other
+	//  place where this is done.)
+	filteredAttributes := make(common.Attributes)
+	for key, value := range cfg.Attributes {
+		if key != "password" {
+			filteredAttributes[key] = value
+		}
+	}
 
-	if err = cfg.Attributes.Unmarshal(what); err != nil {
+	what := ctor()
+	if err = filteredAttributes.Unmarshal(what); err != nil {
 		return
 	}
 
--- a/pkg/controllers/stretches.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/controllers/stretches.go	Fri May 31 09:35:09 2019 +0200
@@ -418,7 +418,7 @@
 		return
 	}
 
-	from, ok := parseFormTime(rw, req, "form", time.Now().AddDate(-1, 0, 0))
+	from, ok := parseFormTime(rw, req, "from", time.Now().AddDate(-1, 0, 0))
 	if !ok {
 		return
 	}
--- a/pkg/imports/agm.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/agm.go	Fri May 31 09:35:09 2019 +0200
@@ -182,10 +182,12 @@
   staging_done
 ) VALUES (
   ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int),
-  (SELECT validity FROM waterway.gauges
-     WHERE location
-          = ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int)
-       AND validity @> CAST($6 AS timestamp with time zone)),
+  COALESCE(
+    (SELECT validity FROM waterway.gauges
+       WHERE location
+            = ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int)
+         AND validity @> CAST($6 AS timestamp with time zone)),
+    tstzrange(NULL, NULL)),
   $6,
   $7,
   $8,
--- a/pkg/imports/bn.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/bn.go	Fri May 31 09:35:09 2019 +0200
@@ -20,6 +20,7 @@
 	"errors"
 	"regexp"
 	"strconv"
+	"strings"
 	"time"
 
 	"gemma.intevation.de/gemma/pkg/soap/ifbn"
@@ -63,8 +64,10 @@
 ) VALUES (
   $1,
   isrs_fromText($2),
-  (SELECT validity FROM waterway.gauges
-     WHERE location = isrs_fromText($2) AND NOT erased),
+  COALESCE(
+    (SELECT validity FROM waterway.gauges
+       WHERE location = isrs_fromText($2) AND NOT erased),
+    tstzrange(NULL, NULL)),
   $3,
   $4,
   isrsrange(least(isrs_fromText($5), isrs_fromText($6)),
@@ -268,10 +271,12 @@
 	rb, lb := splitRBLB(bn.Rb_lb)
 
 	var revisitingTime *int
-	if bn.Revisiting_time != nil {
+	if bn.Revisiting_time != nil &&
+		len(strings.TrimSpace(*bn.Revisiting_time)) > 0 {
 		i, err := strconv.Atoi(*bn.Revisiting_time)
 		if err != nil {
-			feedback.Warn("Cannot convert '%s' to number of months",
+			feedback.Warn(
+				"Cannot convert given revisiting time '%s' to number of months",
 				*bn.Revisiting_time)
 		} else {
 			revisitingTime = &i
--- a/pkg/imports/config.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/config.go	Fri May 31 09:35:09 2019 +0200
@@ -269,7 +269,12 @@
 			if pc.Attributes == nil {
 				pc.Attributes = common.Attributes{}
 			}
-			pc.Attributes.Set(k.String, v.String)
+			// Prevent sending the `password` back to the client.
+			// (See importconfig.infoImportConfig() for the other place
+			//  where this is done.)
+			if k.String != "password" {
+				pc.Attributes.Set(k.String, v.String)
+			}
 		}
 	}
 
--- a/pkg/imports/errors.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/errors.go	Fri May 31 09:35:09 2019 +0200
@@ -57,10 +57,10 @@
 		switch err.SchemaName {
 		case "waterway":
 			switch err.TableName {
-			case "gauge_measurements":
+			case "gauge_measurements", "gauge_predictions", "bottlenecks":
 				switch err.ConstraintName {
 				case "gauge_key":
-					return "Referenced gauge is not in database"
+					return "Referenced gauge with matching temporal validity not available"
 				}
 			}
 		}
--- a/pkg/imports/gm.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/gm.go	Fri May 31 09:35:09 2019 +0200
@@ -73,10 +73,12 @@
   staging_done
 ) VALUES (
   ($1, $2, $3, $4, $5),
-  (SELECT validity FROM waterway.gauges
-     WHERE location
-          = ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int)
-       AND validity @> CAST($6 AS timestamp with time zone)),
+  COALESCE(
+    (SELECT validity FROM waterway.gauges
+       WHERE location
+            = ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int)
+         AND validity @> CAST($6 AS timestamp with time zone)),
+    tstzrange(NULL, NULL)),
   $6,
   $7,
   $8,
@@ -108,10 +110,12 @@
   source_organization
 ) VALUES(
   ($1, $2, $3, $4, $5),
-  (SELECT validity FROM waterway.gauges
-     WHERE location
-          = ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int)
-       AND validity @> CAST($6 AS timestamp with time zone)),
+  COALESCE(
+    (SELECT validity FROM waterway.gauges
+       WHERE location
+            = ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int)
+         AND validity @> CAST($6 AS timestamp with time zone)),
+    tstzrange(NULL, NULL)),
   $6,
   $7,
   $8,
--- a/pkg/imports/sr.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/sr.go	Fri May 31 09:35:09 2019 +0200
@@ -164,7 +164,8 @@
 
 	selectGaugeLDCSQL = `
 SELECT
-  grwl.value
+  grwl.value,
+  grwl.depth_reference
 FROM waterway.gauges_reference_water_levels grwl
   JOIN waterway.bottlenecks bns
     ON grwl.location = bns.gauge_location
@@ -208,7 +209,11 @@
 	if m.DepthReference == "ZPG" {
 		feedback.Info("Found ZPG as reference system -> translating Z values to LDC")
 		var ldc float64
-		err := conn.QueryRowContext(ctx, selectGaugeLDCSQL, m.Bottleneck).Scan(&ldc)
+		var depthReference string
+		err := conn.QueryRowContext(ctx, selectGaugeLDCSQL, m.Bottleneck).Scan(
+			&ldc,
+			&depthReference,
+		)
 		switch {
 		case err == sql.ErrNoRows:
 			return nil, errors.New("Cannot load LDC value")
@@ -218,7 +223,7 @@
 		xform = func(v octree.Vertex) octree.Vertex {
 			return octree.Vertex{X: v.X, Y: v.Y, Z: ldc - v.Z}
 		}
-		m.DepthReference = "LDC"
+		m.DepthReference = depthReference
 	}
 
 	if err := m.Validate(ctx, conn); err != nil {
--- a/pkg/imports/wg.go	Wed May 29 15:42:32 2019 +0200
+++ b/pkg/imports/wg.go	Fri May 31 09:35:09 2019 +0200
@@ -235,10 +235,10 @@
 			gauges = append(gauges, isrs)
 			feedback.Info("Processing %s", code)
 
-			// We need a valid time range to identify gauge versions in DB
+			// We need a valid, non-empty time range to identify gauge versions
 			if dr.Enddate != nil && dr.Startdate != nil &&
-				time.Time(*dr.Enddate).Before(time.Time(*dr.Startdate)) {
-				feedback.Warn("End date before start date")
+				!time.Time(*dr.Enddate).After(time.Time(*dr.Startdate)) {
+				feedback.Warn("End date not after start date")
 				unchanged++
 				continue
 			}
--- a/schema/gemma.sql	Wed May 29 15:42:32 2019 +0200
+++ b/schema/gemma.sql	Fri May 31 09:35:09 2019 +0200
@@ -280,7 +280,7 @@
         geom geography(POINT, 4326) NOT NULL,
         applicability_from_km int8,
         applicability_to_km int8,
-        validity tstzrange,
+        validity tstzrange NOT NULL,
         zero_point double precision NOT NULL,
         geodref varchar,
         date_info timestamp with time zone NOT NULL,
@@ -288,6 +288,7 @@
         lastupdate timestamp with time zone NOT NULL,
         -- entry removed from external data source (RIS-Index)/historicised:
         erased boolean NOT NULL DEFAULT false,
+        CHECK (erased OR NOT isempty(validity)),
         PRIMARY KEY (location, validity),
         EXCLUDE USING GiST (isrs_astext(location) WITH =, validity WITH &&)
             DEFERRABLE INITIALLY DEFERRED
@@ -490,9 +491,9 @@
         bottleneck_id varchar UNIQUE NOT NULL,
         gauge_location isrs NOT NULL,
         gauge_validity tstzrange NOT NULL,
-        FOREIGN KEY (gauge_location, gauge_validity) REFERENCES gauges
-            ON UPDATE CASCADE,
-        -- XXX: DRC references "ch. 3.1.1", which does not exist in document.
+        CONSTRAINT gauge_key
+            FOREIGN KEY (gauge_location, gauge_validity) REFERENCES gauges
+                ON UPDATE CASCADE,
         objnam varchar,
         nobjnm varchar,
         stretch isrsrange NOT NULL,