1
0
mirror of https://github.com/muety/wakapi.git synced 2023-08-10 21:12:56 +03:00

fix: include machine names when importing wakatime data

This commit is contained in:
Ferdinand Mütsch 2021-02-10 22:08:00 +01:00
parent 66b01c2797
commit e4c413a33c
5 changed files with 73 additions and 20 deletions

View File

@ -39,6 +39,7 @@ const (
WakatimeApiHeartbeatsUrl = "/users/current/heartbeats" WakatimeApiHeartbeatsUrl = "/users/current/heartbeats"
WakatimeApiHeartbeatsBulkUrl = "/users/current/heartbeats.bulk" WakatimeApiHeartbeatsBulkUrl = "/users/current/heartbeats.bulk"
WakatimeApiUserAgentsUrl = "/users/current/user_agents" WakatimeApiUserAgentsUrl = "/users/current/user_agents"
WakatimeApiMachineNamesUrl = "/users/current/machine_names"
) )
var cfg *Config var cfg *Config

View File

@ -16,6 +16,15 @@ type AllTimeData struct {
TotalSeconds float32 `json:"total_seconds"` // total number of seconds logged since account created 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> 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> 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 { func NewAllTimeFrom(summary *models.Summary, filters *models.Filters) *AllTimeViewModel {

View File

@ -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"`
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/emvi/logbuch" "github.com/emvi/logbuch"
"github.com/muety/wakapi/config" "github.com/muety/wakapi/config"
@ -53,6 +52,12 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time,
return 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) days := generateDays(startDate, endDate)
c := atomic.NewUint32(uint32(len(days))) c := atomic.NewUint32(uint32(len(days)))
@ -75,7 +80,7 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time,
} }
for _, h := range heartbeats { for _, h := range heartbeats {
out <- mapHeartbeat(h, userAgents, user) out <- mapHeartbeat(h, userAgents, machinesNames, user)
} }
if c.Dec() == 0 { if c.Dec() == 0 {
@ -134,27 +139,17 @@ func (w *WakatimeHeartbeatImporter) fetchRange() (time.Time, time.Time, error) {
return notime, notime, err return notime, notime, err
} }
var allTimeData map[string]interface{} var allTimeData wakatime.AllTimeViewModel
if err := json.NewDecoder(res.Body).Decode(&allTimeData); err != nil { if err := json.NewDecoder(res.Body).Decode(&allTimeData); err != nil {
return notime, notime, err return notime, notime, err
} }
data := allTimeData["data"].(map[string]interface{}) startDate, err := time.Parse("2006-01-02", allTimeData.Data.Range.StartDate)
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))
if err != nil { if err != nil {
return notime, notime, err 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 { if err != nil {
return notime, notime, err return notime, notime, err
} }
@ -189,6 +184,33 @@ func (w *WakatimeHeartbeatImporter) fetchUserAgents() (map[string]*wakatime.User
return userAgents, nil 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 { func (w *WakatimeHeartbeatImporter) withHeaders(req *http.Request) *http.Request {
req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(w.ApiKey)))) req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(w.ApiKey))))
return req return req
@ -197,6 +219,7 @@ func (w *WakatimeHeartbeatImporter) withHeaders(req *http.Request) *http.Request
func mapHeartbeat( func mapHeartbeat(
entry *wakatime.HeartbeatEntry, entry *wakatime.HeartbeatEntry,
userAgents map[string]*wakatime.UserAgentEntry, userAgents map[string]*wakatime.UserAgentEntry,
machineNames map[string]*wakatime.MachineEntry,
user *models.User, user *models.User,
) *models.Heartbeat { ) *models.Heartbeat {
ua := userAgents[entry.UserAgentId] 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{ return (&models.Heartbeat{
User: user, User: user,
UserID: user.ID, UserID: user.ID,
@ -219,7 +250,7 @@ func mapHeartbeat(
IsWrite: entry.IsWrite, IsWrite: entry.IsWrite,
Editor: ua.Editor, Editor: ua.Editor,
OperatingSystem: ua.Os, OperatingSystem: ua.Os,
Machine: entry.MachineNameId, // TODO Machine: ma.Value,
Time: entry.Time, Time: entry.Time,
Origin: OriginWakatime, Origin: OriginWakatime,
OriginId: entry.Id, OriginId: entry.Id,

View File

@ -1 +1 @@
1.23.4 1.23.5