@@ -3692,61 +3692,90 @@ func (q *FakeQuerier) GetTemplateInsightsByTemplate(_ context.Context, arg datab
3692
3692
q .mutex .RLock ()
3693
3693
defer q .mutex .RUnlock ()
3694
3694
3695
- /*
3696
- SELECT
3697
- template_id,
3698
- COUNT(DISTINCT user_id) AS active_users,
3699
- (SUM(usage_mins) * 60)::bigint AS usage_total_seconds, -- Includes app usage.
3700
- (SUM(ssh_mins) * 60)::bigint AS usage_ssh_seconds,
3701
- (SUM(sftp_mins) * 60)::bigint AS usage_sftp_seconds,
3702
- (SUM(reconnecting_pty_mins) * 60)::bigint AS usage_reconnecting_pty_seconds,
3703
- (SUM(vscode_mins) * 60)::bigint AS usage_vscode_seconds,
3704
- (SUM(jetbrains_mins) * 60)::bigint AS usage_jetbrains_seconds
3705
- FROM
3706
- template_usage_stats
3707
- WHERE
3708
- start_time >= @start_time::timestamptz
3709
- AND end_time <= @end_time::timestamptz
3710
- GROUP BY template_id;
3711
- */
3695
+ // map time.Time x TemplateID x UserID x <usage>
3696
+ appUsageByTemplateAndUser := map [time.Time ]map [uuid.UUID ]map [uuid.UUID ]database.GetTemplateInsightsByTemplateRow {}
3712
3697
3713
- type grouped struct {
3714
- database.GetTemplateInsightsByTemplateRow
3715
- activeUserIDs map [uuid.UUID ]struct {}
3716
- }
3717
- groupedByTemplateID := make (map [uuid.UUID ]grouped )
3718
- for _ , tus := range q .templateUsageStats {
3719
- if tus .StartTime .Before (arg .StartTime ) || tus .EndTime .After (arg .EndTime ) {
3698
+ // Review agent stats in terms of usage
3699
+ templateIDSet := make (map [uuid.UUID ]struct {})
3700
+
3701
+ for _ , s := range q .workspaceAgentStats {
3702
+ if s .CreatedAt .Before (arg .StartTime ) || s .CreatedAt .Equal (arg .EndTime ) || s .CreatedAt .After (arg .EndTime ) {
3720
3703
continue
3721
3704
}
3722
- row , ok := groupedByTemplateID [tus .TemplateID ]
3723
- if ! ok {
3724
- row = grouped {
3725
- GetTemplateInsightsByTemplateRow : database.GetTemplateInsightsByTemplateRow {
3726
- TemplateID : tus .TemplateID ,
3727
- },
3728
- activeUserIDs : make (map [uuid.UUID ]struct {}),
3729
- }
3705
+ if s .ConnectionCount == 0 {
3706
+ continue
3707
+ }
3708
+
3709
+ t := s .CreatedAt .Truncate (time .Minute )
3710
+ templateIDSet [s .TemplateID ] = struct {}{}
3711
+
3712
+ if _ , ok := appUsageByTemplateAndUser [t ]; ! ok {
3713
+ appUsageByTemplateAndUser [t ] = make (map [uuid.UUID ]map [uuid.UUID ]database.GetTemplateInsightsByTemplateRow )
3730
3714
}
3731
- row .activeUserIDs [tus .UserID ] = struct {}{}
3732
- row .ActiveUsers = int64 (len (row .activeUserIDs ))
3733
- row .UsageTotalSeconds += int64 (tus .UsageMins ) * 60
3734
- row .UsageSshSeconds += int64 (tus .SshMins ) * 60
3735
- row .UsageSftpSeconds += int64 (tus .SftpMins ) * 60
3736
- row .UsageReconnectingPtySeconds += int64 (tus .ReconnectingPtyMins ) * 60
3737
- row .UsageVscodeSeconds += int64 (tus .VscodeMins ) * 60
3738
- row .UsageJetbrainsSeconds += int64 (tus .JetbrainsMins ) * 60
3739
- groupedByTemplateID [tus .TemplateID ] = row
3715
+
3716
+ if _ , ok := appUsageByTemplateAndUser [t ][s.TemplateID ]; ! ok {
3717
+ appUsageByTemplateAndUser [t ][s.TemplateID ] = make (map [uuid.UUID ]database.GetTemplateInsightsByTemplateRow )
3718
+ }
3719
+
3720
+ if _ , ok := appUsageByTemplateAndUser [t ][s.TemplateID ][s .UserID ]; ! ok {
3721
+ appUsageByTemplateAndUser [t ][s.TemplateID ][s .UserID ] = database.GetTemplateInsightsByTemplateRow {}
3722
+ }
3723
+
3724
+ u := appUsageByTemplateAndUser [t ][s.TemplateID ][s .UserID ]
3725
+ if s .SessionCountJetBrains > 0 {
3726
+ u .UsageJetbrainsSeconds = 60
3727
+ }
3728
+ if s .SessionCountVSCode > 0 {
3729
+ u .UsageVscodeSeconds = 60
3730
+ }
3731
+ if s .SessionCountReconnectingPTY > 0 {
3732
+ u .UsageReconnectingPtySeconds = 60
3733
+ }
3734
+ if s .SessionCountSSH > 0 {
3735
+ u .UsageSshSeconds = 60
3736
+ }
3737
+ appUsageByTemplateAndUser [t ][s.TemplateID ][s .UserID ] = u
3740
3738
}
3741
3739
3742
- var rows []database.GetTemplateInsightsByTemplateRow
3743
- for _ , row := range groupedByTemplateID {
3744
- rows = append (rows , row .GetTemplateInsightsByTemplateRow )
3740
+ // Sort used templates
3741
+ templateIDs := make ([]uuid.UUID , 0 , len (templateIDSet ))
3742
+ for templateID := range templateIDSet {
3743
+ templateIDs = append (templateIDs , templateID )
3745
3744
}
3746
- slices .SortFunc (rows , func (a , b database. GetTemplateInsightsByTemplateRow ) int {
3747
- return slice .Ascending (a .TemplateID . String (), b . TemplateID .String ())
3745
+ slices .SortFunc (templateIDs , func (a , b uuid. UUID ) int {
3746
+ return slice .Ascending (a .String (), b .String ())
3748
3747
})
3749
- return rows , nil
3748
+
3749
+ // Build result
3750
+ var result []database.GetTemplateInsightsByTemplateRow
3751
+ for _ , templateID := range templateIDs {
3752
+ r := database.GetTemplateInsightsByTemplateRow {
3753
+ TemplateID : templateID ,
3754
+ }
3755
+
3756
+ uniqueUsers := map [uuid.UUID ]struct {}{}
3757
+
3758
+ for _ , mTemplateUserUsage := range appUsageByTemplateAndUser {
3759
+ mUserUsage , ok := mTemplateUserUsage [templateID ]
3760
+ if ! ok {
3761
+ continue // template was not used in this time window
3762
+ }
3763
+
3764
+ for userID , usage := range mUserUsage {
3765
+ uniqueUsers [userID ] = struct {}{}
3766
+
3767
+ r .UsageJetbrainsSeconds += usage .UsageJetbrainsSeconds
3768
+ r .UsageVscodeSeconds += usage .UsageVscodeSeconds
3769
+ r .UsageReconnectingPtySeconds += usage .UsageReconnectingPtySeconds
3770
+ r .UsageSshSeconds += usage .UsageSshSeconds
3771
+ }
3772
+ }
3773
+
3774
+ r .ActiveUsers = int64 (len (uniqueUsers ))
3775
+
3776
+ result = append (result , r )
3777
+ }
3778
+ return result , nil
3750
3779
}
3751
3780
3752
3781
func (q * FakeQuerier ) GetTemplateParameterInsights (ctx context.Context , arg database.GetTemplateParameterInsightsParams ) ([]database.GetTemplateParameterInsightsRow , error ) {
0 commit comments