comparison 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
comparison
equal deleted inserted replaced
5198:0e91d200299d 5199:5001582f2ee1
14 package controllers 14 package controllers
15 15
16 import ( 16 import (
17 "context" 17 "context"
18 "database/sql" 18 "database/sql"
19 "fmt"
19 "log" 20 "log"
20 "net/http" 21 "net/http"
21 "time" 22 "time"
22 23
23 "github.com/gorilla/mux" 24 "github.com/gorilla/mux"
35 FROM 36 FROM
36 waterway.bottlenecks 37 waterway.bottlenecks
37 WHERE 38 WHERE
38 bottleneck_id = $1 AND 39 bottleneck_id = $1 AND
39 validity && tstzrange($2, $3)` 40 validity && tstzrange($2, $3)`
41
42 selectSymbolBottlenecksSQL = `
43 SELECT
44 distinct(b.bottleneck_id)
45 FROM
46 users.%s s, waterway.bottlenecks b
47 WHERE
48 ST_Intersects(b.area, s.area)
49 AND s.name = $1
50 AND b.validity && tstzrange($2, $3)`
40 ) 51 )
41 52
42 type ( 53 type (
43 limitingValidity struct { 54 limitingValidity struct {
44 limiting string 55 limiting string
45 lower time.Time 56 lower time.Time
46 upper time.Time 57 upper time.Time
47 } 58 }
48 59
49 limitingValidities []limitingValidity 60 limitingValidities []limitingValidity
61
62 bottleneck struct {
63 id string
64 validities limitingValidities
65 }
50 ) 66 )
67
68 func fairwayAvailability(rw http.ResponseWriter, req *http.Request) {
69
70 from, to, ok := parseFromTo(rw, req)
71 if !ok {
72 return
73 }
74
75 vars := mux.Vars(req)
76 name := vars["name"]
77 if name == "" {
78 http.Error(rw, "missing 'name' parameter.", http.StatusBadRequest)
79 return
80 }
81
82 ctx := req.Context()
83 conn := middleware.GetDBConn(req)
84
85 // Function to extract the bottleneck_id's from the query.
86 var extract func(context.Context, *sql.Conn, string, time.Time, time.Time) ([]bottleneck, error)
87
88 switch vars["kind"] {
89 case "bottleneck":
90 extract = extractBottleneck
91 case "stretch":
92 extract = extractStretch
93 case "section":
94 extract = extractSection
95 default:
96 http.Error(rw, "Invalid kind type.", http.StatusBadRequest)
97 return
98 }
99
100 bottlenecks, err := extract(ctx, conn, name, from, to)
101 if err != nil {
102 log.Println("error: %v\n", err)
103 http.Error(rw, "cannot extract bottlenecks", http.StatusBadRequest)
104 return
105 }
106
107 // load validities and limiting factors
108 for i := range bottlenecks {
109 if err := bottlenecks[i].loadLimitingValidities(ctx, conn, from, to); err != nil {
110 log.Printf("error: %v\n", err)
111 http.Error(rw, "cannot load validities", http.StatusInternalServerError)
112 return
113 }
114 }
115
116 // TODO: Implement me!
117 }
118
119 func dusk(t time.Time) time.Time {
120 return time.Date(
121 t.Year(),
122 t.Month(),
123 t.Day(),
124 0, 0, 0, 0,
125 t.Location())
126 }
127
128 func dawn(t time.Time) time.Time {
129 return time.Date(
130 t.Year(),
131 t.Month(),
132 t.Day(),
133 23, 59, 59, 999999999,
134 t.Location())
135 }
136
137 func parseFromTo(
138 rw http.ResponseWriter,
139 req *http.Request,
140 ) (time.Time, time.Time, bool) {
141 from, ok := parseFormTime(rw, req, "from", time.Now().AddDate(-1, 0, 0))
142 if !ok {
143 return time.Time{}, time.Time{}, false
144 }
145
146 to, ok := parseFormTime(rw, req, "to", from.AddDate(1, 0, 0))
147 if !ok {
148 return time.Time{}, time.Time{}, false
149 }
150
151 from, to = common.OrderTime(from, to)
152 // Operate on daily basis so go to full days.
153 return dusk(from), dawn(to), true
154 }
51 155
52 func (lv *limitingValidity) intersects(from, to time.Time) bool { 156 func (lv *limitingValidity) intersects(from, to time.Time) bool {
53 return !(to.Before(lv.lower) || from.After(lv.upper)) 157 return !(to.Before(lv.lower) || from.After(lv.upper))
54 } 158 }
55 159
105 } 209 }
106 210
107 return lvs, rows.Err() 211 return lvs, rows.Err()
108 } 212 }
109 213
110 func fairwayAvailability(rw http.ResponseWriter, req *http.Request) { 214 func loadSymbolBottlenecksFromTo(
111 215 ctx context.Context,
112 vars := mux.Vars(req) 216 conn *sql.Conn,
113 217 what, name string,
114 switch vars["kind"] { 218 from, to time.Time,
115 case "bottleneck": 219 ) ([]bottleneck, error) {
116 fairwayAvailabilityBottleneck(rw, req) 220
117 case "stretch": // TODO: Implement me! 221 rows, err := conn.QueryContext(
118 case "section": // TODO: Implement me! 222 ctx,
119 default: 223 fmt.Sprintf(selectSymbolBottlenecksSQL, what),
120 http.Error(rw, "Invalid kind type.", http.StatusBadRequest) 224 name,
121 return 225 from, to)
122 } 226 if err != nil {
123 } 227 return nil, err
124 228 }
125 func parseFromTo( 229 defer rows.Close()
126 rw http.ResponseWriter, 230
127 req *http.Request, 231 var bottlenecks []bottleneck
128 ) (time.Time, time.Time, bool) { 232
129 from, ok := parseFormTime(rw, req, "from", time.Now().AddDate(-1, 0, 0)) 233 for rows.Next() {
130 if !ok { 234 var b bottleneck
131 return time.Time{}, time.Time{}, false 235 if err := rows.Scan(&b.id); err != nil {
132 } 236 return nil, err
133 237 }
134 to, ok := parseFormTime(rw, req, "to", from.AddDate(1, 0, 0)) 238 bottlenecks = append(bottlenecks, b)
135 if !ok { 239 }
136 return time.Time{}, time.Time{}, false 240
137 } 241 return bottlenecks, rows.Err()
138 from, to = common.OrderTime(from, to) 242 }
139 return from, to, true 243
140 } 244 func extractBottleneck(
141 245 _ context.Context,
142 func fairwayAvailabilityBottleneck(rw http.ResponseWriter, req *http.Request) { 246 _ *sql.Conn,
143 247 name string,
144 vars := mux.Vars(req) 248 _, _ time.Time,
145 249 ) ([]bottleneck, error) {
146 bottleneckID := vars["name"] 250 return []bottleneck{{id: name}}, nil
147 if bottleneckID == "" { 251 }
148 http.Error(rw, "missing bottleneck_id", http.StatusBadRequest) 252
149 return 253 func extractStretch(
150 } 254 ctx context.Context,
151 log.Printf("info: fairway availability for bottleneck_id '%s'\n", bottleneckID) 255 conn *sql.Conn,
152 256 name string,
153 from, to, ok := parseFromTo(rw, req) 257 from, to time.Time,
154 if !ok { 258 ) ([]bottleneck, error) {
155 return 259 return loadSymbolBottlenecksFromTo(
156 }
157
158 los, ok := parseFormInt(rw, req, "los", 1)
159 if !ok {
160 return
161 }
162
163 ctx := req.Context()
164 conn := middleware.GetDBConn(req)
165
166 lvs, err := loadLimitingValidities(
167 ctx, 260 ctx,
168 conn, 261 conn,
169 bottleneckID, 262 "stretches", name,
170 from, to) 263 from, to)
171 if err != nil { 264 }
172 log.Printf("error: %v\n", err) 265
173 http.Error(rw, "Loading validities failed", http.StatusInternalServerError) 266 func extractSection(
174 return 267 ctx context.Context,
175 } 268 conn *sql.Conn,
176 269 name string,
177 // TODO: Implement me! 270 from, to time.Time,
178 _ = los 271 ) ([]bottleneck, error) {
179 _ = lvs 272 return loadSymbolBottlenecksFromTo(
180 } 273 ctx,
274 conn,
275 "sections", name,
276 from, to)
277 }
278
279 func (bn *bottleneck) loadLimitingValidities(
280 ctx context.Context,
281 conn *sql.Conn,
282 from, to time.Time,
283 ) error {
284 vs, err := loadLimitingValidities(
285 ctx,
286 conn,
287 bn.id,
288 from, to)
289 if err == nil {
290 bn.validities = vs
291 }
292 return err
293 }