view pkg/controllers/fwa.go @ 5197:c352dbbf2778 new-fwa

Started with loading limiting factors and validities.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 07 May 2020 17:50:29 +0200
parents 5bc8daa986d9
children 5001582f2ee1
line wrap: on
line source

// 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, 2019, 2020 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 (
	"context"
	"database/sql"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/mux"

	"gemma.intevation.de/gemma/pkg/common"
	"gemma.intevation.de/gemma/pkg/middleware"
)

const (
	selectBottlenecksLimitingSQL = `
SELECT
  lower(validity),
  upper(validity),
  limiting
FROM
  waterway.bottlenecks
WHERE
  bottleneck_id = $1 AND
  validity && tstzrange($2, $3)`
)

type (
	limitingValidity struct {
		limiting string
		lower    time.Time
		upper    time.Time
	}

	limitingValidities []limitingValidity
)

func (lv *limitingValidity) intersects(from, to time.Time) bool {
	return !(to.Before(lv.lower) || from.After(lv.upper))
}

func (lvs limitingValidities) find() func(from, to time.Time) *limitingValidity {

	var last *limitingValidity

	return func(from, to time.Time) *limitingValidity {
		if last != nil && last.intersects(from, to) {
			return last
		}
		for i := range lvs {
			if lv := &lvs[i]; lv.intersects(from, to) {
				last = lv
				return lv
			}
		}
		return nil
	}
}

func loadLimitingValidities(
	ctx context.Context,
	conn *sql.Conn,
	bottleneckID string,
	from, to time.Time,
) (limitingValidities, error) {

	var lvs limitingValidities

	rows, err := conn.QueryContext(
		ctx,
		selectLimitingSQL,
		from, to)

	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var lv limitingValidity
		if err := rows.Scan(
			&lv.limiting,
			&lv.lower,
			&lv.upper,
		); err != nil {
			return nil, err
		}
		lv.lower = lv.lower.UTC()
		lv.upper = lv.upper.UTC()
		lvs = append(lvs, lv)
	}

	return lvs, rows.Err()
}

func fairwayAvailability(rw http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

	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
	}
}

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 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(
		ctx,
		conn,
		bottleneckID,
		from, to)
	if err != nil {
		log.Printf("error: %v\n", err)
		http.Error(rw, "Loading validities failed", http.StatusInternalServerError)
		return
	}

	// TODO: Implement me!
	_ = los
	_ = lvs
}