-
Notifications
You must be signed in to change notification settings - Fork 0
/
walking.go
114 lines (105 loc) · 3.77 KB
/
walking.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package export
import (
"github.com/opendroid/hk/logger"
"go.uber.org/zap"
"sort"
"strconv"
"strings"
)
// WalkingDataElement for daily StepCount and DistanceWalkingRunning
type WalkingDataElement struct {
YYYYMMDD string `json:"yyyymmdd"` // CreationDate truncated to CreationDate in format "YYYY-MM-DD"
SourceName string `json:"source"`
Unit string `json:"unit,omitempty"`
Value float32 `json:"value"`
Count int `json:"count"`
}
// WalkerData holds step count and walking orr running distance
type WalkerData struct {
StepCount []WalkingDataElement `json:"step_count"`
DistanceWalkingRunning []WalkingDataElement `json:"distance_walking_running"`
}
// WalkerData Get Walker data and send back
func (h *HealthData) WalkerData() *WalkerData {
if h == nil {
return nil
}
// Save data as sum by date
sc := make(map[string]WalkingDataElement)
dwr := make(map[string]WalkingDataElement)
skipped := make(map[string]int)
for _, r := range h.Records {
sd := string([]byte(r.StartDate)[:10])
ed := string([]byte(r.EndDate)[:10])
if sd == ed { // For now ignore multi day data
if RecordType(r.Type) == StepCount {
saveWalkElementValue(sc, &r)
} else if RecordType(r.Type) == DistanceWalkingRunning {
saveWalkElementValue(dwr, &r)
}
} else {
skipped[r.Type]++
}
}
a, _ := maps2SortedSlice(skipped)
logger.Info("WalkerData: skipping multi day data", zap.Array("skipped", a))
var wd WalkerData // Prepare data
wd.StepCount = make([]WalkingDataElement, 0) // Create underlying data slice for Steps
wd.DistanceWalkingRunning = make([]WalkingDataElement, 0)
wd.StepCount = mapToWalkingDataElementSlice(wd.StepCount, sc)
wd.DistanceWalkingRunning = mapToWalkingDataElementSlice(wd.DistanceWalkingRunning, dwr)
return &wd
}
// saveWalkElementValue adds a record 'r' to map[yyyymmdd]
func saveWalkElementValue(m map[string]WalkingDataElement, r *Record) {
date := strings.Split(r.StartDate, " ") // Extract "2019-10-23" from "2019-10-23 19:10:11 -0800"
if len(date) == 0 {
logger.Warn("saveWalkElementValue: Illegal date", zap.String("method", "saveStepCount"), zap.String("date", r.StartDate))
return
}
d := date[0] // Valid date extracted
num, err := strconv.ParseFloat(r.Value, 32) // Convert value to a number
if err != nil {
logger.Error("saveWalkElementValue: strconv error",
zap.String("method", "saveStepCount"),
zap.String("value", r.Value), zap.String("info", err.Error()))
}
// Add 'r' record to m["2019-10-23"]
v, ok := m[d]
if !ok { // New value for the day
m[d] = WalkingDataElement{d, r.SourceName, r.Unit, float32(num), 1}
return
}
// Update collected value so far
if v.Unit != r.Unit { // For now log and continue
logger.Warn("saveWalkElementValue: Non matching unit", zap.String("method", "saveStepCount"),
zap.String("found", r.Unit), zap.String("new", v.Unit))
}
if !strings.Contains(v.SourceName, r.SourceName) {
v.SourceName += ", " + r.SourceName // For now append all unique sources
}
v.Value += float32(num)
v.Count++ // How many such counts for a day
m[d] = v // Update value
}
func mapToWalkingDataElementSlice(wd []WalkingDataElement, m map[string]WalkingDataElement) []WalkingDataElement {
for _, v := range m {
wd = append(wd, v)
}
sort.Slice(wd, func(i, j int) bool { return wd[i].YYYYMMDD < wd[j].YYYYMMDD })
return wd
}
// GetRecords Get all GetRecords
func (h *HealthData) GetRecords(tag RecordType, date string) []Record {
if h == nil {
return nil
}
wd := make([]Record, 0)
for _, r := range h.Records {
if (tag == All || RecordType(r.Type) == tag) && strings.Contains(r.CreationDate, date) {
wd = append(wd, r)
}
}
sort.Slice(wd, func(i, j int) bool { return wd[i].StartDate > wd[j].StartDate })
return wd
}