Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.22% covered (success)
93.22%
55 / 59
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
DashboardMetricService
93.22% covered (success)
93.22%
55 / 59
60.00% covered (warning)
60.00%
3 / 5
20.12
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getProgressionStats
91.30% covered (success)
91.30%
21 / 23
0.00% covered (danger)
0.00%
0 / 1
8.04
 getConversationStatsForPeriod
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
4.10
 getHistoricalWeeklyAverageStats
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 calculateEvolution
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace App\Services\RolePlay;
4
5use App\Http\Models\Auth\User;
6use App\Http\Models\RolePlayConversations;
7use App\Http\Models\RolePlayProjects;
8use App\Http\Models\RolePlaySkillProgressions;
9use Carbon\Carbon;
10use MongoDB\BSON\UTCDateTime;
11
12class DashboardMetricService
13{
14    protected RolePlayConversations $conversationModel;
15    protected RolePlaySkillProgressions $skillProgressionModel;
16    protected RolePlayProjects $projectModel;
17
18    public function __construct(
19        RolePlayConversations $conversationModel,
20        RolePlaySkillProgressions $skillProgressionModel,
21        RolePlayProjects $projectModel
22    ) {
23        $this->conversationModel = $conversationModel;
24        $this->skillProgressionModel = $skillProgressionModel;
25        $this->projectModel = $projectModel;
26    }
27
28    public function getProgressionStats(User $user): array
29    {
30        $skillProgressions = $this->skillProgressionModel->where('status', 'active')->get();
31
32        $criterias = [];
33
34        foreach ($skillProgressions as $progression) {
35            if (empty($progression->scorecard_config) || !is_array($progression->scorecard_config)) {
36                continue;
37            }
38
39            foreach ($progression->scorecard_config as $config) {
40                $projects = $this->projectModel->where('user_id', $user->id)->where('type', $progression->type)->get();
41
42                foreach ($projects as $project) {
43                    $progressions = $project->progression ?? [];
44
45                    $progress = collect($progressions)->first(function ($prog) use ($config) {
46                        return $prog['name'] === $config['name'];
47                    });
48
49                    $existentCriteria = collect($criterias)->first(function ($crit) use ($config, $progression) {
50                        return $crit['name'] === $config['name'] && $crit['type'] === $progression->type;
51                    });
52
53                    if ($existentCriteria) {
54                        $existentCriteria['score'] = ($existentCriteria['score'] + ($progress['score'] ?? 0)) / 2;
55                    } else {
56                        $criterias[] = [
57                            'type' => $progression->type,
58                            'name' => $config['name'],
59                            'score' => $progress['score'] ?? 0,
60                        ];
61                    }
62                }
63            }
64        }
65
66        return $criterias;
67    }
68
69    public function getConversationStatsForPeriod(User $user, ?Carbon $startDate, ?Carbon $endDate): array
70    {
71        $query = $this->conversationModel->where('user_id', $user->id);
72
73        if ($startDate && $endDate) {
74            $query->where('created_at', '>=', new UTCDateTime(strtotime($startDate->toDateString()) * 1000))
75                ->where('created_at', '<=', new UTCDateTime(strtotime($endDate->toDateString()) * 1000));
76        } elseif ($endDate) {
77            $query->where('created_at', '<', new UTCDateTime(strtotime($endDate->toDateString()) * 1000));
78        }
79
80        return [
81            'conversationsCount' => $query->clone()->count() ?? 0,
82            'practiceTimeSeconds' => $query->clone()->sum('duration') ?? 0,
83            'averageScore' => round($query->clone()->avg('score') ?? 0, 2),
84        ];
85    }
86
87    public function getHistoricalWeeklyAverageStats(User $user): array
88    {
89        $historicalQuery = $this->conversationModel->where('user_id', $user->id)
90            ->where('created_at', '<', now()->startOfWeek()->subWeek());
91
92        $firstRecord = $historicalQuery->clone()->orderBy('created_at', 'asc')->first();
93
94        if (!$firstRecord) {
95            return [
96                'conversationsCount' => 0,
97                'practiceTimeSeconds' => 0,
98                'averageScore' => 0,
99            ];
100        }
101
102        $lastRecordDate = $historicalQuery->clone()->orderBy('created_at', 'desc')->first()->created_at;
103        $totalWeeks = ($firstRecord->created_at->diffInWeeks($lastRecordDate) ?? 0) + 1;
104
105        $totalConversations = $historicalQuery->clone()->count() ?? 0;
106        $totalPracticeTime = $historicalQuery->clone()->sum('duration') ?? 0;
107
108        return [
109            'conversationsCount' => $totalWeeks > 0 ? $totalConversations / $totalWeeks : 0,
110            'practiceTimeSeconds' => $totalWeeks > 0 ? $totalPracticeTime / $totalWeeks : 0,
111            'averageScore' => round($historicalQuery->clone()->avg('score') ?? 0, 2),
112        ];
113    }
114
115    public function calculateEvolution(float $current, float $previous): float
116    {
117        if ($previous == 0) {
118            return $current > 0 ? 100.00 : 0.00;
119        }
120
121        $evolution = (($current - $previous) / $previous) * 100;
122
123        return round($evolution, 2);
124    }
125}