/
bikage.go
122 lines (95 loc) · 2.78 KB
/
bikage.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
115
116
117
118
119
120
121
122
package bikage
import (
"errors"
"fmt"
"log"
"sort"
"strings"
"time"
"github.com/Bowbaq/distance"
)
type Bikage struct {
RouteAPI RouteAPI
TripAPI TripAPI
}
const DayFormat = "01/02/2006 EST"
func NewBikage(google_api_key string, mongo_url string) (*Bikage, error) {
stations, err := GetStations()
if err != nil {
return nil, errors.New("Bikage STATIONS GET error -> " + err.Error())
}
var cache Cache
cache, err = NewMongoCache(mongo_url)
if err != nil {
cache = NewJsonCache()
}
directions_api := distance.NewDirectionsAPI(google_api_key)
bikage := Bikage{
RouteAPI: NewRouteAPI(directions_api).WithCache(cache),
TripAPI: NewTripAPI(stations).WithCache(cache),
}
return &bikage, nil
}
func (bk *Bikage) GetTrips(username, password string) (Trips, error) {
return bk.TripAPI.GetTrips(username, password)
}
func (bk *Bikage) GetCachedTrips(username string) Trips {
return bk.TripAPI.GetCachedTrips(username)
}
func (bk *Bikage) ComputeStats(trips Trips) *Stats {
distances := bk.RouteAPI.GetAll(trips)
stats := NewStats()
for _, trip := range trips {
dist, ok := distances[trip]
if !ok {
log.Println("Bikage COULDN'T COMPUTE DISTANCE FOR ROUTE error, trip:", trip)
continue
}
day := trip.StartedAt.Format(DayFormat)
if day_distance_total, ok := stats.DailyDistanceTotal[day]; ok {
stats.DailyDistanceTotal[day] = day_distance_total + dist
} else {
stats.DailyDistanceTotal[day] = dist
}
if day_speed_total, ok := stats.DailySpeedTotal[day]; ok {
stats.DailySpeedTotal[day] = day_speed_total + float64(dist)/trip.Duration().Hours()
} else {
stats.DailySpeedTotal[day] = float64(dist) / trip.Duration().Hours()
}
stats.Total += dist
stats.TotalTime += trip.Duration()
}
stats.AvgSpeed = stats.TotalKm() / stats.TotalTime.Hours()
return stats
}
type Stats struct {
Total uint64
TotalTime time.Duration
DailyDistanceTotal map[string]uint64
DailySpeedTotal map[string]float64
AvgSpeed float64
}
func NewStats() *Stats {
return &Stats{DailyDistanceTotal: make(map[string]uint64), DailySpeedTotal: make(map[string]float64)}
}
func (s *Stats) TotalKm() float64 {
return km_dist(s.Total)
}
func km_dist(dist uint64) float64 {
return float64(dist) / 1000
}
func (s *Stats) TotalMi() float64 {
return mi_dist(s.Total)
}
func mi_dist(dist uint64) float64 {
return float64(dist) / 1609.34
}
func (s Stats) String() string {
summaries := make([]string, 0)
for day, dist := range s.DailyDistanceTotal {
summary := fmt.Sprintf(" %s %.1f km (%.1f mi)", day, km_dist(dist), mi_dist(dist))
summaries = append(summaries, summary)
}
sort.Sort(sort.Reverse(sort.StringSlice(summaries)))
return fmt.Sprintf("Total:\n %.1f km (%.1f mi)\nDetails:\n%s", s.TotalKm(), s.TotalMi(), strings.Join(summaries, "\n"))
}