156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func reportingServiceDB(eventRepo *repository.ReportingEventRepo, rollupRepo *repository.ReportingEventsRollupRepo) *gorm.DB {
|
|
if rollupRepo != nil && rollupRepo.DB() != nil {
|
|
return rollupRepo.DB()
|
|
}
|
|
if eventRepo != nil {
|
|
return eventRepo.DB()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func accountReportingLocation(ctx context.Context, db *gorm.DB, accountID uint) (*time.Location, bool, error) {
|
|
if db == nil {
|
|
return nil, false, nil
|
|
}
|
|
var account model.Account
|
|
if err := db.WithContext(ctx).First(&account, accountID).Error; err != nil {
|
|
return nil, false, err
|
|
}
|
|
tz := strings.TrimSpace(account.ReportingTimezone)
|
|
if tz == "" {
|
|
return nil, false, nil
|
|
}
|
|
loc, err := time.LoadLocation(tz)
|
|
if err != nil {
|
|
return nil, false, nil
|
|
}
|
|
return loc, true, nil
|
|
}
|
|
|
|
func utcBoundariesForReportingDate(date time.Time, loc *time.Location) (time.Time, time.Time, time.Time) {
|
|
localDate := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, loc)
|
|
startUTC := localDate.UTC()
|
|
endUTC := localDate.AddDate(0, 0, 1).UTC()
|
|
rollupDate := time.Date(localDate.Year(), localDate.Month(), localDate.Day(), 0, 0, 0, 0, time.UTC)
|
|
return startUTC, endUTC, rollupDate
|
|
}
|
|
|
|
func rollupDateForReportingTime(value time.Time, loc *time.Location) time.Time {
|
|
localTime := value.In(loc)
|
|
return time.Date(localTime.Year(), localTime.Month(), localTime.Day(), 0, 0, 0, 0, time.UTC)
|
|
}
|
|
|
|
func dimensionIDForEvent(accountID uint, event model.ReportingEvent, dim DimensionSpec) (uint, bool) {
|
|
switch dim.GroupColumn {
|
|
case "":
|
|
return accountID, true
|
|
case "user_id":
|
|
if event.UserID == nil {
|
|
return 0, false
|
|
}
|
|
return *event.UserID, true
|
|
case "inbox_id":
|
|
if event.InboxID == nil {
|
|
return 0, false
|
|
}
|
|
return *event.InboxID, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func isDistinctCountEvent(name string) bool {
|
|
for _, eventName := range DistinctCountEvents {
|
|
if name == eventName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func aggregateReportingEvents(accountID uint, events []model.ReportingEvent) []RollupAggregate {
|
|
type groupKey struct {
|
|
dimType model.DimensionType
|
|
dimID uint
|
|
metric model.RollupMetric
|
|
}
|
|
|
|
groups := map[groupKey]*RollupAggregate{}
|
|
seenDistinct := map[groupKey]map[uint]struct{}{}
|
|
|
|
for _, event := range events {
|
|
for _, dim := range BackfillDimensions {
|
|
dimID, ok := dimensionIDForEvent(accountID, event, dim)
|
|
if !ok {
|
|
continue
|
|
}
|
|
key := groupKey{dimType: dim.Type, dimID: dimID, metric: model.RollupMetric(event.Name)}
|
|
agg, ok := groups[key]
|
|
if !ok {
|
|
agg = &RollupAggregate{DimensionType: dim.Type, DimensionID: dimID, Metric: model.RollupMetric(event.Name)}
|
|
groups[key] = agg
|
|
}
|
|
|
|
if isDistinctCountEvent(event.Name) {
|
|
if event.ConversationID == nil {
|
|
continue
|
|
}
|
|
if seenDistinct[key] == nil {
|
|
seenDistinct[key] = map[uint]struct{}{}
|
|
}
|
|
if _, exists := seenDistinct[key][*event.ConversationID]; exists {
|
|
continue
|
|
}
|
|
seenDistinct[key][*event.ConversationID] = struct{}{}
|
|
agg.Count++
|
|
continue
|
|
}
|
|
|
|
agg.Count++
|
|
agg.SumValue += event.Value
|
|
agg.SumBizHours += event.ValueInBusinessHours
|
|
}
|
|
}
|
|
|
|
aggregates := make([]RollupAggregate, 0, len(groups))
|
|
for _, agg := range groups {
|
|
if agg.Count == 0 {
|
|
continue
|
|
}
|
|
aggregates = append(aggregates, *agg)
|
|
}
|
|
return aggregates
|
|
}
|
|
|
|
func rollupRowsFromAggregates(accountID uint, date time.Time, aggregates []RollupAggregate) []model.ReportingEventsRollup {
|
|
var rollupRows []model.ReportingEventsRollup
|
|
for _, agg := range aggregates {
|
|
rollupMetrics := ExpandEventToRollupMetrics(agg.Metric, agg.Count, agg.SumValue, agg.SumBizHours)
|
|
for rm, data := range rollupMetrics {
|
|
rollupRows = append(rollupRows, model.ReportingEventsRollup{
|
|
AccountID: accountID,
|
|
Date: date,
|
|
DimensionType: agg.DimensionType,
|
|
DimensionID: agg.DimensionID,
|
|
Metric: rm,
|
|
Count: data.Count,
|
|
SumValue: data.SumValue,
|
|
SumValueBusinessHours: data.SumBizHours,
|
|
})
|
|
}
|
|
}
|
|
return rollupRows
|
|
}
|