2020-09-12 00:24:51 +03:00
|
|
|
package v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"github.com/muety/wakapi/models"
|
|
|
|
"github.com/muety/wakapi/utils"
|
|
|
|
"math"
|
2020-09-12 01:20:16 +03:00
|
|
|
"sync"
|
2020-09-12 00:24:51 +03:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// https://wakatime.com/developers#summaries
|
|
|
|
// https://pastr.de/v/736450
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
type SummariesViewModel struct {
|
|
|
|
Data []*summariesData `json:"data"`
|
|
|
|
End time.Time `json:"end"`
|
|
|
|
Start time.Time `json:"start"`
|
2020-09-12 00:24:51 +03:00
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
type summariesData struct {
|
|
|
|
Categories []*summariesEntry `json:"categories"`
|
|
|
|
Dependencies []*summariesEntry `json:"dependencies"`
|
|
|
|
Editors []*summariesEntry `json:"editors"`
|
|
|
|
Languages []*summariesEntry `json:"languages"`
|
|
|
|
Machines []*summariesEntry `json:"machines"`
|
|
|
|
OperatingSystems []*summariesEntry `json:"operating_systems"`
|
|
|
|
Projects []*summariesEntry `json:"projects"`
|
|
|
|
GrandTotal *summariesGrandTotal `json:"grand_total"`
|
|
|
|
Range *summariesRange `json:"range"`
|
2020-09-12 00:24:51 +03:00
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
type summariesEntry struct {
|
2020-09-12 00:24:51 +03:00
|
|
|
Digital string `json:"digital"`
|
|
|
|
Hours int `json:"hours"`
|
|
|
|
Minutes int `json:"minutes"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Percent float64 `json:"percent"`
|
|
|
|
Seconds int `json:"seconds"`
|
|
|
|
Text string `json:"text"`
|
|
|
|
TotalSeconds float64 `json:"total_seconds"`
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
type summariesGrandTotal struct {
|
2020-09-12 00:24:51 +03:00
|
|
|
Digital string `json:"digital"`
|
|
|
|
Hours int `json:"hours"`
|
|
|
|
Minutes int `json:"minutes"`
|
|
|
|
Text string `json:"text"`
|
|
|
|
TotalSeconds float64 `json:"total_seconds"`
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
type summariesRange struct {
|
2020-09-12 00:24:51 +03:00
|
|
|
Date string `json:"date"`
|
|
|
|
End time.Time `json:"end"`
|
|
|
|
Start time.Time `json:"start"`
|
|
|
|
Text string `json:"text"`
|
|
|
|
Timezone string `json:"timezone"`
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
func NewSummariesFrom(summaries []*models.Summary, filters *models.Filters) *SummariesViewModel {
|
|
|
|
data := make([]*summariesData, len(summaries))
|
2020-09-12 00:24:51 +03:00
|
|
|
minDate, maxDate := time.Now().Add(1*time.Second), time.Time{}
|
|
|
|
|
|
|
|
for i, s := range summaries {
|
|
|
|
data[i] = newDataFrom(s)
|
|
|
|
|
2020-10-16 15:49:22 +03:00
|
|
|
if s.FromTime.T().Before(minDate) {
|
|
|
|
minDate = s.FromTime.T()
|
2020-09-12 00:24:51 +03:00
|
|
|
}
|
2020-10-16 15:49:22 +03:00
|
|
|
if s.ToTime.T().After(maxDate) {
|
|
|
|
maxDate = s.ToTime.T()
|
2020-09-12 00:24:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
return &SummariesViewModel{
|
2020-09-12 00:24:51 +03:00
|
|
|
Data: data,
|
|
|
|
End: maxDate,
|
|
|
|
Start: minDate,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
func newDataFrom(s *models.Summary) *summariesData {
|
2020-09-12 00:24:51 +03:00
|
|
|
zone, _ := time.Now().Zone()
|
|
|
|
total := s.TotalTime()
|
|
|
|
totalHrs, totalMins := int(total.Hours()), int((total - time.Duration(total.Hours())*time.Hour).Minutes())
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
data := &summariesData{
|
|
|
|
Categories: make([]*summariesEntry, 0),
|
|
|
|
Dependencies: make([]*summariesEntry, 0),
|
|
|
|
Editors: make([]*summariesEntry, len(s.Editors)),
|
|
|
|
Languages: make([]*summariesEntry, len(s.Languages)),
|
|
|
|
Machines: make([]*summariesEntry, len(s.Machines)),
|
|
|
|
OperatingSystems: make([]*summariesEntry, len(s.OperatingSystems)),
|
|
|
|
Projects: make([]*summariesEntry, len(s.Projects)),
|
|
|
|
GrandTotal: &summariesGrandTotal{
|
2020-09-12 00:24:51 +03:00
|
|
|
Digital: fmt.Sprintf("%d:%d", totalHrs, totalMins),
|
|
|
|
Hours: totalHrs,
|
|
|
|
Minutes: totalMins,
|
|
|
|
Text: utils.FmtWakatimeDuration(total),
|
|
|
|
TotalSeconds: total.Seconds(),
|
|
|
|
},
|
2020-09-12 17:09:23 +03:00
|
|
|
Range: &summariesRange{
|
2020-09-12 00:24:51 +03:00
|
|
|
Date: time.Now().Format(time.RFC3339),
|
2020-10-16 15:49:22 +03:00
|
|
|
End: s.ToTime.T(),
|
|
|
|
Start: s.FromTime.T(),
|
2020-09-12 00:24:51 +03:00
|
|
|
Text: "",
|
|
|
|
Timezone: zone,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-09-12 01:20:16 +03:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(5)
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
go func(data *summariesData) {
|
2020-09-12 01:20:16 +03:00
|
|
|
defer wg.Done()
|
|
|
|
for i, e := range s.Projects {
|
|
|
|
data.Projects[i] = convertEntry(e, s.TotalTimeBy(models.SummaryProject))
|
|
|
|
}
|
|
|
|
}(data)
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
go func(data *summariesData) {
|
2020-09-12 01:20:16 +03:00
|
|
|
defer wg.Done()
|
|
|
|
for i, e := range s.Editors {
|
|
|
|
data.Editors[i] = convertEntry(e, s.TotalTimeBy(models.SummaryEditor))
|
|
|
|
}
|
|
|
|
}(data)
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
go func(data *summariesData) {
|
2020-09-12 01:20:16 +03:00
|
|
|
defer wg.Done()
|
|
|
|
for i, e := range s.Languages {
|
|
|
|
data.Languages[i] = convertEntry(e, s.TotalTimeBy(models.SummaryLanguage))
|
|
|
|
|
|
|
|
}
|
|
|
|
}(data)
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
go func(data *summariesData) {
|
2020-09-12 01:20:16 +03:00
|
|
|
defer wg.Done()
|
|
|
|
for i, e := range s.OperatingSystems {
|
|
|
|
data.OperatingSystems[i] = convertEntry(e, s.TotalTimeBy(models.SummaryOS))
|
|
|
|
}
|
|
|
|
}(data)
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
go func(data *summariesData) {
|
2020-09-12 01:20:16 +03:00
|
|
|
defer wg.Done()
|
|
|
|
for i, e := range s.Machines {
|
|
|
|
data.Machines[i] = convertEntry(e, s.TotalTimeBy(models.SummaryMachine))
|
|
|
|
}
|
|
|
|
}(data)
|
2020-09-12 00:24:51 +03:00
|
|
|
|
2020-09-12 01:20:16 +03:00
|
|
|
wg.Wait()
|
2020-09-12 00:24:51 +03:00
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
func convertEntry(e *models.SummaryItem, entityTotal time.Duration) *summariesEntry {
|
2020-09-12 00:24:51 +03:00
|
|
|
// this is a workaround, since currently, the total time of a summary item is mistakenly represented in seconds
|
|
|
|
// TODO: fix some day, while migrating persisted summary items
|
|
|
|
total := e.Total * time.Second
|
|
|
|
hrs := int(total.Hours())
|
|
|
|
mins := int((total - time.Duration(hrs)*time.Hour).Minutes())
|
|
|
|
secs := int((total - time.Duration(hrs)*time.Hour - time.Duration(mins)*time.Minute).Seconds())
|
2020-10-16 16:16:43 +03:00
|
|
|
percentage := math.Round((total.Seconds()/entityTotal.Seconds())*1e4) / 100
|
|
|
|
if math.IsNaN(percentage) || math.IsInf(percentage, 0) {
|
|
|
|
percentage = 0
|
|
|
|
}
|
2020-09-12 00:24:51 +03:00
|
|
|
|
2020-09-12 17:09:23 +03:00
|
|
|
return &summariesEntry{
|
2020-09-12 00:24:51 +03:00
|
|
|
Digital: fmt.Sprintf("%d:%d:%d", hrs, mins, secs),
|
|
|
|
Hours: hrs,
|
|
|
|
Minutes: mins,
|
|
|
|
Name: e.Key,
|
2020-10-16 16:16:43 +03:00
|
|
|
Percent: percentage,
|
2020-09-12 00:24:51 +03:00
|
|
|
Seconds: secs,
|
|
|
|
Text: utils.FmtWakatimeDuration(total),
|
|
|
|
TotalSeconds: total.Seconds(),
|
|
|
|
}
|
|
|
|
}
|