, * tools: array, * models: array, * recent: array * } */ public function forUser(User $user, int $days = 14): array { $days = max(7, min(30, $days)); $startDate = CarbonImmutable::today()->subDays($days - 1); $events = EmbeddingUsageEvent::query() ->whereBelongsTo($user) ->where('created_at', '>=', $startDate->startOfDay()) ->latest() ->get(['tool', 'model', 'input_count', 'result_count', 'created_at']); return [ 'days' => $days, 'totals' => $this->totals($events), 'daily' => $this->dailyActivity($events, $startDate, $days), 'tools' => $this->toolUsage($events), 'models' => $this->modelUsage($events), 'recent' => $this->recentActivity($events), ]; } /** * @param Collection $events * @return array{actions: int, inputs: int, results: int, models: int} */ protected function totals(Collection $events): array { return [ 'actions' => $events->count(), 'inputs' => (int) $events->sum('input_count'), 'results' => (int) $events->sum('result_count'), 'models' => $events->pluck('model')->unique()->count(), ]; } /** * @param Collection $events * @return array */ protected function dailyActivity(Collection $events, CarbonImmutable $startDate, int $days): array { $daily = collect(range(0, $days - 1)) ->map(function (int $offset) use ($events, $startDate): array { $date = $startDate->addDays($offset); $dayEvents = $events->filter(fn (EmbeddingUsageEvent $event): bool => $event->created_at->isSameDay($date)); return [ 'date' => $date->toDateString(), 'label' => $date->format('M j'), 'actions' => $dayEvents->count(), 'inputs' => (int) $dayEvents->sum('input_count'), 'height' => 0, ]; }); $maxActions = max(1, (int) $daily->max('actions')); return $daily ->map(fn (array $day): array => [ ...$day, 'height' => $day['actions'] > 0 ? max(8, (int) round(($day['actions'] / $maxActions) * 100)) : 0, ]) ->values() ->all(); } /** * @param Collection $events * @return array */ protected function toolUsage(Collection $events): array { $maxActions = max(1, $events->groupBy('tool')->map->count()->max() ?? 0); return $events ->groupBy('tool') ->map(fn (Collection $toolEvents, string $tool): array => [ 'key' => $tool, 'label' => Str::of($tool)->replace('_', ' ')->headline()->value(), 'actions' => $toolEvents->count(), 'inputs' => (int) $toolEvents->sum('input_count'), 'results' => (int) $toolEvents->sum('result_count'), 'percentage' => (int) round(($toolEvents->count() / $maxActions) * 100), ]) ->sortByDesc('actions') ->values() ->all(); } /** * @param Collection $events * @return array */ protected function modelUsage(Collection $events): array { $maxActions = max(1, $events->groupBy('model')->map->count()->max() ?? 0); return $events ->groupBy('model') ->map(fn (Collection $modelEvents, string $model): array => [ 'label' => $model, 'actions' => $modelEvents->count(), 'inputs' => (int) $modelEvents->sum('input_count'), 'percentage' => (int) round(($modelEvents->count() / $maxActions) * 100), ]) ->sortByDesc('actions') ->take(5) ->values() ->all(); } /** * @param Collection $events * @return array */ protected function recentActivity(Collection $events): array { return $events ->take(6) ->map(fn (EmbeddingUsageEvent $event): array => [ 'tool' => $event->tool, 'tool_label' => Str::of($event->tool)->replace('_', ' ')->headline()->value(), 'model' => $event->model, 'inputs' => $event->input_count, 'results' => $event->result_count, 'created_at' => $event->created_at->diffForHumans(short: true), ]) ->values() ->all(); } }