mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
fix: generate dummy items for missing types in historic summary data
This commit is contained in:
@@ -13,6 +13,8 @@ const (
|
|||||||
SummaryMachine uint8 = 4
|
SummaryMachine uint8 = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const UnknownSummaryKey = "unknown"
|
||||||
|
|
||||||
type Summary struct {
|
type Summary struct {
|
||||||
ID uint `json:"-" gorm:"primary_key"`
|
ID uint `json:"-" gorm:"primary_key"`
|
||||||
UserID string `json:"user_id" gorm:"not null; index:idx_time_summary_user"`
|
UserID string `json:"user_id" gorm:"not null; index:idx_time_summary_user"`
|
||||||
@@ -45,3 +47,55 @@ type SummaryViewModel struct {
|
|||||||
Success string
|
Success string
|
||||||
ApiKey string
|
ApiKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Augments the summary in a way that at least one item is present for every type.
|
||||||
|
If a summary has zero items for a given type, but one or more for any of the other types,
|
||||||
|
the total summary duration can be derived from those and inserted as a dummy-item with key "unknown"
|
||||||
|
for the missing type.
|
||||||
|
For instance, the machine type was introduced post hoc. Accordingly, no "machine"-information is present in
|
||||||
|
the data for old heartbeats and summaries. If a user has two years of data without machine information and
|
||||||
|
one day with such, a "machine"-chart plotted from that data will reference a way smaller absolute total amount
|
||||||
|
of time than the other ones.
|
||||||
|
To avoid having to modify persisted data retrospectively, i.e. inserting a dummy SummaryItem for the new type,
|
||||||
|
such is generated dynamically here, considering the "machine" for all old heartbeats "unknown".
|
||||||
|
*/
|
||||||
|
func (s *Summary) FillUnknown() {
|
||||||
|
types := []uint8{SummaryProject, SummaryLanguage, SummaryEditor, SummaryOS, SummaryMachine}
|
||||||
|
missingTypes := make([]uint8, 0)
|
||||||
|
typeItems := map[uint8]*[]*SummaryItem{
|
||||||
|
SummaryProject: &s.Projects,
|
||||||
|
SummaryLanguage: &s.Languages,
|
||||||
|
SummaryEditor: &s.Editors,
|
||||||
|
SummaryOS: &s.OperatingSystems,
|
||||||
|
SummaryMachine: &s.Machines,
|
||||||
|
}
|
||||||
|
var somePresentType uint8
|
||||||
|
|
||||||
|
for _, t := range types {
|
||||||
|
if len(*typeItems[t]) == 0 {
|
||||||
|
missingTypes = append(missingTypes, t)
|
||||||
|
} else {
|
||||||
|
somePresentType = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't proceed if entire summary is empty
|
||||||
|
if len(missingTypes) == len(types) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate total duration from any of the present sets of items
|
||||||
|
var timeSum time.Duration
|
||||||
|
for _, item := range *typeItems[somePresentType] {
|
||||||
|
timeSum += item.Total
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct dummy item for all missing types
|
||||||
|
for _, t := range missingTypes {
|
||||||
|
*typeItems[t] = append(*typeItems[t], &SummaryItem{
|
||||||
|
Type: t,
|
||||||
|
Key: UnknownSummaryKey,
|
||||||
|
Total: timeSum,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -103,6 +103,10 @@ func (srv *SummaryService) Construct(from, to time.Time, user *models.User, reco
|
|||||||
if len(existingSummaries) > 0 {
|
if len(existingSummaries) > 0 {
|
||||||
realFrom = existingSummaries[0].FromTime
|
realFrom = existingSummaries[0].FromTime
|
||||||
realTo = existingSummaries[len(existingSummaries)-1].ToTime
|
realTo = existingSummaries[len(existingSummaries)-1].ToTime
|
||||||
|
|
||||||
|
for _, summary := range existingSummaries {
|
||||||
|
summary.FillUnknown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(heartbeats) > 0 {
|
if len(heartbeats) > 0 {
|
||||||
t1, t2 := time.Time(heartbeats[0].Time), time.Time(heartbeats[len(heartbeats)-1].Time)
|
t1, t2 := time.Time(heartbeats[0].Time), time.Time(heartbeats[len(heartbeats)-1].Time)
|
||||||
@@ -197,7 +201,7 @@ func (srv *SummaryService) aggregateBy(heartbeats []*models.Heartbeat, summaryTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
key = "unknown"
|
key = models.UnknownSummaryKey
|
||||||
}
|
}
|
||||||
|
|
||||||
if aliasedKey, err := srv.AliasService.GetAliasOrDefault(user.ID, summaryType, key); err == nil {
|
if aliasedKey, err := srv.AliasService.GetAliasOrDefault(user.ID, summaryType, key); err == nil {
|
||||||
|
@@ -57,7 +57,7 @@ function draw() {
|
|||||||
.map(p => {
|
.map(p => {
|
||||||
return {
|
return {
|
||||||
label: p.key,
|
label: p.key,
|
||||||
data: [parseInt(p.total)],
|
data: [parseInt(p.total) / 60],
|
||||||
backgroundColor: getRandomColor(p.key)
|
backgroundColor: getRandomColor(p.key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -68,6 +68,14 @@ function draw() {
|
|||||||
legend: {
|
legend: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: 'Minutes'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
onResize: onChartResize
|
onResize: onChartResize
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
1.8.0
|
1.8.1
|
Reference in New Issue
Block a user