diff --git a/models/filters.go b/models/filters.go index a2ca2c3..7e5efaf 100644 --- a/models/filters.go +++ b/models/filters.go @@ -104,7 +104,6 @@ func (f *Filters) Hash() string { } func (f *Filters) Match(h *Heartbeat) bool { - // TODO: labels? return (f.Project == nil || f.Project.MatchAny(h.Project)) && (f.OS == nil || f.OS.MatchAny(h.OperatingSystem)) && (f.Language == nil || f.Language.MatchAny(h.Language)) && @@ -113,12 +112,12 @@ func (f *Filters) Match(h *Heartbeat) bool { } // WithAliases adds OR-conditions for every alias of a filter key as additional filter keys -func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters { +func (f *Filters) WithAliases(resolve AliasReverseResolver) *Filters { if f.Project != nil { updated := OrFilter(make([]string, 0, len(f.Project))) for _, e := range f.Project { updated = append(updated, e) - updated = append(updated, resolver(SummaryProject, e)...) + updated = append(updated, resolve(SummaryProject, e)...) } f.Project = updated } @@ -126,7 +125,7 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters { updated := OrFilter(make([]string, 0, len(f.OS))) for _, e := range f.OS { updated = append(updated, e) - updated = append(updated, resolver(SummaryOS, e)...) + updated = append(updated, resolve(SummaryOS, e)...) } f.OS = updated } @@ -134,7 +133,7 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters { updated := OrFilter(make([]string, 0, len(f.Language))) for _, e := range f.Language { updated = append(updated, e) - updated = append(updated, resolver(SummaryLanguage, e)...) + updated = append(updated, resolve(SummaryLanguage, e)...) } f.Language = updated } @@ -142,7 +141,7 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters { updated := OrFilter(make([]string, 0, len(f.Editor))) for _, e := range f.Editor { updated = append(updated, e) - updated = append(updated, resolver(SummaryEditor, e)...) + updated = append(updated, resolve(SummaryEditor, e)...) } f.Editor = updated } @@ -150,9 +149,19 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters { updated := OrFilter(make([]string, 0, len(f.Machine))) for _, e := range f.Machine { updated = append(updated, e) - updated = append(updated, resolver(SummaryMachine, e)...) + updated = append(updated, resolve(SummaryMachine, e)...) } f.Machine = updated } return f } + +func (f *Filters) WithProjectLabels(resolve ProjectLabelReverseResolver) *Filters { + if f.Label == nil || !f.Label.Exists() { + return f + } + for _, l := range f.Label { + f.Project = append(f.Project, resolve(l)...) + } + return f +} diff --git a/models/project_label.go b/models/project_label.go index e344766..d143e50 100644 --- a/models/project_label.go +++ b/models/project_label.go @@ -1,5 +1,8 @@ package models +// ProjectLabelReverseResolver returns all projects for a given label +type ProjectLabelReverseResolver func(l string) []string + type ProjectLabel struct { ID uint `json:"id" gorm:"primary_key"` User *User `json:"-" gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` diff --git a/services/project_label.go b/services/project_label.go index b2ac19b..7581688 100644 --- a/services/project_label.go +++ b/services/project_label.go @@ -43,6 +43,7 @@ func (srv *ProjectLabelService) GetByUser(userId string) ([]*models.ProjectLabel return labels, nil } +// GetByUserGrouped returns lists of project labels, grouped by their project key func (srv *ProjectLabelService) GetByUserGrouped(userId string) (map[string][]*models.ProjectLabel, error) { labelsByProject := make(map[string][]*models.ProjectLabel) userLabels, err := srv.GetByUser(userId) @@ -60,6 +61,7 @@ func (srv *ProjectLabelService) GetByUserGrouped(userId string) (map[string][]*m return labelsByProject, nil } +// GetByUserGroupedInverted returns lists of project labels, grouped by their label key func (srv *ProjectLabelService) GetByUserGroupedInverted(userId string) (map[string][]*models.ProjectLabel, error) { projectsByLabel := make(map[string][]*models.ProjectLabel) userLabels, err := srv.GetByUser(userId) diff --git a/services/summary.go b/services/summary.go index 1a81e38..abd7d3f 100644 --- a/services/summary.go +++ b/services/summary.go @@ -62,23 +62,15 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f Summ return cacheResult.(*models.Summary), nil } - // Wrap alias resolution - resolve := func(t uint8, k string) string { - s, _ := srv.aliasService.GetAliasOrDefault(user.ID, t, k) - return s - } - resolveReverse := func(t uint8, k string) []string { - aliases, _ := srv.aliasService.GetByUserAndKeyAndType(user.ID, k, t) - aliasStrings := make([]string, 0, len(aliases)) - for _, a := range aliases { - aliasStrings = append(aliasStrings, a.Value) - } - return aliasStrings - } + // Resolver functions + resolveAliases := srv.getAliasResolver(user) + resolveAliasesReverse := srv.getAliasReverseResolver(user) + resolveProjectLabelsReverse := srv.getProjectLabelsReverseResolver(user) // Post-process filters if filters != nil { - filters = filters.WithAliases(resolveReverse) + filters = filters.WithAliases(resolveAliasesReverse) + filters = filters.WithProjectLabels(resolveProjectLabelsReverse) } // Initialize alias resolver service @@ -93,7 +85,7 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f Summ } // Post-process summary and cache it - summary := s.WithResolvedAliases(resolve) + summary := s.WithResolvedAliases(resolveAliases) summary = srv.withProjectLabels(summary) summary.FillBy(models.SummaryProject, models.SummaryLabel) // first fill up labels from projects summary.FillMissing() // then, full up types which are entirely missing @@ -419,3 +411,41 @@ func (srv *SummaryService) invalidateUserCache(userId string) { } } } + +func (srv *SummaryService) getAliasResolver(user *models.User) models.AliasResolver { + return func(t uint8, k string) string { + s, _ := srv.aliasService.GetAliasOrDefault(user.ID, t, k) + return s + } +} + +func (srv *SummaryService) getAliasReverseResolver(user *models.User) models.AliasReverseResolver { + return func(t uint8, k string) []string { + aliases, err := srv.aliasService.GetByUserAndKeyAndType(user.ID, k, t) + if err != nil { + aliases = []*models.Alias{} + } + aliasStrings := make([]string, 0, len(aliases)) + for _, a := range aliases { + aliasStrings = append(aliasStrings, a.Value) + } + return aliasStrings + } +} + +func (srv *SummaryService) getProjectLabelsReverseResolver(user *models.User) models.ProjectLabelReverseResolver { + return func(k string) []string { + var labels []*models.ProjectLabel + allLabels, err := srv.projectLabelService.GetByUserGroupedInverted(user.ID) + if err == nil { + if l, ok := allLabels[k]; ok { + labels = l + } + } + projectStrings := make([]string, 0, len(labels)) + for _, l := range labels { + projectStrings = append(projectStrings, l.ProjectKey) + } + return projectStrings + } +}