fix trend calculation if days are missing by linear interpolation

This commit is contained in:
gutmet 2020-06-14 11:06:20 +02:00
parent 79282317b1
commit b1c5b729d1

View File

@ -102,14 +102,21 @@ type yRange struct {
max float64
}
func process(file string) ([]point, error) {
func readFile(file string) (map[time.Time]float64, time.Time, time.Time) {
optPanic := func(err error) {
if err != nil {
panic("process:" + err.Error())
}
}
weights, err := goutil.ReadFile(file)
if err != nil {
return nil, err
return nil, time.Time{}, time.Time{}
}
lines := strings.Split(weights, "\n")
trend := 0.0
s := []point{}
var firstDay time.Time
firstDayIsSet := false
var lastDay time.Time
dateToWeight := make(map[time.Time]float64)
for _, line := range lines {
line = strings.TrimSpace(line)
if len(line) == 0 {
@ -117,18 +124,58 @@ func process(file string) ([]point, error) {
}
splitline := strings.Split(line, "\t")
if len(splitline) < 2 {
return nil, errors.New("process - invalid line in weight file: " + line)
optPanic(errors.New("invalid line in weight file " + file + ": " + line))
}
date, err := time.Parse(dateLayout, splitline[0])
optPanic(err)
weight, err := strconv.ParseFloat(splitline[1], 64)
optPanic(err)
if !firstDayIsSet {
firstDay = date
firstDayIsSet = true
}
lastDay = date
dateToWeight[date] = weight
}
return dateToWeight, firstDay, lastDay
}
func process(file string) []point {
dateToWeight, firstDay, lastDay := readFile(file)
if dateToWeight == nil {
return []point{}
}
trend := 0.0
points := []point{}
for d := firstDay; d.Before(lastDay) || d == lastDay; d = d.AddDate(0, 0, 1) {
var weight float64
if w, ok := dateToWeight[d]; ok {
weight = w
} else {
prevPoint := points[len(points)-1]
prevDay := prevPoint.date
prevWeight := prevPoint.weight
var nextWeight float64
var nextDay time.Time
for nextDay = d.AddDate(0, 0, 1); nextDay.Before(lastDay) || nextDay == lastDay; nextDay = nextDay.AddDate(0, 0, 1) {
if w, ok := dateToWeight[nextDay]; ok {
nextWeight = w
break
}
}
days := nextDay.Sub(prevDay).Hours() / 24
changePerDay := (nextWeight - prevWeight) / days
weight = prevWeight + changePerDay
}
date, _ := time.Parse(dateLayout, splitline[0])
weight, _ := strconv.ParseFloat(splitline[1], 64)
if trend == 0.0 {
trend = weight
} else {
trend = math.Floor((trend+0.1*(weight-trend))*100) / 100
}
s = append(s, point{date, weight, trend})
points = append(points, point{d, weight, trend})
}
return s, nil
return points
}
type showFlags struct {
@ -138,11 +185,8 @@ type showFlags struct {
}
func show(args showFlags) error {
points, err := process(logfile)
if err != nil {
return err
}
dietpoints, _ := process(dietfile)
points := process(logfile)
dietpoints := process(dietfile)
var to time.Time
var from time.Time
if args.to.date != nil {
@ -170,7 +214,7 @@ func show(args showFlags) error {
YRange.min = math.Floor(YRange.min) - 1.0
YRange.max = math.Ceil(YRange.max) + 1.0
graph := graph(xticks(xRange), s1, s2, s3, YRange)
err = writeGraphToFile(graph)
err := writeGraphToFile(graph)
if err != nil {
return err
}
@ -194,7 +238,7 @@ func showCommand() (goutil.CommandFlagsInit, goutil.CommandFunc) {
func writeDietFile(from time.Time, initial float64, goal float64, change float64) error {
date := from
weight := initial
s := "#Date\tTarget\n"
s := ""
s += fmt.Sprintf("%s\t%.1f\n", date.Format(dateLayout), weight)
for weight > goal {
date = date.AddDate(0, 0, 1)