Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConsolidateDailyMetricsJob
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 3
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handle
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
132
 incrementFor
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Analytics\Infrastructure\Jobs;
6
7use Illuminate\Bus\Queueable;
8use Illuminate\Contracts\Queue\ShouldQueue;
9use Illuminate\Foundation\Bus\Dispatchable;
10use Illuminate\Queue\InteractsWithQueue;
11use Illuminate\Queue\SerializesModels;
12use Illuminate\Support\Carbon;
13use ImovelZapAi\Analytics\Domain\Enums\AnalyticsEventKey;
14use ImovelZapAi\Analytics\Domain\Enums\MetricKey;
15use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsDailyMetricModel;
16use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsFactModel;
17
18final class ConsolidateDailyMetricsJob implements ShouldQueue
19{
20    use Dispatchable;
21    use InteractsWithQueue;
22    use Queueable;
23    use SerializesModels;
24
25    public function __construct(
26        public readonly string $tenantId,
27        public readonly ?string $metricDate = null,
28    ) {}
29
30    public function handle(): void
31    {
32        $date = $this->metricDate
33            ? Carbon::parse($this->metricDate)->toDateString()
34            : now()->subDay()->toDateString();
35
36        AnalyticsDailyMetricModel::query()
37            ->where('tenant_id', $this->tenantId)
38            ->whereDate('metric_date', $date)
39            ->delete();
40
41        $aggregates = [];
42
43        $facts = AnalyticsFactModel::query()
44            ->where('tenant_id', $this->tenantId)
45            ->whereDate('occurred_at', $date)
46            ->get();
47
48        foreach ($facts as $fact) {
49            $eventKey = AnalyticsEventKey::tryFrom((string) $fact->event_key);
50            if ($eventKey === null) {
51                continue;
52            }
53
54            $dimensions = [null];
55            if ($fact->user_id !== null) {
56                $dimensions[] = 'user:'.$fact->user_id;
57            }
58            $source = $fact->meta['source'] ?? null;
59            if (is_string($source) && $source !== '') {
60                $dimensions[] = 'channel:'.$source;
61            }
62
63            foreach ($eventKey->metricKeys() as $metricKey) {
64                foreach (array_unique($dimensions) as $dimensionKey) {
65                    $key = $metricKey->value.'|'.($dimensionKey ?? '');
66                    $increment = $this->incrementFor($metricKey, $fact->amount, $fact->meta ?? []);
67                    $aggregates[$key] = ($aggregates[$key] ?? 0) + $increment;
68                }
69            }
70        }
71
72        foreach ($aggregates as $compoundKey => $value) {
73            [$metricKey, $dimensionKey] = array_pad(explode('|', $compoundKey, 2), 2, '');
74            AnalyticsDailyMetricModel::query()->create([
75                'tenant_id' => $this->tenantId,
76                'metric_date' => $date,
77                'metric_key' => $metricKey,
78                'dimension_key' => $dimensionKey !== '' ? $dimensionKey : null,
79                'value' => $value,
80            ]);
81        }
82    }
83
84    /** @param array<string, mixed> $meta */
85    private function incrementFor(MetricKey $metricKey, mixed $amount, array $meta): float
86    {
87        if ($metricKey === MetricKey::VolumeNegotiated || $metricKey === MetricKey::PaymentsReceivedAmount) {
88            $value = $amount ?? ($meta['amount'] ?? null);
89
90            return $value !== null ? (float) $value : 0.0;
91        }
92
93        return 1.0;
94    }
95}