From e4c413a33c9466bb9eabf90ac2d37f89b240ccb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferdinand=20M=C3=BCtsch?= Date: Wed, 10 Feb 2021 22:08:00 +0100 Subject: [PATCH] fix: include machine names when importing wakatime data --- config/config.go | 1 + models/compat/wakatime/v1/all_time.go | 15 +++++-- models/compat/wakatime/v1/machine.go | 12 +++++ services/imports/wakatime.go | 63 ++++++++++++++++++++------- version.txt | 2 +- 5 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 models/compat/wakatime/v1/machine.go diff --git a/config/config.go b/config/config.go index cabba17..25999d6 100644 --- a/config/config.go +++ b/config/config.go @@ -39,6 +39,7 @@ const ( WakatimeApiHeartbeatsUrl = "/users/current/heartbeats" WakatimeApiHeartbeatsBulkUrl = "/users/current/heartbeats.bulk" WakatimeApiUserAgentsUrl = "/users/current/user_agents" + WakatimeApiMachineNamesUrl = "/users/current/machine_names" ) var cfg *Config diff --git a/models/compat/wakatime/v1/all_time.go b/models/compat/wakatime/v1/all_time.go index b2444c1..11704a3 100644 --- a/models/compat/wakatime/v1/all_time.go +++ b/models/compat/wakatime/v1/all_time.go @@ -13,9 +13,18 @@ type AllTimeViewModel struct { } type AllTimeData struct { - TotalSeconds float32 `json:"total_seconds"` // total number of seconds logged since account created - Text string `json:"text"` // total time logged since account created as human readable string> - IsUpToDate bool `json:"is_up_to_date"` // true if the stats are up to date; when false, a 202 response code is returned and stats will be refreshed soon> + TotalSeconds float32 `json:"total_seconds"` // total number of seconds logged since account created + Text string `json:"text"` // total time logged since account created as human readable string> + IsUpToDate bool `json:"is_up_to_date"` // true if the stats are up to date; when false, a 202 response code is returned and stats will be refreshed soon> + Range *AllTimeRange `json:"range"` +} + +type AllTimeRange struct { + End string `json:"end"` + EndDate string `json:"end_date"` + Start string `json:"start"` + StartDate string `json:"start_date"` + Timezone string `json:"timezone"` } func NewAllTimeFrom(summary *models.Summary, filters *models.Filters) *AllTimeViewModel { diff --git a/models/compat/wakatime/v1/machine.go b/models/compat/wakatime/v1/machine.go new file mode 100644 index 0000000..bfdc68a --- /dev/null +++ b/models/compat/wakatime/v1/machine.go @@ -0,0 +1,12 @@ +package v1 + +// https://wakatime.com/api/v1/users/current/machine_names + +type MachineViewModel struct { + Data []*MachineEntry `json:"data"` +} + +type MachineEntry struct { + Id string `json:"id"` + Value string `json:"value"` +} diff --git a/services/imports/wakatime.go b/services/imports/wakatime.go index 63b5e87..39871c8 100644 --- a/services/imports/wakatime.go +++ b/services/imports/wakatime.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "encoding/json" - "errors" "fmt" "github.com/emvi/logbuch" "github.com/muety/wakapi/config" @@ -53,6 +52,12 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time, return } + machinesNames, err := w.fetchMachineNames() + if err != nil { + logbuch.Error("failed to fetch machine names while importing wakatime heartbeats for user '%s' – %v", user.ID, err) + return + } + days := generateDays(startDate, endDate) c := atomic.NewUint32(uint32(len(days))) @@ -75,7 +80,7 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time, } for _, h := range heartbeats { - out <- mapHeartbeat(h, userAgents, user) + out <- mapHeartbeat(h, userAgents, machinesNames, user) } if c.Dec() == 0 { @@ -134,27 +139,17 @@ func (w *WakatimeHeartbeatImporter) fetchRange() (time.Time, time.Time, error) { return notime, notime, err } - var allTimeData map[string]interface{} + var allTimeData wakatime.AllTimeViewModel if err := json.NewDecoder(res.Body).Decode(&allTimeData); err != nil { return notime, notime, err } - data := allTimeData["data"].(map[string]interface{}) - if data == nil { - return notime, notime, errors.New("invalid response") - } - - dataRange := data["range"].(map[string]interface{}) - if dataRange == nil { - return notime, notime, errors.New("invalid response") - } - - startDate, err := time.Parse("2006-01-02", dataRange["start_date"].(string)) + startDate, err := time.Parse("2006-01-02", allTimeData.Data.Range.StartDate) if err != nil { return notime, notime, err } - endDate, err := time.Parse("2006-01-02", dataRange["end_date"].(string)) + endDate, err := time.Parse("2006-01-02", allTimeData.Data.Range.EndDate) if err != nil { return notime, notime, err } @@ -189,6 +184,33 @@ func (w *WakatimeHeartbeatImporter) fetchUserAgents() (map[string]*wakatime.User return userAgents, nil } +// https://wakatime.com/api/v1/users/current/machine_names +func (w *WakatimeHeartbeatImporter) fetchMachineNames() (map[string]*wakatime.MachineEntry, error) { + httpClient := &http.Client{Timeout: 10 * time.Second} + + req, err := http.NewRequest(http.MethodGet, config.WakatimeApiUrl+config.WakatimeApiMachineNamesUrl, nil) + if err != nil { + return nil, err + } + + res, err := httpClient.Do(w.withHeaders(req)) + if err != nil { + return nil, err + } + + var machineData wakatime.MachineViewModel + if err := json.NewDecoder(res.Body).Decode(&machineData); err != nil { + return nil, err + } + + machines := make(map[string]*wakatime.MachineEntry) + for _, ma := range machineData.Data { + machines[ma.Id] = ma + } + + return machines, nil +} + func (w *WakatimeHeartbeatImporter) withHeaders(req *http.Request) *http.Request { req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(w.ApiKey)))) return req @@ -197,6 +219,7 @@ func (w *WakatimeHeartbeatImporter) withHeaders(req *http.Request) *http.Request func mapHeartbeat( entry *wakatime.HeartbeatEntry, userAgents map[string]*wakatime.UserAgentEntry, + machineNames map[string]*wakatime.MachineEntry, user *models.User, ) *models.Heartbeat { ua := userAgents[entry.UserAgentId] @@ -207,6 +230,14 @@ func mapHeartbeat( } } + ma := machineNames[entry.MachineNameId] + if ma == nil { + ma = &wakatime.MachineEntry{ + Id: entry.MachineNameId, + Value: entry.MachineNameId, + } + } + return (&models.Heartbeat{ User: user, UserID: user.ID, @@ -219,7 +250,7 @@ func mapHeartbeat( IsWrite: entry.IsWrite, Editor: ua.Editor, OperatingSystem: ua.Os, - Machine: entry.MachineNameId, // TODO + Machine: ma.Value, Time: entry.Time, Origin: OriginWakatime, OriginId: entry.Id, diff --git a/version.txt b/version.txt index 8cb0745..c7b2146 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.23.4 \ No newline at end of file +1.23.5 \ No newline at end of file