UserStatsService   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 9
eloc 59
c 4
b 0
f 0
dl 0
loc 112
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getUsersByRole() 0 15 1
A getDownloadsPerDay() 0 23 3
A getSummaryStats() 0 10 1
A getApiHitsPerDay() 0 24 3
A getTopDownloaders() 0 13 1
1
<?php
2
3
namespace App\Services;
4
5
use App\Models\User;
6
use App\Models\UserDownload;
7
use App\Models\UserRequest;
8
use Carbon\Carbon;
9
use Illuminate\Support\Facades\DB;
10
11
class UserStatsService
12
{
13
    /**
14
     * Get user statistics by role
15
     */
16
    public function getUsersByRole(): array
17
    {
18
        $usersByRole = User::query()
19
            ->join('roles', 'users.roles_id', '=', 'roles.id')
20
            ->select('roles.name as role_name', DB::raw('COUNT(users.id) as count'))
21
            ->whereNull('users.deleted_at')
22
            ->groupBy('roles.id', 'roles.name')
23
            ->get();
24
25
        return $usersByRole->map(function ($item) {
26
            return [
27
                'role' => $item->role_name,
28
                'count' => $item->count,
29
            ];
30
        })->toArray();
31
    }
32
33
    /**
34
     * Get downloads per day for the last 7 days
35
     */
36
    public function getDownloadsPerDay(int $days = 7): array
37
    {
38
        $startDate = Carbon::now()->subDays($days - 1)->startOfDay();
39
40
        $downloads = UserDownload::query()
41
            ->select(DB::raw('DATE(timestamp) as date'), DB::raw('COUNT(*) as count'))
42
            ->where('timestamp', '>=', $startDate)
43
            ->groupBy(DB::raw('DATE(timestamp)'))
44
            ->orderBy('date', 'asc')
0 ignored issues
show
Bug introduced by
'date' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderBy(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

44
            ->orderBy(/** @scrutinizer ignore-type */ 'date', 'asc')
Loading history...
45
            ->get();
46
47
        // Fill in missing days with zero counts
48
        $result = [];
49
        for ($i = $days - 1; $i >= 0; $i--) {
50
            $date = Carbon::now()->subDays($i)->format('Y-m-d');
51
            $found = $downloads->firstWhere('date', $date);
52
            $result[] = [
53
                'date' => Carbon::parse($date)->format('M d'),
54
                'count' => $found ? $found->count : 0,
55
            ];
56
        }
57
58
        return $result;
59
    }
60
61
    /**
62
     * Get API hits per day for the last 7 days
63
     * Note: This tracks actual API requests from user_requests table
64
     */
65
    public function getApiHitsPerDay(int $days = 7): array
66
    {
67
        $startDate = Carbon::now()->subDays($days - 1)->startOfDay();
68
69
        // Track actual API requests from user_requests table
70
        $apiHits = UserRequest::query()
71
            ->select(DB::raw('DATE(timestamp) as date'), DB::raw('COUNT(*) as count'))
72
            ->where('timestamp', '>=', $startDate)
73
            ->groupBy(DB::raw('DATE(timestamp)'))
74
            ->orderBy('date', 'asc')
0 ignored issues
show
Bug introduced by
'date' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderBy(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
            ->orderBy(/** @scrutinizer ignore-type */ 'date', 'asc')
Loading history...
75
            ->get();
76
77
        // Fill in missing days with zero counts
78
        $result = [];
79
        for ($i = $days - 1; $i >= 0; $i--) {
80
            $date = Carbon::now()->subDays($i)->format('Y-m-d');
81
            $found = $apiHits->firstWhere('date', $date);
82
            $result[] = [
83
                'date' => Carbon::parse($date)->format('M d'),
84
                'count' => $found ? $found->count : 0,
85
            ];
86
        }
87
88
        return $result;
89
    }
90
91
    /**
92
     * Get summary statistics
93
     */
94
    public function getSummaryStats(): array
95
    {
96
        $today = Carbon::now()->startOfDay();
97
98
        return [
99
            'total_users' => User::whereNull('deleted_at')->count(),
100
            'downloads_today' => UserDownload::where('timestamp', '>=', $today)->count(),
101
            'downloads_week' => UserDownload::where('timestamp', '>=', Carbon::now()->subDays(7))->count(),
102
            'api_hits_today' => UserRequest::query()->where('timestamp', '>=', $today)->count(),
103
            'api_hits_week' => UserRequest::query()->where('timestamp', '>=', Carbon::now()->subDays(7))->count(),
104
        ];
105
    }
106
107
    /**
108
     * Get top downloaders
109
     */
110
    public function getTopDownloaders(int $limit = 5): array
111
    {
112
        $weekAgo = Carbon::now()->subDays(7);
113
114
        return UserDownload::query()
115
            ->join('users', 'user_downloads.users_id', '=', 'users.id')
116
            ->select('users.username', DB::raw('COUNT(*) as download_count'))
117
            ->where('user_downloads.timestamp', '>=', $weekAgo)
118
            ->groupBy('users.id', 'users.username')
119
            ->orderByDesc('download_count')
0 ignored issues
show
Bug introduced by
'download_count' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderByDesc(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
            ->orderByDesc(/** @scrutinizer ignore-type */ 'download_count')
Loading history...
120
            ->limit($limit)
121
            ->get()
122
            ->toArray();
123
    }
124
}
125