GetUserUsageBySource returns per-source aggregated usage for one user. Legacy is excluded by design (visible to admins only via the admin variant).
(db *gorm.DB, userID, period string)
| 264 | // GetUserUsageBySource returns per-source aggregated usage for one user. Legacy |
| 265 | // is excluded by design (visible to admins only via the admin variant). |
| 266 | func GetUserUsageBySource(db *gorm.DB, userID, period string) ([]UsageBucket, SourceTotals, error) { |
| 267 | sqlite := isSQLiteDB(db) |
| 268 | since, dateFmt := periodToWindow(period, sqlite) |
| 269 | bucketExpr := fmt.Sprintf("%s as bucket", dateFmt) |
| 270 | |
| 271 | query := db.Model(&UsageRecord{}). |
| 272 | Select(bucketExpr+", source, COALESCE(api_key_id, '') as api_key_id, api_key_name, "+ |
| 273 | "SUM(prompt_tokens) as prompt_tokens, "+ |
| 274 | "SUM(completion_tokens) as completion_tokens, "+ |
| 275 | "SUM(total_tokens) as total_tokens, "+ |
| 276 | "COUNT(*) as request_count"). |
| 277 | Where("user_id = ?", userID). |
| 278 | Where("source <> ?", UsageSourceLegacy). |
| 279 | Group("bucket, source, api_key_id, api_key_name"). |
| 280 | Order("bucket ASC") |
| 281 | |
| 282 | if !since.IsZero() { |
| 283 | query = query.Where("created_at >= ?", since) |
| 284 | } |
| 285 | |
| 286 | var buckets []UsageBucket |
| 287 | if err := query.Find(&buckets).Error; err != nil { |
| 288 | return nil, SourceTotals{}, err |
| 289 | } |
| 290 | |
| 291 | totals := computeSourceTotals(db, userID, "", since, false) |
| 292 | return buckets, totals, nil |
| 293 | } |
| 294 | |
| 295 | // computeSourceTotals rolls up by_source / by_key / grand_total. |
| 296 | // userID/apiKeyID are optional filters. includeLegacy controls whether the |