Mercurial > gemma
view pkg/controllers/fwa.go @ 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 | c352dbbf2778 |
children | 5572da077c89 |
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" "fmt" "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)` 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 ( limitingValidity struct { limiting string lower time.Time upper time.Time } 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)) } 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 loadSymbolBottlenecksFromTo( ctx context.Context, conn *sql.Conn, what, name string, from, to time.Time, ) ([]bottleneck, error) { 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 extractBottleneck( _ context.Context, _ *sql.Conn, name string, _, _ time.Time, ) ([]bottleneck, error) { return []bottleneck{{id: name}}, nil } func extractStretch( ctx context.Context, conn *sql.Conn, name string, from, to time.Time, ) ([]bottleneck, error) { return loadSymbolBottlenecksFromTo( ctx, conn, "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) } 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 } return err }