changeset 5199:5001582f2ee1 new-fwa

Prepare to treat stretches and sections, too.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 08 May 2020 11:43:06 +0200
parents 0e91d200299d
children 5572da077c89
files pkg/controllers/fwa.go
diffstat 1 files changed, 173 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/fwa.go	Fri May 08 09:24:19 2020 +0200
+++ b/pkg/controllers/fwa.go	Fri May 08 11:43:06 2020 +0200
@@ -16,6 +16,7 @@
 import (
 	"context"
 	"database/sql"
+	"fmt"
 	"log"
 	"net/http"
 	"time"
@@ -37,6 +38,16 @@
 WHERE
   bottleneck_id = $1 AND
   validity && tstzrange($2, $3)`
+
+	selectSymbolBottlenecksSQL = `
+SELECT
+  distinct(b.bottleneck_id)
+FROM
+  users.%s s, waterway.bottlenecks b
+WHERE
+  ST_Intersects(b.area, s.area)
+  AND s.name = $1
+  AND b.validity && tstzrange($2, $3)`
 )
 
 type (
@@ -47,8 +58,101 @@
 	}
 
 	limitingValidities []limitingValidity
+
+	bottleneck struct {
+		id         string
+		validities limitingValidities
+	}
 )
 
+func fairwayAvailability(rw http.ResponseWriter, req *http.Request) {
+
+	from, to, ok := parseFromTo(rw, req)
+	if !ok {
+		return
+	}
+
+	vars := mux.Vars(req)
+	name := vars["name"]
+	if name == "" {
+		http.Error(rw, "missing 'name' parameter.", http.StatusBadRequest)
+		return
+	}
+
+	ctx := req.Context()
+	conn := middleware.GetDBConn(req)
+
+	// Function to extract the bottleneck_id's from the query.
+	var extract func(context.Context, *sql.Conn, string, time.Time, time.Time) ([]bottleneck, error)
+
+	switch vars["kind"] {
+	case "bottleneck":
+		extract = extractBottleneck
+	case "stretch":
+		extract = extractStretch
+	case "section":
+		extract = extractSection
+	default:
+		http.Error(rw, "Invalid kind type.", http.StatusBadRequest)
+		return
+	}
+
+	bottlenecks, err := extract(ctx, conn, name, from, to)
+	if err != nil {
+		log.Println("error: %v\n", err)
+		http.Error(rw, "cannot extract bottlenecks", http.StatusBadRequest)
+		return
+	}
+
+	// load validities and limiting factors
+	for i := range bottlenecks {
+		if err := bottlenecks[i].loadLimitingValidities(ctx, conn, from, to); err != nil {
+			log.Printf("error: %v\n", err)
+			http.Error(rw, "cannot load validities", http.StatusInternalServerError)
+			return
+		}
+	}
+
+	// TODO: Implement me!
+}
+
+func dusk(t time.Time) time.Time {
+	return time.Date(
+		t.Year(),
+		t.Month(),
+		t.Day(),
+		0, 0, 0, 0,
+		t.Location())
+}
+
+func dawn(t time.Time) time.Time {
+	return time.Date(
+		t.Year(),
+		t.Month(),
+		t.Day(),
+		23, 59, 59, 999999999,
+		t.Location())
+}
+
+func parseFromTo(
+	rw http.ResponseWriter,
+	req *http.Request,
+) (time.Time, time.Time, bool) {
+	from, ok := parseFormTime(rw, req, "from", time.Now().AddDate(-1, 0, 0))
+	if !ok {
+		return time.Time{}, time.Time{}, false
+	}
+
+	to, ok := parseFormTime(rw, req, "to", from.AddDate(1, 0, 0))
+	if !ok {
+		return time.Time{}, time.Time{}, false
+	}
+
+	from, to = common.OrderTime(from, to)
+	// Operate on daily basis so go to full days.
+	return dusk(from), dawn(to), true
+}
+
 func (lv *limitingValidity) intersects(from, to time.Time) bool {
 	return !(to.Before(lv.lower) || from.After(lv.upper))
 }
@@ -107,74 +211,83 @@
 	return lvs, rows.Err()
 }
 
-func fairwayAvailability(rw http.ResponseWriter, req *http.Request) {
-
-	vars := mux.Vars(req)
+func loadSymbolBottlenecksFromTo(
+	ctx context.Context,
+	conn *sql.Conn,
+	what, name string,
+	from, to time.Time,
+) ([]bottleneck, error) {
 
-	switch vars["kind"] {
-	case "bottleneck":
-		fairwayAvailabilityBottleneck(rw, req)
-	case "stretch": // TODO: Implement me!
-	case "section": // TODO: Implement me!
-	default:
-		http.Error(rw, "Invalid kind type.", http.StatusBadRequest)
-		return
+	rows, err := conn.QueryContext(
+		ctx,
+		fmt.Sprintf(selectSymbolBottlenecksSQL, what),
+		name,
+		from, to)
+	if err != nil {
+		return nil, err
 	}
+	defer rows.Close()
+
+	var bottlenecks []bottleneck
+
+	for rows.Next() {
+		var b bottleneck
+		if err := rows.Scan(&b.id); err != nil {
+			return nil, err
+		}
+		bottlenecks = append(bottlenecks, b)
+	}
+
+	return bottlenecks, rows.Err()
 }
 
-func parseFromTo(
-	rw http.ResponseWriter,
-	req *http.Request,
-) (time.Time, time.Time, bool) {
-	from, ok := parseFormTime(rw, req, "from", time.Now().AddDate(-1, 0, 0))
-	if !ok {
-		return time.Time{}, time.Time{}, false
-	}
-
-	to, ok := parseFormTime(rw, req, "to", from.AddDate(1, 0, 0))
-	if !ok {
-		return time.Time{}, time.Time{}, false
-	}
-	from, to = common.OrderTime(from, to)
-	return from, to, true
+func extractBottleneck(
+	_ context.Context,
+	_ *sql.Conn,
+	name string,
+	_, _ time.Time,
+) ([]bottleneck, error) {
+	return []bottleneck{{id: name}}, nil
 }
 
-func fairwayAvailabilityBottleneck(rw http.ResponseWriter, req *http.Request) {
-
-	vars := mux.Vars(req)
-
-	bottleneckID := vars["name"]
-	if bottleneckID == "" {
-		http.Error(rw, "missing bottleneck_id", http.StatusBadRequest)
-		return
-	}
-	log.Printf("info: fairway availability for bottleneck_id '%s'\n", bottleneckID)
-
-	from, to, ok := parseFromTo(rw, req)
-	if !ok {
-		return
-	}
-
-	los, ok := parseFormInt(rw, req, "los", 1)
-	if !ok {
-		return
-	}
-
-	ctx := req.Context()
-	conn := middleware.GetDBConn(req)
-
-	lvs, err := loadLimitingValidities(
+func extractStretch(
+	ctx context.Context,
+	conn *sql.Conn,
+	name string,
+	from, to time.Time,
+) ([]bottleneck, error) {
+	return loadSymbolBottlenecksFromTo(
 		ctx,
 		conn,
-		bottleneckID,
+		"stretches", name,
+		from, to)
+}
+
+func extractSection(
+	ctx context.Context,
+	conn *sql.Conn,
+	name string,
+	from, to time.Time,
+) ([]bottleneck, error) {
+	return loadSymbolBottlenecksFromTo(
+		ctx,
+		conn,
+		"sections", name,
 		from, to)
-	if err != nil {
-		log.Printf("error: %v\n", err)
-		http.Error(rw, "Loading validities failed", http.StatusInternalServerError)
-		return
+}
+
+func (bn *bottleneck) loadLimitingValidities(
+	ctx context.Context,
+	conn *sql.Conn,
+	from, to time.Time,
+) error {
+	vs, err := loadLimitingValidities(
+		ctx,
+		conn,
+		bn.id,
+		from, to)
+	if err == nil {
+		bn.validities = vs
 	}
-
-	// TODO: Implement me!
-	_ = los
-	_ = lvs
+	return err
 }