Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
93.22% |
55 / 59 |
|
60.00% |
3 / 5 |
CRAP | |
0.00% |
0 / 1 |
| DashboardMetricService | |
93.22% |
55 / 59 |
|
60.00% |
3 / 5 |
20.12 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| getProgressionStats | |
91.30% |
21 / 23 |
|
0.00% |
0 / 1 |
8.04 | |||
| getConversationStatsForPeriod | |
81.82% |
9 / 11 |
|
0.00% |
0 / 1 |
4.10 | |||
| getHistoricalWeeklyAverageStats | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
4 | |||
| calculateEvolution | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Services\RolePlay; |
| 4 | |
| 5 | use App\Http\Models\Auth\User; |
| 6 | use App\Http\Models\RolePlayConversations; |
| 7 | use App\Http\Models\RolePlayProjects; |
| 8 | use App\Http\Models\RolePlaySkillProgressions; |
| 9 | use Carbon\Carbon; |
| 10 | use MongoDB\BSON\UTCDateTime; |
| 11 | |
| 12 | class 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 | } |