changeset 3452:ae6c09fbc590

fairway depth/availability: bring unified CVS to streches and sections, too.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 24 May 2019 13:09:08 +0200
parents 8634d2bd0aac
children 41349a9c8ce9
files pkg/controllers/stretches.go
diffstat 1 files changed, 133 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/stretches.go	Fri May 24 13:07:02 2019 +0200
+++ b/pkg/controllers/stretches.go	Fri May 24 13:09:08 2019 +0200
@@ -60,8 +60,33 @@
 		breaks       []float64
 		access       func(*availMeasurement) float64
 	}
+
+	availResult struct {
+		label       string
+		from, to    time.Time
+		ldc, breaks []time.Duration
+	}
+
+	availCalculation struct {
+		result      *availResult
+		ldc, breaks []time.Duration
+	}
+
+	availJob struct {
+		result *availResult
+		bn     *fullStretchBottleneck
+	}
 )
 
+func (r *availResult) add(c availCalculation) {
+	for i, v := range c.ldc {
+		r.ldc[i] += v
+	}
+	for i, v := range c.breaks {
+		r.breaks[i] += v
+	}
+}
+
 func (bns stretchBottlenecks) contains(limiting string) bool {
 	for i := range bns {
 		if bns[i].limiting == limiting {
@@ -159,7 +184,6 @@
 	vars := mux.Vars(req)
 	stretch := vars["kind"] == "stretch"
 	name := vars["name"]
-
 	mode := intervalMode(req.FormValue("mode"))
 
 	depthbreaks, widthbreaks := afdRefs, afdRefs
@@ -250,31 +274,13 @@
 		return
 	}
 
-	type (
-		result struct {
-			label       string
-			from, to    time.Time
-			ldc, breaks []time.Duration
-		}
-
-		calculation struct {
-			result      *result
-			ldc, breaks []time.Duration
-		}
-
-		job struct {
-			result *result
-			bn     *fullStretchBottleneck
-		}
-	)
-
 	n := runtime.NumCPU() / 2
 	if n == 0 {
 		n = 1
 	}
 
-	jobCh := make(chan job)
-	calcCh := make(chan calculation, n)
+	jobCh := make(chan availJob)
+	calcCh := make(chan availCalculation, n)
 	done := make(chan struct{})
 
 	var wg sync.WaitGroup
@@ -282,14 +288,7 @@
 	go func() {
 		defer close(done)
 		for calc := range calcCh {
-			ldc := calc.result.ldc
-			for i, v := range calc.ldc {
-				ldc[i] += v
-			}
-			breaks := calc.result.breaks
-			for i, v := range calc.breaks {
-				breaks[i] += v
-			}
+			calc.result.add(calc)
 		}
 	}()
 
@@ -310,7 +309,7 @@
 					bn.breaks,
 					bn.access,
 				)
-				calcCh <- calculation{
+				calcCh <- availCalculation{
 					result: res,
 					breaks: breaks,
 					ldc:    ldc,
@@ -319,7 +318,7 @@
 		}()
 	}
 
-	var results []*result
+	var results []*availResult
 
 	interval := intervals[mode](from, to)
 
@@ -333,7 +332,7 @@
 
 	for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() {
 
-		res := &result{
+		res := &availResult{
 			label:  label,
 			from:   pfrom,
 			to:     pto,
@@ -343,9 +342,10 @@
 		results = append(results, res)
 
 		for _, bn := range loaded {
-			jobCh <- job{bn: bn, result: res}
+			jobCh <- availJob{bn: bn, result: res}
 		}
 	}
+
 	close(jobCh)
 	wg.Wait()
 	close(calcCh)
@@ -355,21 +355,22 @@
 
 	out := csv.NewWriter(rw)
 
-	// label, classes, lnwl
-	record := make([]string, 1+1+len(breaks)+1)
-	record[0] = "#label"
-	record[1] = "# >= LDC [h]"
+	// label, lnwl, classes
+	record := make([]string, 1+2+len(breaks)+1)
+	record[0] = "# time"
+	record[1] = "# < LDC [h]"
+	record[2] = "# >= LDC [h]"
 	for i, v := range breaks {
 		if useDepth && useWidth {
 			if i == 0 {
-				record[2] = "# < break_1 [h]"
+				record[3] = "# < break_1 [h]"
 			}
-			record[i+3] = fmt.Sprintf("# >= break_%d", i+1)
+			record[i+4] = fmt.Sprintf("# >= break_%d", i+1)
 		} else {
 			if i == 0 {
-				record[2] = fmt.Sprintf("# < %.2f [h]", v)
+				record[3] = fmt.Sprintf("# < %.2f [h]", v)
 			}
-			record[i+3] = fmt.Sprintf("# >= %.2f [h]", v)
+			record[i+4] = fmt.Sprintf("# >= %.2f [h]", v)
 		}
 	}
 
@@ -384,10 +385,12 @@
 
 	for _, r := range results {
 		record[0] = r.label
-		record[1] = fmt.Sprintf("%.3f", r.ldc[1].Hours()*scale)
+		for i, v := range r.ldc {
+			record[1+i] = fmt.Sprintf("%.3f", v.Hours()*scale)
+		}
 
 		for i, d := range r.breaks {
-			record[2+i] = fmt.Sprintf("%.3f", d.Hours()*scale)
+			record[3+i] = fmt.Sprintf("%.3f", d.Hours()*scale)
 		}
 
 		if err := out.Write(record); err != nil {
@@ -420,6 +423,7 @@
 	vars := mux.Vars(req)
 	stretch := vars["kind"] == "stretch"
 	name := vars["name"]
+	mode := intervalMode(req.FormValue("mode"))
 
 	if name == "" {
 		http.Error(
@@ -522,40 +526,21 @@
 		return
 	}
 
-	type calculation struct {
-		ldc, afd []time.Duration
-	}
-
 	n := runtime.NumCPU() / 2
 	if n == 0 {
 		n = 1
 	}
 
-	var breaks []float64
-	if useDepth {
-		breaks = depthbreaks
-	} else {
-		breaks = widthbreaks
-	}
-
-	jobCh := make(chan *fullStretchBottleneck)
-	calcCh := make(chan calculation, n)
+	jobCh := make(chan availJob)
+	calcCh := make(chan availCalculation, n)
 	done := make(chan struct{})
 
-	ldc := make([]time.Duration, 2)
-	afd := make([]time.Duration, len(breaks)+1)
-
 	var wg sync.WaitGroup
 
 	go func() {
 		defer close(done)
 		for calc := range calcCh {
-			for i, v := range calc.ldc {
-				ldc[i] += v
-			}
-			for i, v := range calc.afd {
-				afd[i] += v
-			}
+			calc.result.add(calc)
 		}
 	}()
 
@@ -563,25 +548,55 @@
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
-			for bn := range jobCh {
+			for job := range jobCh {
+				bn := job.bn
+				res := job.result
 				ldc := bn.measurements.classify(
 					from, to,
 					bn.ldc,
 					(*availMeasurement).getValue,
 				)
 
-				afd := bn.measurements.classify(
+				breaks := bn.measurements.classify(
 					from, to,
 					bn.breaks,
 					bn.access,
 				)
-				calcCh <- calculation{ldc: ldc, afd: afd}
+				calcCh <- availCalculation{
+					result: res,
+					breaks: breaks,
+					ldc:    ldc,
+				}
 			}
 		}()
 	}
 
-	for _, bn := range loaded {
-		jobCh <- bn
+	var results []*availResult
+
+	interval := intervals[mode](from, to)
+
+	var breaks []float64
+
+	if useDepth {
+		breaks = depthbreaks
+	} else {
+		breaks = widthbreaks
+	}
+
+	for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() {
+
+		res := &availResult{
+			label:  label,
+			from:   pfrom,
+			to:     pto,
+			ldc:    make([]time.Duration, 2),
+			breaks: make([]time.Duration, len(breaks)+1),
+		}
+		results = append(results, res)
+
+		for _, bn := range loaded {
+			jobCh <- availJob{bn: bn, result: res}
+		}
 	}
 
 	close(jobCh)
@@ -589,61 +604,61 @@
 	close(calcCh)
 	<-done
 
-	duration := to.Sub(from) * time.Duration(len(loaded))
+	rw.Header().Add("Content-Type", "text/csv")
 
-	lnwlPercents := durationsToPercentage(duration, ldc)
-	afdPercents := durationsToPercentage(duration, afd)
+	out := csv.NewWriter(rw)
 
-	_ = lnwlPercents
-	_ = afdPercents
-
-	/* // TODO: Rewrite this
-
-	type ldcOutput struct {
-		Value float64 `json:"value"`
-		Below float64 `json:"below"`
-		Above float64 `json:"above"`
+	// label, lnwl, classes
+	record := make([]string, 1+2+len(breaks)+1)
+	record[0] = "# time"
+	record[1] = "# < LDC [%%]"
+	record[2] = "# >= LDC [%%]"
+	for i, v := range breaks {
+		if useDepth && useWidth {
+			if i == 0 {
+				record[3] = "# < break_1 [%%]"
+			}
+			record[i+4] = fmt.Sprintf("# >= break_%d [%%]", i+1)
+		} else {
+			if i == 0 {
+				record[3] = fmt.Sprintf("# < %.3f [%%]", v)
+			}
+			record[i+4] = fmt.Sprintf("# >= %.3f [%%]", v)
+		}
 	}
 
-	type intervalOutput struct {
-		From    *float64 `json:"from,omitempty"`
-		To      *float64 `json:"to,omitempty"`
-		Percent float64  `json:"percent"`
-	}
-
-	type output struct {
-		LDC []intervalOutput `json:"ldc"`
-		AFD []intervalOutput `json:"afd"`
+	if err := out.Write(record); err != nil {
+		// Too late for HTTP status message.
+		log.Printf("error: %v\n", err)
+		return
 	}
 
-	out := output{
-		LDC: []intervalOutput{
-			{To: new(float64), Percent: lnwlPercents[0]},
-			{From: new(float64), Percent: lnwlPercents[1]},
-		},
+	for _, r := range results {
+		duration := r.to.Sub(r.from) * time.Duration(len(loaded))
+
+		ldcPercents := durationsToPercentage(duration, r.ldc)
+		breaksPercents := durationsToPercentage(duration, r.breaks)
+
+		record[0] = r.label
+
+		for i, v := range ldcPercents {
+			record[2+i] = fmt.Sprintf("%.3f", v)
+		}
+
+		for i, v := range breaksPercents {
+			record[3+i] = fmt.Sprintf("%.3f", v)
+		}
+
+		if err := out.Write(record); err != nil {
+			// Too late for HTTP status message.
+			log.Printf("error: %v\n", err)
+			return
+		}
 	}
 
-	// XXX: In mixed mode the breaks are not correct!
-
-	for i, percent := range afdPercents {
-		var from, to *float64
-		switch {
-		case i == 0:
-			to = &breaks[i]
-		case i == len(afdPercents)-1:
-			from = &breaks[len(breaks)-1]
-		default:
-			from = &breaks[i-1]
-			to = &breaks[i]
-		}
-		out.AFD = append(out.AFD, intervalOutput{
-			From:    from,
-			To:      to,
-			Percent: percent,
-		})
+	out.Flush()
+	if err := out.Error(); err != nil {
+		// Too late for HTTP status message.
+		log.Printf("error: %v\n", err)
 	}
-
-	jr = JSONResult{Result: &out}
-	return
-	*/
 }