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