Files
gochat/internal/service/reporting_rollup_helpers.go
T

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
}