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(