Include users' own activity in active users calculation

This commit is contained in:
Viktor Liu 2024-03-04 12:53:45 +01:00
parent 17f5abc653
commit 7649b962c1
5 changed files with 63 additions and 32 deletions

View File

@ -676,28 +676,33 @@ func (s *FileStore) CalculateUsageStats(_ context.Context, accountID string, sta
return nil, fmt.Errorf("account not found")
}
stats := &AccountUsageStats{
TotalUsers: 0,
TotalPeers: int64(len(account.Peers)),
}
stats := &AccountUsageStats{}
activeUsers := make(map[string]struct{})
for userID, user := range account.Users {
if user.LastLogin.After(start) && user.LastLogin.Before(end) && !user.IsServiceUser {
activeUsers[userID] = struct{}{}
}
for _, user := range account.Users {
if !user.IsServiceUser {
stats.TotalUsers++
}
}
activeUsers := make(map[string]bool)
for _, peer := range account.Peers {
lastSeen := peer.Status.LastSeen
if lastSeen.Compare(start) >= 0 && lastSeen.Compare(end) <= 0 {
if _, exists := account.Users[peer.UserID]; exists && !activeUsers[peer.UserID] {
activeUsers[peer.UserID] = true
stats.ActiveUsers++
}
if peer.Status.LastSeen.After(start) && peer.Status.LastSeen.Before(end) {
stats.ActivePeers++
// service users don't have peers, but we check regardless.
if _, exists := account.Users[peer.UserID]; exists && !account.Users[peer.UserID].IsServiceUser {
activeUsers[peer.UserID] = struct{}{}
}
}
}
stats.ActiveUsers = int64(len(activeUsers))
stats.TotalPeers = int64(len(account.Peers))
return stats, nil
}

View File

@ -674,15 +674,15 @@ func TestFileStore_CalculateUsageStats(t *testing.T) {
stats1, err := store.CalculateUsageStats(context.TODO(), "account-1", startDate, endDate)
require.NoError(t, err)
assert.Equal(t, int64(2), stats1.ActiveUsers)
assert.Equal(t, int64(4), stats1.TotalUsers)
assert.Equal(t, int64(3), stats1.ActiveUsers)
assert.Equal(t, int64(5), stats1.TotalUsers)
assert.Equal(t, int64(3), stats1.ActivePeers)
assert.Equal(t, int64(7), stats1.TotalPeers)
assert.Equal(t, int64(8), stats1.TotalPeers)
stats2, err := store.CalculateUsageStats(context.TODO(), "account-2", startDate, endDate)
require.NoError(t, err)
assert.Equal(t, int64(1), stats2.ActiveUsers)
assert.Equal(t, int64(2), stats2.ActiveUsers)
assert.Equal(t, int64(2), stats2.TotalUsers)
assert.Equal(t, int64(1), stats2.ActivePeers)
assert.Equal(t, int64(2), stats2.TotalPeers)

View File

@ -505,10 +505,13 @@ func (s *SqliteStore) CalculateUsageStats(ctx context.Context, accountID string,
stats := &AccountUsageStats{}
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
err := tx.Model(&nbpeer.Peer{}).
Where("account_id = ? AND peer_status_last_seen BETWEEN ? AND ?", accountID, start, end).
Distinct("user_id").
Count(&stats.ActiveUsers).Error
err := tx.Raw(`
SELECT COUNT(DISTINCT user_id) FROM (
SELECT id as user_id FROM users WHERE account_id = ? AND last_login BETWEEN ? AND ? AND is_service_user = ?
UNION
SELECT user_id FROM peers WHERE account_id = ? AND peer_status_last_seen BETWEEN ? AND ?
) AS combined`, accountID, start, end, false, accountID, start, end).Scan(&stats.ActiveUsers).Error
if err != nil {
return fmt.Errorf("get active users: %w", err)
}

View File

@ -360,15 +360,15 @@ func TestSqliteStore_CalculateUsageStats(t *testing.T) {
stats1, err := store.CalculateUsageStats(context.TODO(), "account-1", startDate, endDate)
require.NoError(t, err)
assert.Equal(t, int64(2), stats1.ActiveUsers)
assert.Equal(t, int64(4), stats1.TotalUsers)
assert.Equal(t, int64(3), stats1.ActiveUsers)
assert.Equal(t, int64(5), stats1.TotalUsers)
assert.Equal(t, int64(3), stats1.ActivePeers)
assert.Equal(t, int64(7), stats1.TotalPeers)
assert.Equal(t, int64(8), stats1.TotalPeers)
stats2, err := store.CalculateUsageStats(context.TODO(), "account-2", startDate, endDate)
require.NoError(t, err)
assert.Equal(t, int64(1), stats2.ActiveUsers)
assert.Equal(t, int64(2), stats2.ActiveUsers)
assert.Equal(t, int64(2), stats2.TotalUsers)
assert.Equal(t, int64(1), stats2.ActivePeers)
assert.Equal(t, int64(2), stats2.TotalPeers)

View File

@ -13,20 +13,29 @@
},
"Users": {
"user-1-account-1": {
"Id": "user-1-account-1"
"Id": "user-1-account-1",
"LastLogin": "2023-01-15T00:00:00Z"
},
"user-2-account-1": {
"Id": "user-2-account-1"
"Id": "user-2-account-1",
"LastLogin": "2024-01-02T00:00:00Z"
},
"user-3-account-1": {
"Id": "user-3-account-1"
"Id": "user-3-account-1",
"LastLogin": "2024-02-05T00:00:00Z"
},
"user-4-account-1": {
"Id": "user-4-account-1"
"Id": "user-4-account-1",
"LastLogin": "2024-01-20T00:00:00Z"
},
"user-5-account-1": {
"Id": "user-5-account-1",
"IsServiceUser": true
"IsServiceUser": true,
"LastLogin": "2024-02-15T00:00:00Z"
},
"user-6-account-1": {
"Id": "user-6-account-1",
"LastLogin": "2024-02-10T00:00:00Z"
}
},
"Peers": {
@ -106,6 +115,17 @@
"Meta": {
"Hostname": "peer7-host"
}
},
"peer-8-account-1": {
"ID": "peer-8-account-1",
"UserID": "user-6-account-1",
"Status": {
"LastSeen": "2024-01-01T00:00:00Z"
},
"Name": "Peer Eight",
"Meta": {
"Hostname": "peer8-host"
}
}
}
},
@ -122,14 +142,17 @@
},
"Users": {
"user-1-account-2": {
"Id": "user-1-account-2"
"Id": "user-1-account-2",
"LastLogin": "2023-12-15T00:00:00Z"
},
"user-2-account-2": {
"Id": "user-1-account-2"
"Id": "user-2-account-2",
"LastLogin": "2024-02-28T00:00:00Z"
},
"user-3-account-2": {
"Id": "user-3-account-2",
"IsServiceUser": true
"IsServiceUser": true,
"LastLogin": "2024-01-30T00:00:00Z"
}
},
"Peers": {