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

feat: implement file statistics (resolve #80)

This commit is contained in:
Ferdinand Mütsch
2023-03-22 20:45:27 +01:00
parent 0cf09a0871
commit 4ee3da6f7e
18 changed files with 1324 additions and 1138 deletions

View File

@@ -77,6 +77,8 @@ func NewStatsFrom(summary *models.Summary, filters *models.Filters) *StatsViewMo
branches[i] = convertEntry(e, summary.TotalTimeBy(models.SummaryBranch))
}
// entities omitted intentionally
data.Editors = editors
data.Languages = languages
data.Machines = machines

View File

@@ -35,6 +35,7 @@ type SummariesData struct {
OperatingSystems []*SummariesEntry `json:"operating_systems"`
Projects []*SummariesEntry `json:"projects"`
Branches []*SummariesEntry `json:"branches,omitempty"`
Entities []*SummariesEntry `json:"entities,omitempty"`
GrandTotal *SummariesGrandTotal `json:"grand_total"`
Range *SummariesRange `json:"range"`
}
@@ -132,8 +133,8 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
var wg sync.WaitGroup
wg.Add(6)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.Projects {
@@ -141,6 +142,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
}(data)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.Editors {
@@ -148,6 +150,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
}(data)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.Languages {
@@ -155,6 +158,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
}(data)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.OperatingSystems {
@@ -162,6 +166,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
}(data)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.Machines {
@@ -169,6 +174,7 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
}(data)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.Branches {
@@ -176,9 +182,20 @@ func newDataFrom(s *models.Summary) *SummariesData {
}
}(data)
wg.Add(1)
go func(data *SummariesData) {
defer wg.Done()
for i, e := range s.Entities {
data.Entities[i] = convertEntry(e, s.TotalTimeBy(models.SummaryEntity))
}
}(data)
if s.Branches == nil {
data.Branches = nil
}
if s.Entities == nil {
data.Entities = nil
}
wg.Wait()
return data

View File

@@ -5,6 +5,7 @@ import (
"github.com/emvi/logbuch"
"github.com/mitchellh/hashstructure/v2"
"time"
"unicode"
)
type Duration struct {
@@ -17,8 +18,24 @@ type Duration struct {
OperatingSystem string `json:"operating_system"`
Machine string `json:"machine"`
Branch string `json:"branch"`
Entity string `json:"entity"`
NumHeartbeats int `json:"-" hash:"ignore"`
GroupHash string `json:"-" hash:"ignore"`
excludeEntity bool `json:"-" hash:"ignore"`
}
func (d *Duration) HashInclude(field string, v interface{}) (bool, error) {
if field == "Entity" {
return !d.excludeEntity, nil
}
if field == "Time" ||
field == "Duration" ||
field == "NumHeartbeats" ||
field == "GroupHash" ||
unicode.IsLower(rune(field[0])) {
return false, nil
}
return true, nil
}
func NewDurationFromHeartbeat(h *Heartbeat) *Duration {
@@ -32,11 +49,17 @@ func NewDurationFromHeartbeat(h *Heartbeat) *Duration {
OperatingSystem: h.OperatingSystem,
Machine: h.Machine,
Branch: h.Branch,
Entity: h.Entity,
NumHeartbeats: 1,
}
return d.Hashed()
}
func (d *Duration) WithEntityIgnored() *Duration {
d.excludeEntity = true
return d
}
func (d *Duration) Hashed() *Duration {
hash, err := hashstructure.Hash(d, hashstructure.FormatV2, nil)
if err != nil {
@@ -60,6 +83,8 @@ func (d *Duration) GetKey(t uint8) (key string) {
key = d.Machine
case SummaryBranch:
key = d.Branch
case SummaryEntity:
key = d.Entity
}
if key == "" {

View File

@@ -14,6 +14,7 @@ type Filters struct {
Machine OrFilter
Label OrFilter
Branch OrFilter
Entity OrFilter
}
type OrFilter []string
@@ -65,6 +66,8 @@ func (f *Filters) WithMultiple(entity uint8, keys []string) *Filters {
f.Label = append(f.Label, keys...)
case SummaryBranch:
f.Branch = append(f.Branch, keys...)
case SummaryEntity:
f.Entity = append(f.Entity, keys...)
}
return f
}
@@ -84,6 +87,8 @@ func (f *Filters) One() (bool, uint8, OrFilter) {
return true, SummaryLabel, f.Label
} else if f.Branch != nil && f.Branch.Exists() {
return true, SummaryBranch, f.Branch
} else if f.Entity != nil && f.Entity.Exists() {
return true, SummaryEntity, f.Entity
}
return false, 0, OrFilter{}
}
@@ -102,7 +107,7 @@ func (f *Filters) IsEmpty() bool {
func (f *Filters) Count() int {
var count int
for i := SummaryProject; i <= SummaryBranch; i++ {
for i := SummaryProject; i <= SummaryEntity; i++ {
count += f.CountByEntity(i)
}
return count
@@ -114,7 +119,7 @@ func (f *Filters) CountByEntity(entity uint8) int {
func (f *Filters) EntityCount() int {
var count int
for i := SummaryProject; i <= SummaryBranch; i++ {
for i := SummaryProject; i <= SummaryEntity; i++ {
if c := f.CountByEntity(i); c > 0 {
count++
}
@@ -138,6 +143,8 @@ func (f *Filters) ResolveEntity(entityId uint8) *OrFilter {
return &f.Label
case SummaryBranch:
return &f.Branch
case SummaryEntity:
return &f.Entity
default:
return &OrFilter{}
}
@@ -209,6 +216,7 @@ func (f *Filters) WithAliases(resolve AliasReverseResolver) *Filters {
}
f.Branch = updated
}
// no aliases for entites / files
return f
}
@@ -221,3 +229,7 @@ func (f *Filters) WithProjectLabels(resolve ProjectLabelReverseResolver) *Filter
}
return f
}
func (f *Filters) IsProjectDetails() bool {
return f != nil && f.Project != nil && f.Project.Exists()
}

View File

@@ -63,6 +63,8 @@ func (h *Heartbeat) GetKey(t uint8) (key string) {
key = h.Machine
case SummaryBranch:
key = h.Branch
case SummaryEntity:
key = h.Entity
}
if key == "" {

View File

@@ -16,6 +16,7 @@ const (
SummaryMachine uint8 = 4
SummaryLabel uint8 = 5
SummaryBranch uint8 = 6
SummaryEntity uint8 = 7
)
const UnknownSummaryKey = "unknown"
@@ -34,6 +35,7 @@ type Summary struct {
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
Branches SummaryItems `json:"branches" gorm:"-"` // branches are not persisted, but calculated at runtime in case a project filter is applied
Entities SummaryItems `json:"entities" gorm:"-"` // entities are not persisted, but calculated at runtime in case a project filter is applied
NumHeartbeats int `json:"-"`
}
@@ -62,11 +64,11 @@ type SummaryParams struct {
}
func SummaryTypes() []uint8 {
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryLabel, SummaryBranch}
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryLabel, SummaryBranch, SummaryEntity}
}
func NativeSummaryTypes() []uint8 {
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryBranch}
return []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine, SummaryBranch, SummaryEntity}
}
func PersistedSummaryTypes() []uint8 {
@@ -81,6 +83,7 @@ func (s *Summary) Sorted() *Summary {
sort.Sort(sort.Reverse(s.Editors))
sort.Sort(sort.Reverse(s.Labels))
sort.Sort(sort.Reverse(s.Branches))
sort.Sort(sort.Reverse(s.Entities))
return s
}
@@ -97,6 +100,7 @@ func (s *Summary) MappedItems() map[uint8]*SummaryItems {
SummaryMachine: &s.Machines,
SummaryLabel: &s.Labels,
SummaryBranch: &s.Branches,
SummaryEntity: &s.Entities,
}
}
@@ -116,6 +120,8 @@ func (s *Summary) ItemsByType(summaryType uint8) *SummaryItems {
return &s.Labels
case SummaryBranch:
return &s.Branches
case SummaryEntity:
return &s.Entities
}
return nil
}
@@ -325,6 +331,7 @@ func (s *Summary) WithResolvedAliases(resolve AliasResolver) *Summary {
s.Machines = processAliases(s.Machines)
s.Labels = processAliases(s.Labels)
s.Branches = processAliases(s.Branches)
// no aliases for entities / files
return s
}