mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
feat: basic implementation of branch statistics
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@ type StatsData struct {
|
|||||||
Machines []*SummariesEntry `json:"machines"`
|
Machines []*SummariesEntry `json:"machines"`
|
||||||
Projects []*SummariesEntry `json:"projects"`
|
Projects []*SummariesEntry `json:"projects"`
|
||||||
OperatingSystems []*SummariesEntry `json:"operating_systems"`
|
OperatingSystems []*SummariesEntry `json:"operating_systems"`
|
||||||
|
Branches []*SummariesEntry `json:"branches"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStatsFrom(summary *models.Summary, filters *models.Filters) *StatsViewModel {
|
func NewStatsFrom(summary *models.Summary, filters *models.Filters) *StatsViewModel {
|
||||||
@ -71,11 +72,21 @@ func NewStatsFrom(summary *models.Summary, filters *models.Filters) *StatsViewMo
|
|||||||
oss[i] = convertEntry(e, summary.TotalTimeBy(models.SummaryOS))
|
oss[i] = convertEntry(e, summary.TotalTimeBy(models.SummaryOS))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
branches := make([]*SummariesEntry, len(summary.Branches))
|
||||||
|
for i, e := range summary.Branches {
|
||||||
|
branches[i] = convertEntry(e, summary.TotalTimeBy(models.SummaryBranch))
|
||||||
|
}
|
||||||
|
|
||||||
data.Editors = editors
|
data.Editors = editors
|
||||||
data.Languages = languages
|
data.Languages = languages
|
||||||
data.Machines = machines
|
data.Machines = machines
|
||||||
data.Projects = projects
|
data.Projects = projects
|
||||||
data.OperatingSystems = oss
|
data.OperatingSystems = oss
|
||||||
|
data.Branches = branches
|
||||||
|
|
||||||
|
if summary.Branches == nil {
|
||||||
|
data.Branches = nil
|
||||||
|
}
|
||||||
|
|
||||||
return &StatsViewModel{
|
return &StatsViewModel{
|
||||||
Data: data,
|
Data: data,
|
||||||
|
@ -26,6 +26,7 @@ type SummariesData struct {
|
|||||||
Machines []*SummariesEntry `json:"machines"`
|
Machines []*SummariesEntry `json:"machines"`
|
||||||
OperatingSystems []*SummariesEntry `json:"operating_systems"`
|
OperatingSystems []*SummariesEntry `json:"operating_systems"`
|
||||||
Projects []*SummariesEntry `json:"projects"`
|
Projects []*SummariesEntry `json:"projects"`
|
||||||
|
Branches []*SummariesEntry `json:"branches"`
|
||||||
GrandTotal *SummariesGrandTotal `json:"grand_total"`
|
GrandTotal *SummariesGrandTotal `json:"grand_total"`
|
||||||
Range *SummariesRange `json:"range"`
|
Range *SummariesRange `json:"range"`
|
||||||
}
|
}
|
||||||
@ -92,6 +93,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
|
|||||||
Machines: make([]*SummariesEntry, len(s.Machines)),
|
Machines: make([]*SummariesEntry, len(s.Machines)),
|
||||||
OperatingSystems: make([]*SummariesEntry, len(s.OperatingSystems)),
|
OperatingSystems: make([]*SummariesEntry, len(s.OperatingSystems)),
|
||||||
Projects: make([]*SummariesEntry, len(s.Projects)),
|
Projects: make([]*SummariesEntry, len(s.Projects)),
|
||||||
|
Branches: make([]*SummariesEntry, len(s.Branches)),
|
||||||
GrandTotal: &SummariesGrandTotal{
|
GrandTotal: &SummariesGrandTotal{
|
||||||
Digital: fmt.Sprintf("%d:%d", totalHrs, totalMins),
|
Digital: fmt.Sprintf("%d:%d", totalHrs, totalMins),
|
||||||
Hours: totalHrs,
|
Hours: totalHrs,
|
||||||
@ -109,7 +111,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(5)
|
wg.Add(6)
|
||||||
|
|
||||||
go func(data *SummariesData) {
|
go func(data *SummariesData) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
@ -146,6 +148,17 @@ func newDataFrom(s *models.Summary) *SummariesData {
|
|||||||
}
|
}
|
||||||
}(data)
|
}(data)
|
||||||
|
|
||||||
|
go func(data *SummariesData) {
|
||||||
|
defer wg.Done()
|
||||||
|
for i, e := range s.Branches {
|
||||||
|
data.Branches[i] = convertEntry(e, s.TotalTimeBy(models.SummaryBranch))
|
||||||
|
}
|
||||||
|
}(data)
|
||||||
|
|
||||||
|
if s.Branches == nil {
|
||||||
|
data.Branches = nil
|
||||||
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ type Duration struct {
|
|||||||
Editor string `json:"editor"`
|
Editor string `json:"editor"`
|
||||||
OperatingSystem string `json:"operating_system"`
|
OperatingSystem string `json:"operating_system"`
|
||||||
Machine string `json:"machine"`
|
Machine string `json:"machine"`
|
||||||
|
Branch string `json:"branch"`
|
||||||
NumHeartbeats int `json:"-" hash:"ignore"`
|
NumHeartbeats int `json:"-" hash:"ignore"`
|
||||||
GroupHash string `json:"-" hash:"ignore"`
|
GroupHash string `json:"-" hash:"ignore"`
|
||||||
}
|
}
|
||||||
@ -30,6 +31,7 @@ func NewDurationFromHeartbeat(h *Heartbeat) *Duration {
|
|||||||
Editor: h.Editor,
|
Editor: h.Editor,
|
||||||
OperatingSystem: h.OperatingSystem,
|
OperatingSystem: h.OperatingSystem,
|
||||||
Machine: h.Machine,
|
Machine: h.Machine,
|
||||||
|
Branch: h.Branch,
|
||||||
NumHeartbeats: 1,
|
NumHeartbeats: 1,
|
||||||
}
|
}
|
||||||
return d.Hashed()
|
return d.Hashed()
|
||||||
@ -56,6 +58,8 @@ func (d *Duration) GetKey(t uint8) (key string) {
|
|||||||
key = d.OperatingSystem
|
key = d.OperatingSystem
|
||||||
case SummaryMachine:
|
case SummaryMachine:
|
||||||
key = d.Machine
|
key = d.Machine
|
||||||
|
case SummaryBranch:
|
||||||
|
key = d.Branch
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
|
@ -13,6 +13,7 @@ type Filters struct {
|
|||||||
Editor OrFilter
|
Editor OrFilter
|
||||||
Machine OrFilter
|
Machine OrFilter
|
||||||
Label OrFilter
|
Label OrFilter
|
||||||
|
Branch OrFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrFilter []string
|
type OrFilter []string
|
||||||
@ -62,6 +63,8 @@ func (f *Filters) WithMultiple(entity uint8, keys []string) *Filters {
|
|||||||
f.Machine = append(f.Machine, keys...)
|
f.Machine = append(f.Machine, keys...)
|
||||||
case SummaryLabel:
|
case SummaryLabel:
|
||||||
f.Label = append(f.Label, keys...)
|
f.Label = append(f.Label, keys...)
|
||||||
|
case SummaryBranch:
|
||||||
|
f.Branch = append(f.Branch, keys...)
|
||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
@ -79,6 +82,8 @@ func (f *Filters) One() (bool, uint8, OrFilter) {
|
|||||||
return true, SummaryMachine, f.Machine
|
return true, SummaryMachine, f.Machine
|
||||||
} else if f.Label != nil && f.Label.Exists() {
|
} else if f.Label != nil && f.Label.Exists() {
|
||||||
return true, SummaryLabel, f.Label
|
return true, SummaryLabel, f.Label
|
||||||
|
} else if f.Branch != nil && f.Branch.Exists() {
|
||||||
|
return true, SummaryBranch, f.Branch
|
||||||
}
|
}
|
||||||
return false, 0, OrFilter{}
|
return false, 0, OrFilter{}
|
||||||
}
|
}
|
||||||
@ -153,6 +158,14 @@ func (f *Filters) WithAliases(resolve AliasReverseResolver) *Filters {
|
|||||||
}
|
}
|
||||||
f.Machine = updated
|
f.Machine = updated
|
||||||
}
|
}
|
||||||
|
if f.Branch != nil {
|
||||||
|
updated := OrFilter(make([]string, 0, len(f.Branch)))
|
||||||
|
for _, e := range f.Branch {
|
||||||
|
updated = append(updated, e)
|
||||||
|
updated = append(updated, resolve(SummaryBranch, e)...)
|
||||||
|
}
|
||||||
|
f.Branch = updated
|
||||||
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,8 @@ func (h *Heartbeat) GetKey(t uint8) (key string) {
|
|||||||
key = h.OperatingSystem
|
key = h.OperatingSystem
|
||||||
case SummaryMachine:
|
case SummaryMachine:
|
||||||
key = h.Machine
|
key = h.Machine
|
||||||
|
case SummaryBranch:
|
||||||
|
key = h.Branch
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
|
@ -14,6 +14,7 @@ const (
|
|||||||
SummaryOS uint8 = 3
|
SummaryOS uint8 = 3
|
||||||
SummaryMachine uint8 = 4
|
SummaryMachine uint8 = 4
|
||||||
SummaryLabel uint8 = 5
|
SummaryLabel uint8 = 5
|
||||||
|
SummaryBranch uint8 = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
const UnknownSummaryKey = "unknown"
|
const UnknownSummaryKey = "unknown"
|
||||||
@ -31,6 +32,7 @@ type Summary struct {
|
|||||||
OperatingSystems SummaryItems `json:"operating_systems" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
OperatingSystems SummaryItems `json:"operating_systems" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
Machines SummaryItems `json:"machines" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
Machines SummaryItems `json:"machines" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
Labels SummaryItems `json:"labels" gorm:"-"` // labels are not persisted, but calculated at runtime, i.e. when summary is retrieved
|
Labels SummaryItems `json:"labels" gorm:"-"` // labels are not persisted, but calculated at runtime, i.e. when summary is retrieved
|
||||||
|
Branches SummaryItems `json:"branches" gorm:"-"` // branches are not persisted, but calculated at runtime in case a project filter is applied
|
||||||
NumHeartbeats int `json:"-" gorm:"default:0"`
|
NumHeartbeats int `json:"-" gorm:"default:0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +60,14 @@ type SummaryParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SummaryTypes() []uint8 {
|
func SummaryTypes() []uint8 {
|
||||||
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryLabel}
|
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryLabel, SummaryBranch}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NativeSummaryTypes() []uint8 {
|
func NativeSummaryTypes() []uint8 {
|
||||||
|
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryBranch}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PersistedSummaryTypes() []uint8 {
|
||||||
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine}
|
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +78,7 @@ func (s *Summary) Sorted() *Summary {
|
|||||||
sort.Sort(sort.Reverse(s.Languages))
|
sort.Sort(sort.Reverse(s.Languages))
|
||||||
sort.Sort(sort.Reverse(s.Editors))
|
sort.Sort(sort.Reverse(s.Editors))
|
||||||
sort.Sort(sort.Reverse(s.Labels))
|
sort.Sort(sort.Reverse(s.Labels))
|
||||||
|
sort.Sort(sort.Reverse(s.Branches))
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +94,7 @@ func (s *Summary) MappedItems() map[uint8]*SummaryItems {
|
|||||||
SummaryOS: &s.OperatingSystems,
|
SummaryOS: &s.OperatingSystems,
|
||||||
SummaryMachine: &s.Machines,
|
SummaryMachine: &s.Machines,
|
||||||
SummaryLabel: &s.Labels,
|
SummaryLabel: &s.Labels,
|
||||||
|
SummaryBranch: &s.Branches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +241,10 @@ func (s *Summary) MaxByToString(entityType uint8) string {
|
|||||||
|
|
||||||
func (s *Summary) WithResolvedAliases(resolve AliasResolver) *Summary {
|
func (s *Summary) WithResolvedAliases(resolve AliasResolver) *Summary {
|
||||||
processAliases := func(origin []*SummaryItem) []*SummaryItem {
|
processAliases := func(origin []*SummaryItem) []*SummaryItem {
|
||||||
|
if origin == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
target := make([]*SummaryItem, 0)
|
target := make([]*SummaryItem, 0)
|
||||||
|
|
||||||
findItem := func(key string) *SummaryItem {
|
findItem := func(key string) *SummaryItem {
|
||||||
@ -278,6 +290,7 @@ func (s *Summary) WithResolvedAliases(resolve AliasResolver) *Summary {
|
|||||||
s.OperatingSystems = processAliases(s.OperatingSystems)
|
s.OperatingSystems = processAliases(s.OperatingSystems)
|
||||||
s.Machines = processAliases(s.Machines)
|
s.Machines = processAliases(s.Machines)
|
||||||
s.Labels = processAliases(s.Labels)
|
s.Labels = processAliases(s.Labels)
|
||||||
|
s.Branches = processAliases(s.Branches)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ func (r *SummaryRepository) GetAll() ([]*models.Summary, error) {
|
|||||||
Preload("Editors", "type = ?", models.SummaryEditor).
|
Preload("Editors", "type = ?", models.SummaryEditor).
|
||||||
Preload("OperatingSystems", "type = ?", models.SummaryOS).
|
Preload("OperatingSystems", "type = ?", models.SummaryOS).
|
||||||
Preload("Machines", "type = ?", models.SummaryMachine).
|
Preload("Machines", "type = ?", models.SummaryMachine).
|
||||||
|
// branch summaries are currently not persisted, as only relevant in combination with project filter
|
||||||
Find(&summaries).Error; err != nil {
|
Find(&summaries).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -48,6 +49,7 @@ func (r *SummaryRepository) GetByUserWithin(user *models.User, from, to time.Tim
|
|||||||
Preload("Editors", "type = ?", models.SummaryEditor).
|
Preload("Editors", "type = ?", models.SummaryEditor).
|
||||||
Preload("OperatingSystems", "type = ?", models.SummaryOS).
|
Preload("OperatingSystems", "type = ?", models.SummaryOS).
|
||||||
Preload("Machines", "type = ?", models.SummaryMachine).
|
Preload("Machines", "type = ?", models.SummaryMachine).
|
||||||
|
// branch summaries are currently not persisted, as only relevant in combination with project filter
|
||||||
Find(&summaries).Error; err != nil {
|
Find(&summaries).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,7 @@ func (h *BadgeHandler) Get(w http.ResponseWriter, r *http.Request) {
|
|||||||
case "label":
|
case "label":
|
||||||
permitEntity = user.ShareLabels
|
permitEntity = user.ShareLabels
|
||||||
filters = models.NewFiltersWith(models.SummaryLabel, filterKey)
|
filters = models.NewFiltersWith(models.SummaryLabel, filterKey)
|
||||||
|
// branches are intentionally omitted here, as only relevant in combination with a project filter
|
||||||
default:
|
default:
|
||||||
permitEntity = true
|
permitEntity = true
|
||||||
filters = &models.Filters{}
|
filters = &models.Filters{}
|
||||||
|
@ -77,6 +77,9 @@ func typeName(t uint8) string {
|
|||||||
if t == models.SummaryLabel {
|
if t == models.SummaryLabel {
|
||||||
return "label"
|
return "label"
|
||||||
}
|
}
|
||||||
|
if t == models.SummaryBranch {
|
||||||
|
return "branch"
|
||||||
|
}
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,5 +51,8 @@ func ParseFilters(r *http.Request) *models.Filters {
|
|||||||
if q := r.URL.Query().Get("label"); q != "" {
|
if q := r.URL.Query().Get("label"); q != "" {
|
||||||
filters.With(models.SummaryLabel, q)
|
filters.With(models.SummaryLabel, q)
|
||||||
}
|
}
|
||||||
|
if q := r.URL.Query().Get("branch"); q != "" {
|
||||||
|
filters.With(models.SummaryBranch, q)
|
||||||
|
}
|
||||||
return filters
|
return filters
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ const (
|
|||||||
TestOsWin = "Windows"
|
TestOsWin = "Windows"
|
||||||
TestMachine1 = "muety-desktop"
|
TestMachine1 = "muety-desktop"
|
||||||
TestMachine2 = "muety-work"
|
TestMachine2 = "muety-work"
|
||||||
|
TestBranchMaster = "master"
|
||||||
|
TestBranchDev = "dev"
|
||||||
MinUnixTime1 = 1601510400000 * 1e6
|
MinUnixTime1 = 1601510400000 * 1e6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -214,6 +214,7 @@ func (srv *HeartbeatService) updateEntityUserCacheByHeartbeat(hb *models.Heartbe
|
|||||||
go srv.updateEntityUserCache(models.SummaryEditor, hb.Editor, hb.User)
|
go srv.updateEntityUserCache(models.SummaryEditor, hb.Editor, hb.User)
|
||||||
go srv.updateEntityUserCache(models.SummaryOS, hb.OperatingSystem, hb.User)
|
go srv.updateEntityUserCache(models.SummaryOS, hb.OperatingSystem, hb.User)
|
||||||
go srv.updateEntityUserCache(models.SummaryMachine, hb.Machine, hb.User)
|
go srv.updateEntityUserCache(models.SummaryMachine, hb.Machine, hb.User)
|
||||||
|
go srv.updateEntityUserCache(models.SummaryBranch, hb.Branch, hb.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *HeartbeatService) notifyBatch(heartbeats []*models.Heartbeat) {
|
func (srv *HeartbeatService) notifyBatch(heartbeats []*models.Heartbeat) {
|
||||||
|
@ -90,6 +90,10 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f Summ
|
|||||||
summary.FillBy(models.SummaryProject, models.SummaryLabel) // first fill up labels from projects
|
summary.FillBy(models.SummaryProject, models.SummaryLabel) // first fill up labels from projects
|
||||||
summary.FillMissing() // then, full up types which are entirely missing
|
summary.FillMissing() // then, full up types which are entirely missing
|
||||||
|
|
||||||
|
if withBranches := filters != nil && filters.Project != nil && filters.Project.Exists(); !withBranches {
|
||||||
|
summary.Branches = nil
|
||||||
|
}
|
||||||
|
|
||||||
srv.cache.SetDefault(cacheKey, summary)
|
srv.cache.SetDefault(cacheKey, summary)
|
||||||
return summary.Sorted(), nil
|
return summary.Sorted(), nil
|
||||||
}
|
}
|
||||||
@ -134,7 +138,10 @@ func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filt
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
types := models.NativeSummaryTypes()
|
types := models.PersistedSummaryTypes()
|
||||||
|
if filters != nil && filters.Project != nil && filters.Project.Exists() {
|
||||||
|
types = append(types, models.SummaryBranch)
|
||||||
|
}
|
||||||
|
|
||||||
typedAggregations := make(chan models.SummaryItemContainer)
|
typedAggregations := make(chan models.SummaryItemContainer)
|
||||||
defer close(typedAggregations)
|
defer close(typedAggregations)
|
||||||
@ -148,6 +155,7 @@ func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filt
|
|||||||
var editorItems []*models.SummaryItem
|
var editorItems []*models.SummaryItem
|
||||||
var osItems []*models.SummaryItem
|
var osItems []*models.SummaryItem
|
||||||
var machineItems []*models.SummaryItem
|
var machineItems []*models.SummaryItem
|
||||||
|
var branchItems []*models.SummaryItem
|
||||||
|
|
||||||
for i := 0; i < len(types); i++ {
|
for i := 0; i < len(types); i++ {
|
||||||
item := <-typedAggregations
|
item := <-typedAggregations
|
||||||
@ -162,6 +170,8 @@ func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filt
|
|||||||
osItems = item.Items
|
osItems = item.Items
|
||||||
case models.SummaryMachine:
|
case models.SummaryMachine:
|
||||||
machineItems = item.Items
|
machineItems = item.Items
|
||||||
|
case models.SummaryBranch:
|
||||||
|
branchItems = item.Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +189,7 @@ func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filt
|
|||||||
Editors: editorItems,
|
Editors: editorItems,
|
||||||
OperatingSystems: osItems,
|
OperatingSystems: osItems,
|
||||||
Machines: machineItems,
|
Machines: machineItems,
|
||||||
|
Branches: branchItems,
|
||||||
NumHeartbeats: durations.TotalNumHeartbeats(),
|
NumHeartbeats: durations.TotalNumHeartbeats(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +296,7 @@ func (srv *SummaryService) mergeSummaries(summaries []*models.Summary) (*models.
|
|||||||
OperatingSystems: make([]*models.SummaryItem, 0),
|
OperatingSystems: make([]*models.SummaryItem, 0),
|
||||||
Machines: make([]*models.SummaryItem, 0),
|
Machines: make([]*models.SummaryItem, 0),
|
||||||
Labels: make([]*models.SummaryItem, 0),
|
Labels: make([]*models.SummaryItem, 0),
|
||||||
|
Branches: make([]*models.SummaryItem, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
var processed = map[time.Time]bool{}
|
var processed = map[time.Time]bool{}
|
||||||
@ -314,6 +326,7 @@ func (srv *SummaryService) mergeSummaries(summaries []*models.Summary) (*models.
|
|||||||
finalSummary.OperatingSystems = srv.mergeSummaryItems(finalSummary.OperatingSystems, s.OperatingSystems)
|
finalSummary.OperatingSystems = srv.mergeSummaryItems(finalSummary.OperatingSystems, s.OperatingSystems)
|
||||||
finalSummary.Machines = srv.mergeSummaryItems(finalSummary.Machines, s.Machines)
|
finalSummary.Machines = srv.mergeSummaryItems(finalSummary.Machines, s.Machines)
|
||||||
finalSummary.Labels = srv.mergeSummaryItems(finalSummary.Labels, s.Labels)
|
finalSummary.Labels = srv.mergeSummaryItems(finalSummary.Labels, s.Labels)
|
||||||
|
finalSummary.Branches = srv.mergeSummaryItems(finalSummary.Branches, s.Branches)
|
||||||
finalSummary.NumHeartbeats += s.NumHeartbeats
|
finalSummary.NumHeartbeats += s.NumHeartbeats
|
||||||
|
|
||||||
processed[hash] = true
|
processed[hash] = true
|
||||||
|
@ -42,6 +42,7 @@ func (suite *SummaryServiceTestSuite) SetupSuite() {
|
|||||||
Editor: TestEditorGoland,
|
Editor: TestEditorGoland,
|
||||||
OperatingSystem: TestOsLinux,
|
OperatingSystem: TestOsLinux,
|
||||||
Machine: TestMachine1,
|
Machine: TestMachine1,
|
||||||
|
Branch: TestBranchMaster,
|
||||||
Time: models.CustomTime(suite.TestStartTime),
|
Time: models.CustomTime(suite.TestStartTime),
|
||||||
Duration: 150 * time.Second,
|
Duration: 150 * time.Second,
|
||||||
NumHeartbeats: 2,
|
NumHeartbeats: 2,
|
||||||
@ -53,6 +54,7 @@ func (suite *SummaryServiceTestSuite) SetupSuite() {
|
|||||||
Editor: TestEditorGoland,
|
Editor: TestEditorGoland,
|
||||||
OperatingSystem: TestOsLinux,
|
OperatingSystem: TestOsLinux,
|
||||||
Machine: TestMachine1,
|
Machine: TestMachine1,
|
||||||
|
Branch: TestBranchMaster,
|
||||||
Time: models.CustomTime(suite.TestStartTime.Add((30 + 130) * time.Second)),
|
Time: models.CustomTime(suite.TestStartTime.Add((30 + 130) * time.Second)),
|
||||||
Duration: 20 * time.Second,
|
Duration: 20 * time.Second,
|
||||||
NumHeartbeats: 1,
|
NumHeartbeats: 1,
|
||||||
@ -64,6 +66,7 @@ func (suite *SummaryServiceTestSuite) SetupSuite() {
|
|||||||
Editor: TestEditorVscode,
|
Editor: TestEditorVscode,
|
||||||
OperatingSystem: TestOsLinux,
|
OperatingSystem: TestOsLinux,
|
||||||
Machine: TestMachine1,
|
Machine: TestMachine1,
|
||||||
|
Branch: TestBranchDev,
|
||||||
Time: models.CustomTime(suite.TestStartTime.Add(3 * time.Minute)),
|
Time: models.CustomTime(suite.TestStartTime.Add(3 * time.Minute)),
|
||||||
Duration: 15 * time.Second,
|
Duration: 15 * time.Second,
|
||||||
NumHeartbeats: 3,
|
NumHeartbeats: 3,
|
||||||
@ -145,6 +148,13 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Summarize() {
|
|||||||
assert.Equal(suite.T(), suite.TestDurations[0].Time.T(), result.FromTime.T())
|
assert.Equal(suite.T(), suite.TestDurations[0].Time.T(), result.FromTime.T())
|
||||||
assert.Equal(suite.T(), suite.TestDurations[len(suite.TestDurations)-1].Time.T(), result.ToTime.T())
|
assert.Equal(suite.T(), suite.TestDurations[len(suite.TestDurations)-1].Time.T(), result.ToTime.T())
|
||||||
assert.Equal(suite.T(), 185*time.Second, result.TotalTime())
|
assert.Equal(suite.T(), 185*time.Second, result.TotalTime())
|
||||||
|
assert.Equal(suite.T(), 185*time.Second, result.TotalTimeBy(models.SummaryProject))
|
||||||
|
assert.Equal(suite.T(), 185*time.Second, result.TotalTimeBy(models.SummaryOS))
|
||||||
|
assert.Equal(suite.T(), 185*time.Second, result.TotalTimeBy(models.SummaryMachine))
|
||||||
|
assert.Equal(suite.T(), 185*time.Second, result.TotalTimeBy(models.SummaryLanguage))
|
||||||
|
assert.Equal(suite.T(), 185*time.Second, result.TotalTimeBy(models.SummaryEditor))
|
||||||
|
assert.Zero(suite.T(), result.TotalTimeBy(models.SummaryBranch)) // no filters -> no branches contained
|
||||||
|
assert.Zero(suite.T(), result.TotalTimeBy(models.SummaryLabel))
|
||||||
assert.Equal(suite.T(), 170*time.Second, result.TotalTimeByKey(models.SummaryEditor, TestEditorGoland))
|
assert.Equal(suite.T(), 170*time.Second, result.TotalTimeByKey(models.SummaryEditor, TestEditorGoland))
|
||||||
assert.Equal(suite.T(), 15*time.Second, result.TotalTimeByKey(models.SummaryEditor, TestEditorVscode))
|
assert.Equal(suite.T(), 15*time.Second, result.TotalTimeByKey(models.SummaryEditor, TestEditorVscode))
|
||||||
assert.Equal(suite.T(), 6, result.NumHeartbeats)
|
assert.Equal(suite.T(), 6, result.NumHeartbeats)
|
||||||
@ -400,6 +410,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased() {
|
|||||||
assert.Zero(suite.T(), result.TotalTimeByKey(models.SummaryProject, TestProject1))
|
assert.Zero(suite.T(), result.TotalTimeByKey(models.SummaryProject, TestProject1))
|
||||||
assert.NotZero(suite.T(), result.TotalTimeByKey(models.SummaryProject, TestProject2))
|
assert.NotZero(suite.T(), result.TotalTimeByKey(models.SummaryProject, TestProject2))
|
||||||
assert.Equal(suite.T(), 6, result.NumHeartbeats)
|
assert.Equal(suite.T(), 6, result.NumHeartbeats)
|
||||||
|
assert.Nil(suite.T(), result.Branches)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased_ProjectLabels() {
|
func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased_ProjectLabels() {
|
||||||
@ -464,7 +475,8 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Filters() {
|
|||||||
suite.TestLabels[1].Label: suite.TestLabels[1:2],
|
suite.TestLabels[1].Label: suite.TestLabels[1:2],
|
||||||
}, nil).Once()
|
}, nil).Once()
|
||||||
|
|
||||||
sut.Aliased(from, to, suite.TestUser, sut.Summarize, filters, false)
|
result, _ := sut.Aliased(from, to, suite.TestUser, sut.Summarize, filters, false)
|
||||||
|
assert.NotNil(suite.T(), result.Branches) // project filters were applied -> include branches
|
||||||
|
|
||||||
effectiveFilters := suite.DurationService.Calls[0].Arguments[3].(*models.Filters)
|
effectiveFilters := suite.DurationService.Calls[0].Arguments[3].(*models.Filters)
|
||||||
assert.Contains(suite.T(), effectiveFilters.Project, TestProject1) // because actually requested
|
assert.Contains(suite.T(), effectiveFilters.Project, TestProject1) // because actually requested
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"info": {
|
"info": {
|
||||||
"_postman_id": "a7f72e0a-a32e-4ec4-bf60-9915ba0acbd3",
|
"_postman_id": "43639725-0458-40d7-a4d4-9f55a539a7f7",
|
||||||
"name": "Wakapi API Tests",
|
"name": "Wakapi API Tests",
|
||||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||||
},
|
},
|
||||||
@ -996,6 +996,7 @@
|
|||||||
" pm.expect(jsonData.editors.length).to.eql(1);",
|
" pm.expect(jsonData.editors.length).to.eql(1);",
|
||||||
" pm.expect(jsonData.operating_systems.length).to.eql(1);",
|
" pm.expect(jsonData.operating_systems.length).to.eql(1);",
|
||||||
" pm.expect(jsonData.machines.length).to.eql(1);",
|
" pm.expect(jsonData.machines.length).to.eql(1);",
|
||||||
|
" pm.expect(jsonData.branches).to.be.null;",
|
||||||
"});",
|
"});",
|
||||||
"",
|
"",
|
||||||
"/*",
|
"/*",
|
||||||
@ -1463,6 +1464,7 @@
|
|||||||
" pm.expect(jsonData.editors.length).to.eql(1);",
|
" pm.expect(jsonData.editors.length).to.eql(1);",
|
||||||
" pm.expect(jsonData.operating_systems.length).to.eql(1);",
|
" pm.expect(jsonData.operating_systems.length).to.eql(1);",
|
||||||
" pm.expect(jsonData.machines.length).to.eql(1);",
|
" pm.expect(jsonData.machines.length).to.eql(1);",
|
||||||
|
" pm.expect(jsonData.branches.length).to.eql(1);",
|
||||||
"});"
|
"});"
|
||||||
],
|
],
|
||||||
"type": "text/javascript"
|
"type": "text/javascript"
|
||||||
@ -2078,6 +2080,7 @@
|
|||||||
" const jsonData = pm.response.json();",
|
" const jsonData = pm.response.json();",
|
||||||
" pm.expect(jsonData.data.languages).to.eql(null); // because share_languages is false",
|
" pm.expect(jsonData.data.languages).to.eql(null); // because share_languages is false",
|
||||||
" pm.expect(jsonData.data.projects.length).to.eql(1);",
|
" pm.expect(jsonData.data.projects.length).to.eql(1);",
|
||||||
|
" pm.expect(jsonData.data.branches).to.be.null;",
|
||||||
"});"
|
"});"
|
||||||
],
|
],
|
||||||
"type": "text/javascript"
|
"type": "text/javascript"
|
||||||
@ -2133,6 +2136,7 @@
|
|||||||
" const jsonData = pm.response.json();",
|
" const jsonData = pm.response.json();",
|
||||||
" pm.expect(jsonData.data.projects.length).to.eql(1);",
|
" pm.expect(jsonData.data.projects.length).to.eql(1);",
|
||||||
" pm.expect(jsonData.data.total_seconds).to.gt(0);",
|
" pm.expect(jsonData.data.total_seconds).to.gt(0);",
|
||||||
|
" pm.expect(jsonData.data.branches.length).to.eql(1);",
|
||||||
"});"
|
"});"
|
||||||
],
|
],
|
||||||
"type": "text/javascript"
|
"type": "text/javascript"
|
||||||
|
Reference in New Issue
Block a user