package chart import ( "fmt" "html/template" "strings" "time" "powerwatts/internal/store" ) func FormatGermanNumber(n int) string { s := fmt.Sprintf("%d", n) if len(s) <= 3 { return s } neg := s[0] == '-' if neg { s = s[1:] } var b strings.Builder if neg { b.WriteByte('-') } rem := len(s) % 3 if rem == 0 { rem = 3 } b.WriteString(s[:rem]) for i := rem; i < len(s); i += 3 { b.WriteByte('.') b.WriteString(s[i : i+3]) } return b.String() } func BuildHistoryChart(history []store.Sample, maxWatts int) template.HTML { const ( width = 640 height = 220 padL = 72 padR = 18 padT = 18 padB = 42 ) if len(history) == 0 { return template.HTML(`No power history yet`) } minV, maxV := 0, history[0].Value if maxWatts > 0 { maxV = maxWatts } start := history[0].At end := history[len(history)-1].At for _, point := range history[1:] { if maxWatts <= 0 && point.Value > maxV { maxV = point.Value } if point.At.Before(start) { start = point.At } if point.At.After(end) { end = point.At } } if maxV <= 0 { maxV = 1 } span := end.Sub(start) if span <= 0 { span = time.Minute } plotW := float64(width - padL - padR) plotH := float64(height - padT - padB) points := make([]string, 0, len(history)) var lastX, lastY float64 for _, point := range history { x := float64(padL) + plotW*point.At.Sub(start).Seconds()/span.Seconds() y := float64(height-padB) - plotH*(float64(point.Value-minV)/float64(maxV-minV)) if y < float64(padT) { y = float64(padT) } if y > float64(height-padB) { y = float64(height - padB) } points = append(points, fmt.Sprintf("%.1f,%.1f", x, y)) lastX, lastY = x, y } midV := minV + (maxV-minV)/2 midT := start.Add(span / 2) x0 := float64(padL) x1 := float64(width - padR) yTop := float64(padT) yMid := float64(padT) + plotH/2 yBottom := float64(height - padB) var b strings.Builder b.WriteString(``) b.WriteString(``) b.WriteString(``) fmt.Fprintf(&b, ``, x0, yTop, x1) fmt.Fprintf(&b, ``, x0, yMid, x1) fmt.Fprintf(&b, ``, x0, yBottom, x1) fmt.Fprintf(&b, ``, x0, yTop, yBottom) fmt.Fprintf(&b, ``, x0+(plotW/2), yTop, yBottom) fmt.Fprintf(&b, ``, x1, yTop, yBottom) b.WriteString(``) b.WriteString(``) fmt.Fprintf(&b, `%s W`, x0-10, int(yTop)+14, FormatGermanNumber(maxV)) fmt.Fprintf(&b, `%s W`, x0-10, int(yMid)+14, FormatGermanNumber(midV)) fmt.Fprintf(&b, `0 W`, x0-10, int(yBottom)+14) fmt.Fprintf(&b, `%s`, x0, int(height)-10, start.Format("15:04")) fmt.Fprintf(&b, `%s`, x0+(plotW/2), int(height)-10, midT.Format("15:04")) fmt.Fprintf(&b, `%s`, x1, int(height)-10, end.Format("15:04")) b.WriteString(``) fmt.Fprintf(&b, ``, strings.Join(points, " ")) fmt.Fprintf(&b, ``, lastX, lastY) b.WriteString(``) return template.HTML(b.String()) }