changeset 2694:0d7a4fdb9e12

Added GET /api/data/waterlevels/{gauge isrs}?from={time_a}&to={time_b} to fetch waterlevels of gauge.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 15 Mar 2019 18:34:13 +0100
parents 02d012272dd4
children 04b082a86d50
files pkg/controllers/gauges.go pkg/controllers/routes.go
diffstat 2 files changed, 147 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkg/controllers/gauges.go	Fri Mar 15 18:34:13 2019 +0100
@@ -0,0 +1,142 @@
+// 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) 2019 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 (
+	"encoding/csv"
+	"fmt"
+	"log"
+	"net/http"
+	"strconv"
+	"time"
+
+	"github.com/gorilla/mux"
+
+	"gemma.intevation.de/gemma/pkg/middleware"
+	"gemma.intevation.de/gemma/pkg/models"
+)
+
+const (
+	selectWaterlevelsSQL = `
+SELECT
+  measure_date,
+  water_level,
+  predicted
+FROM waterway.gauge_measurements
+WHERE
+`
+)
+
+func waterlevels(rw http.ResponseWriter, req *http.Request) {
+	gauge := mux.Vars(req)["gauge"]
+
+	isrs, err := models.IsrsFromString(gauge)
+	if err != nil {
+		http.Error(
+			rw, fmt.Sprintf("error: Invalid ISRS code: %v", err),
+			http.StatusBadRequest)
+		return
+	}
+
+	var fb filterBuilder
+	fb.stmt.WriteString(selectWaterlevelsSQL)
+
+	fb.cond(
+		" fk_gauge_id = ($%d::char(2), $%d::char(3), $%d::char(5), $%d::char(5), $%d::int) ",
+		isrs.CountryCode,
+		isrs.LoCode,
+		isrs.FairwaySection,
+		isrs.Orc,
+		isrs.Hectometre,
+	)
+
+	if from := req.FormValue("from"); from != "" {
+		fromTime, err := time.Parse(models.ImportTimeFormat, from)
+		if err != nil {
+			http.Error(
+				rw, fmt.Sprintf("error: Invalid from time: %v", err),
+				http.StatusBadRequest)
+			return
+		}
+		fb.cond("measure_date >= $%d", fromTime)
+	}
+
+	if to := req.FormValue("to"); to != "" {
+		toTime, err := time.Parse(models.ImportTimeFormat, to)
+		if err != nil {
+			http.Error(
+				rw, fmt.Sprintf("error: Invalid from time: %v", err),
+				http.StatusBadRequest)
+			return
+		}
+		fb.cond("measure_date <= $%d", toTime)
+	}
+
+	conn := middleware.GetDBConn(req)
+
+	ctx := req.Context()
+
+	rows, err := conn.QueryContext(ctx, fb.stmt.String(), fb.args...)
+	if err != nil {
+		http.Error(
+			rw, fmt.Sprintf("error: %v", err),
+			http.StatusInternalServerError)
+		return
+	}
+	defer rows.Close()
+
+	rw.Header().Add("Content-Type", "text/csv")
+
+	out := csv.NewWriter(rw)
+
+	record := make([]string, 3)
+
+	for rows.Next() {
+		var (
+			measureDate time.Time
+			waterlevel  float64
+			predicted   bool
+		)
+		if err := rows.Scan(&measureDate, &waterlevel, &predicted); err != nil {
+			log.Printf("error: %v\n", err)
+			// Too late for an HTTP error code.
+			return
+		}
+		record[0] = measureDate.Format(models.ImportTimeFormat)
+		record[1] = strconv.FormatFloat(waterlevel, 'f', -1, 64)
+		if predicted {
+			record[2] = "t"
+		} else {
+			record[2] = "f"
+		}
+		if err := out.Write(record); err != nil {
+			log.Printf("error: %v", err)
+			// Too late for an HTTP error code.
+			return
+		}
+	}
+
+	if err := rows.Err(); err != nil {
+		log.Printf("error: %v", err)
+		// Too late for an HTTP error code.
+		return
+	}
+
+	out.Flush()
+	if err := out.Error(); err != nil {
+		log.Printf("error: %v", err)
+		// Too late for an HTTP error code.
+		return
+	}
+}
--- a/pkg/controllers/routes.go	Fri Mar 15 18:24:57 2019 +0100
+++ b/pkg/controllers/routes.go	Fri Mar 15 18:34:13 2019 +0100
@@ -297,6 +297,11 @@
 			Handle: reviewImport,
 		})).Methods(http.MethodPut)
 
+	// Handler to serve data to the client.
+
+	api.Handle("/data/waterlevels/{gauge}", any(
+		middleware.DBConn(http.HandlerFunc(waterlevels)))).Methods(http.MethodGet)
+
 	// Token handling: Login/Logout.
 	api.HandleFunc("/login", login).
 		Methods(http.MethodPost)