Mercurial > gemma
comparison pkg/controllers/fwa.go @ 5576:2b862190aee4 surveysperbottleneckid
merge with default
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Tue, 08 Feb 2022 10:20:26 +0100 |
parents | 5f47eeea988d |
children |
comparison
equal
deleted
inserted
replaced
5575:6709530f002e | 5576:2b862190aee4 |
---|---|
16 import ( | 16 import ( |
17 "context" | 17 "context" |
18 "database/sql" | 18 "database/sql" |
19 "encoding/csv" | 19 "encoding/csv" |
20 "fmt" | 20 "fmt" |
21 "log" | |
22 "net/http" | 21 "net/http" |
23 "sort" | 22 "sort" |
24 "strconv" | 23 "strconv" |
25 "strings" | 24 "strings" |
26 "time" | 25 "time" |
27 | 26 |
28 "github.com/gorilla/mux" | 27 "github.com/gorilla/mux" |
29 | 28 |
30 "gemma.intevation.de/gemma/pkg/common" | 29 "gemma.intevation.de/gemma/pkg/common" |
30 "gemma.intevation.de/gemma/pkg/log" | |
31 "gemma.intevation.de/gemma/pkg/middleware" | 31 "gemma.intevation.de/gemma/pkg/middleware" |
32 ) | 32 ) |
33 | 33 |
34 const ( | 34 const ( |
35 selectBottlenecksLimitingSQL = ` | 35 selectBottlenecksLimitingSQL = ` |
217 http.Error(rw, "Invalid kind type.", http.StatusBadRequest) | 217 http.Error(rw, "Invalid kind type.", http.StatusBadRequest) |
218 return | 218 return |
219 } | 219 } |
220 | 220 |
221 if err != nil { | 221 if err != nil { |
222 log.Printf("error: %v\n", err) | 222 log.Errorf("%v\n", err) |
223 http.Error(rw, "cannot extract bottlenecks", http.StatusBadRequest) | 223 http.Error(rw, "cannot extract bottlenecks", http.StatusBadRequest) |
224 return | 224 return |
225 } | 225 } |
226 | 226 |
227 // If there are no bottlenecks there is nothing to do. | 227 // If there are no bottlenecks there is nothing to do. |
231 } | 231 } |
232 | 232 |
233 // load validities and limiting factors | 233 // load validities and limiting factors |
234 for i := range bns { | 234 for i := range bns { |
235 if err := bns[i].loadLimitingValidities(ctx, conn, from, to); err != nil { | 235 if err := bns[i].loadLimitingValidities(ctx, conn, from, to); err != nil { |
236 log.Printf("error: %v\n", err) | 236 log.Errorf("%v\n", err) |
237 http.Error(rw, "cannot load validities", http.StatusInternalServerError) | 237 http.Error(rw, "cannot load validities", http.StatusInternalServerError) |
238 return | 238 return |
239 } | 239 } |
240 // load LCDs | 240 // load LCDs |
241 if err := bns[i].loadLDCs(ctx, conn, from, to); err != nil { | 241 if err := bns[i].loadLDCs(ctx, conn, from, to); err != nil { |
242 log.Printf("error: %v\n", err) | 242 log.Errorf("%v\n", err) |
243 http.Error(rw, "cannot load LDCs", http.StatusInternalServerError) | 243 http.Error(rw, "cannot load LDCs", http.StatusInternalServerError) |
244 return | 244 return |
245 } | 245 } |
246 // load values | 246 // load values |
247 if err := bns[i].loadValues(ctx, conn, from, to, los); err != nil { | 247 if err := bns[i].loadValues(ctx, conn, from, to, los); err != nil { |
248 log.Printf("error: %v\n", err) | 248 log.Errorf("%v\n", err) |
249 http.Error(rw, "cannot load values", http.StatusInternalServerError) | 249 http.Error(rw, "cannot load values", http.StatusInternalServerError) |
250 return | 250 return |
251 } | 251 } |
252 } | 252 } |
253 | 253 |
254 // separate breaks for depth and width | 254 // separate breaks for depth and width |
255 var ( | 255 breaks, ok := parseBreaks(rw, req, "breaks", afdRefs) |
256 breaks = parseBreaks(req.FormValue("breaks"), afdRefs) | 256 if !ok { |
257 depthBreaks = parseBreaks(req.FormValue("depthbreaks"), breaks) | 257 return |
258 widthBreaks = parseBreaks(req.FormValue("widthbreaks"), breaks) | 258 } |
259 chooseBreaks = [...][]float64{ | 259 depthBreaks, ok := parseBreaks(rw, req, "depthbreaks", breaks) |
260 limitingDepth: depthBreaks, | 260 if !ok { |
261 limitingWidth: widthBreaks, | 261 return |
262 } | 262 } |
263 | 263 widthBreaks, ok := parseBreaks(rw, req, "widthbreaks", breaks) |
264 useDepth = bns.hasLimiting(limitingDepth, from, to) | 264 if !ok { |
265 useWidth = bns.hasLimiting(limitingWidth, from, to) | 265 return |
266 ) | 266 } |
267 | |
268 chooseBreaks := [...][]float64{ | |
269 limitingDepth: depthBreaks, | |
270 limitingWidth: widthBreaks, | |
271 } | |
272 | |
273 useDepth := bns.hasLimiting(limitingDepth, from, to) | |
274 useWidth := bns.hasLimiting(limitingWidth, from, to) | |
267 | 275 |
268 if useDepth && useWidth && len(widthBreaks) != len(depthBreaks) { | 276 if useDepth && useWidth && len(widthBreaks) != len(depthBreaks) { |
269 http.Error( | 277 http.Error( |
270 rw, | 278 rw, |
271 fmt.Sprintf("class breaks lengths differ: %d != %d", | 279 fmt.Sprintf("class breaks lengths differ: %d != %d", |
290 | 298 |
291 out := csv.NewWriter(rw) | 299 out := csv.NewWriter(rw) |
292 | 300 |
293 if err := out.Write(record); err != nil { | 301 if err := out.Write(record); err != nil { |
294 // Too late for HTTP status message. | 302 // Too late for HTTP status message. |
295 log.Printf("error: %v\n", err) | 303 log.Errorf("%v\n", err) |
296 return | 304 return |
297 } | 305 } |
298 | 306 |
299 for i := range record[1:] { | 307 for i := range record[1:] { |
300 record[i+1] = "0" | 308 record[i+1] = "0" |
382 if (result[0] != 0 || result[1] != 0) && result[1] < 12*time.Hour { | 390 if (result[0] != 0 || result[1] != 0) && result[1] < 12*time.Hour { |
383 overLDC = false | 391 overLDC = false |
384 } | 392 } |
385 } | 393 } |
386 | 394 |
387 if min := minClass(bns[i].measurements.classify( | 395 classes := bns[i].measurements.classify( |
388 current, next, | 396 current, next, |
389 chooseBreaks[vs.limiting], | 397 chooseBreaks[vs.limiting], |
390 limitingAccess[vs.limiting]), | 398 limitingAccess[vs.limiting]) |
391 12*time.Hour, | 399 |
392 ); min < lowest { | 400 if min := minClass(classes, 12*time.Hour); min < lowest { |
393 lowest = min | 401 lowest = min |
394 } | 402 } |
395 } | 403 } |
396 | 404 |
397 if hasValid { | 405 if hasValid { |
407 totalDays++ | 415 totalDays++ |
408 | 416 |
409 if finish(next) { | 417 if finish(next) { |
410 if err := write(); err != nil { | 418 if err := write(); err != nil { |
411 // Too late for HTTP status message. | 419 // Too late for HTTP status message. |
412 log.Printf("error: %v\n", err) | 420 log.Errorf("%v\n", err) |
413 return | 421 return |
414 } | 422 } |
415 | 423 |
416 // Reset counters | 424 // Reset counters |
417 overLDCDays, totalDays = 0, 0 | 425 overLDCDays, totalDays = 0, 0 |
425 | 433 |
426 // Write rest if last period was not finished. | 434 // Write rest if last period was not finished. |
427 if totalDays > 0 { | 435 if totalDays > 0 { |
428 if err := write(); err != nil { | 436 if err := write(); err != nil { |
429 // Too late for HTTP status message. | 437 // Too late for HTTP status message. |
430 log.Printf("error: %v\n", err) | 438 log.Errorf("%v\n", err) |
431 return | 439 return |
432 } | 440 } |
433 } | 441 } |
434 | 442 |
435 for i, days := range missingLDCs { | 443 for i, days := range missingLDCs { |
436 if missingLDCs[i] > 0 { | 444 if missingLDCs[i] > 0 { |
437 log.Printf("warn: Missing LDCs for %s on %d days.\n", | 445 log.Warnf("warn: Missing LDCs for %s on %d days.\n", |
438 bns[i].id, days) | 446 bns[i].id, days) |
439 } | 447 } |
440 } | 448 } |
441 | 449 |
442 out.Flush() | 450 out.Flush() |
443 if err := out.Error(); err != nil { | 451 if err := out.Error(); err != nil { |
444 // Too late for HTTP status message. | 452 // Too late for HTTP status message. |
445 log.Printf("error: %v\n", err) | 453 log.Errorf("%v\n", err) |
446 } | 454 } |
447 } | 455 } |
448 | 456 |
449 func minClass(classes []time.Duration, threshold time.Duration) int { | 457 func minClass(classes []time.Duration, threshold time.Duration) int { |
450 var sum time.Duration | 458 var sum time.Duration |
486 default: | 494 default: |
487 return fwaMonthly | 495 return fwaMonthly |
488 } | 496 } |
489 } | 497 } |
490 | 498 |
491 func breaksToReferenceValue(breaks string) []float64 { | 499 func breaksToReferenceValue(breaks string) ([]float64, error) { |
492 parts := strings.Split(breaks, ",") | 500 parts := strings.Split(breaks, ",") |
493 var values []float64 | 501 var values []float64 |
494 | 502 |
495 for _, part := range parts { | 503 for _, part := range parts { |
496 part = strings.TrimSpace(part) | 504 part = strings.TrimSpace(part) |
497 if v, err := strconv.ParseFloat(part, 64); err == nil { | 505 v, err := strconv.ParseFloat(part, 64) |
498 values = append(values, v) | 506 if err != nil { |
499 } | 507 return nil, err |
500 } | 508 } |
501 | 509 values = append(values, v) |
502 return common.DedupFloat64s(values) | 510 } |
503 } | 511 |
504 | 512 return common.DedupFloat64s(values), nil |
505 func parseBreaks(breaks string, defaults []float64) []float64 { | 513 } |
506 if breaks != "" { | 514 |
507 return breaksToReferenceValue(breaks) | 515 func parseBreaks( |
508 } | 516 rw http.ResponseWriter, req *http.Request, |
509 return defaults | 517 parameter string, |
518 defaults []float64, | |
519 ) ([]float64, bool) { | |
520 | |
521 breaks := strings.TrimSpace(req.FormValue(parameter)) | |
522 if breaks == "" { | |
523 return defaults, true | |
524 } | |
525 | |
526 defaults, err := breaksToReferenceValue(breaks) | |
527 if err != nil { | |
528 msg := fmt.Sprintf("Parameter '%s' is invalid: %s.", parameter, err) | |
529 log.Errorf("%s\n", msg) | |
530 http.Error(rw, msg, http.StatusBadRequest) | |
531 return nil, false | |
532 } | |
533 | |
534 return defaults, true | |
510 } | 535 } |
511 | 536 |
512 func (tr *timeRange) intersects(from, to time.Time) bool { | 537 func (tr *timeRange) intersects(from, to time.Time) bool { |
513 return !(to.Before(tr.lower) || from.After(tr.upper)) | 538 return !(to.Before(tr.lower) || from.After(tr.upper)) |
514 } | 539 } |
559 case "depth": | 584 case "depth": |
560 return limitingDepth | 585 return limitingDepth |
561 case "width": | 586 case "width": |
562 return limitingWidth | 587 return limitingWidth |
563 default: | 588 default: |
564 log.Printf("warn: unknown limitation '%s'. default to 'depth'\n", limiting) | 589 log.Warnf("unknown limitation '%s'. default to 'depth'\n", limiting) |
565 return limitingDepth | 590 return limitingDepth |
566 } | 591 } |
567 } | 592 } |
568 | 593 |
569 func loadLimitingValidities( | 594 func loadLimitingValidities( |