Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
GetRankingQuery
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 3
42
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 topByDimension
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
6
 period
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Analytics\Application\Queries;
6
7use Illuminate\Support\Carbon;
8use Illuminate\Support\Facades\DB;
9use ImovelZapAi\Analytics\Domain\Enums\MetricKey;
10use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsDailyMetricModel;
11
12final class GetRankingQuery
13{
14    /**
15     * @param  array{from?: string, to?: string, days?: int, limit?: int}  $filters
16     * @return array<string, mixed>
17     */
18    public function execute(string $tenantId, array $filters = []): array
19    {
20        [$from, $to] = $this->period($filters);
21        $limit = (int) ($filters['limit'] ?? 5);
22
23        return [
24            'period' => ['from' => $from->toDateString(), 'to' => $to->toDateString()],
25            'users_deals_won' => $this->topByDimension($tenantId, $from, $to, MetricKey::DealsWon, 'user:', $limit),
26            'users_visits_completed' => $this->topByDimension($tenantId, $from, $to, MetricKey::VisitsCompleted, 'user:', $limit),
27            'channels' => $this->topByDimension($tenantId, $from, $to, MetricKey::PeopleOpportunitiesCreated, 'channel:', $limit),
28        ];
29    }
30
31    /**
32     * @return list<array{key: string, label: string, value: float}>
33     */
34    private function topByDimension(
35        string $tenantId,
36        Carbon $from,
37        Carbon $to,
38        MetricKey $metricKey,
39        string $prefix,
40        int $limit,
41    ): array {
42        $rows = AnalyticsDailyMetricModel::query()
43            ->select('dimension_key', DB::raw('SUM(value) as total'))
44            ->where('tenant_id', $tenantId)
45            ->where('metric_key', $metricKey->value)
46            ->whereNotNull('dimension_key')
47            ->where('dimension_key', 'like', $prefix.'%')
48            ->whereBetween('metric_date', [$from->toDateString(), $to->toDateString()])
49            ->groupBy('dimension_key')
50            ->orderByDesc('total')
51            ->limit($limit)
52            ->get();
53
54        return $rows->map(static function ($row) use ($prefix): array {
55            $key = (string) $row->dimension_key;
56            $label = str_starts_with($key, 'user:')
57                ? 'Corretor #'.substr($key, 5)
58                : ucfirst(substr($key, 8));
59
60            return [
61                'key' => $key,
62                'label' => $label,
63                'value' => (float) $row->total,
64            ];
65        })->all();
66    }
67
68    /** @param array{from?: string, to?: string, days?: int} $filters */
69    /** @return array{0: Carbon, 1: Carbon} */
70    private function period(array $filters): array
71    {
72        $to = isset($filters['to']) ? Carbon::parse($filters['to'])->endOfDay() : now();
73        $from = isset($filters['from'])
74            ? Carbon::parse($filters['from'])->startOfDay()
75            : $to->copy()->subDays((int) ($filters['days'] ?? 30))->startOfDay();
76
77        return [$from, $to];
78    }
79}