Completed
Push — master ( 4f00d5...2afce4 )
by John
29s queued 13s
created

SiteRank::getRecords()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 22
c 1
b 0
f 0
nc 32
nop 1
dl 0
loc 29
rs 8.9457
1
<?php
2
3
namespace App\Models\Eloquent\Tool;
4
5
use App\Models\Eloquent\ProblemSolution;
6
use App\Models\Eloquent\Submission;
7
use App\Models\Eloquent\User;
8
use Arr;
9
use Cache;
10
use Carbon;
11
use DB;
12
13
class SiteRank
14
{
15
    private static $professionalRanking = [
16
        "Legendary Grandmaster" => "cm-colorful-text",
17
        "International Grandmaster" => "wemd-pink-text",
18
        "Grandmaster" => "wemd-red-text",
19
        "International Master" => "wemd-amber-text",
20
        "Master" => "wemd-orange-text",
21
        "Candidate Master" => "wemd-purple-text",
22
        "Expert" => "wemd-blue-text",
23
        "Specialist" => "wemd-cyan-text",
24
        "Pupil" => "wemd-green-text",
25
        "Newbie" => "wemd-gray-text",
26
    ];
27
28
    private static $professionalRankingPer = [
29
        "Legendary Grandmaster" => 3000,
30
        "International Grandmaster" => 2600,
31
        "Grandmaster" => 2400,
32
        "International Master" => 2300,
33
        "Master" => 2100,
34
        "Candidate Master" => 1900,
35
        "Expert" => 1600,
36
        "Specialist" => 1400,
37
        "Pupil" => 1200,
38
        "Newbie" => 1,
39
    ];
40
41
    private static $casualRanking = [
42
        "Fleet Admiral" => "cm-colorful-text",
43
        "Admiral" => "wemd-pink-text",
44
        "Vice Admiral" => "wemd-red-text",
45
        "Captain" => "wemd-deep-orange-text",
46
        "Commander" => "wemd-orange-text",
47
        "Lieutenant Commander" => "wemd-purple-text",
48
        "Lieutenant" => "wemd-blue-text",
49
        "Ensign" => "wemd-cyan-text",
50
        "Apprentice" => "wemd-green-text",
51
        "Recruit" => "wemd-gray-text",
52
    ];
53
54
    private static $casualRankingPer = [
55
        "Fleet Admiral" => 1,
56
        "Admiral" => 5,
57
        "Vice Admiral" => 10,
58
        "Captain" => 10,
59
        "Commander" => 50,
60
        "Lieutenant Commander" => 100,
61
        "Lieutenant" => 300,
62
        "Ensign" => 700,
63
        "Apprentice" => 1000,
64
        "Recruit" => 400,
65
    ];
66
67
    public static function getColor($rankTitle)
68
    {
69
        if (is_null($rankTitle)) {
70
            return "";
71
        }
72
        return self::$casualRanking[$rankTitle];
73
    }
74
75
    public static function getProfessionalColor($rankTitle)
76
    {
77
        if (is_null($rankTitle)) {
78
            return self::$professionalRanking["None"];
79
        }
80
        return self::$professionalRanking[$rankTitle];
81
    }
82
83
    public static function list($num)
84
    {
85
        $rankList = Cache::tags(['rank'])->get('general');
86
        if ($rankList == null) {
87
            $rankList = [];
88
        }
89
        $rankList = collect($rankList)->slice(0, $num);
90
        $userIDArr = $rankList->pluck('uid');
91
        $userInfoRaw = User::whereIntegerInRaw('id', $userIDArr)->get();
92
        $userInfo = [];
93
        foreach ($userInfoRaw as $u) {
94
            $userInfo[$u->id] = $u;
95
        }
96
        return $rankList->map(function ($item) use ($userInfo) {
97
            $item["details"] = isset($userInfo[$item["uid"]]) ? $userInfo[$item["uid"]] : [];
98
            return $item;
99
        });
100
    }
101
102
    private static function getRecords(Carbon $from = null)
103
    {
104
        $userAcceptedRecords = Submission::select("uid", DB::raw("count(distinct pid) as solved"))->where("verdict", "Accepted");
105
        $userCommunityRecords = ProblemSolution::select("uid", DB::raw("count(distinct pid) as community"))->where("audit", 1);
106
        if(filled($from)){
107
            $userAcceptedRecords = $userAcceptedRecords->where("submission_date", ">", $from->timestamp);
108
            $userCommunityRecords = $userCommunityRecords->where("created_at", ">", $from);
109
        }
110
        $userAcceptedRecords = collect($userAcceptedRecords->groupBy("uid")->get()->toArray());
111
        $userCommunityRecords = collect($userCommunityRecords->groupBy("uid")->get()->toArray());
112
        $totUserRecords = $userAcceptedRecords->pluck('uid')->merge($userCommunityRecords->pluck('uid'))->unique();
113
        $rankList = [];
114
        foreach($totUserRecords as $uid) {
115
            $rankList[$uid]['uid'] = $uid;
116
            $rankList[$uid]['solved'] = 0;
117
            $rankList[$uid]['community'] = 0;
118
            $rankList[$uid]['tot'] = 0;
119
        }
120
        foreach($userAcceptedRecords as $userAcceptedRecord) {
121
            $rankList[$userAcceptedRecord['uid']]['solved'] = $userAcceptedRecord['solved'];
122
        }
123
        foreach($userCommunityRecords as $userCommunityRecord) {
124
            $rankList[$userCommunityRecord['uid']]['community'] = $userCommunityRecord['community'];
125
        }
126
        foreach($rankList as &$rankItem) {
127
            $rankItem['tot'] = $rankItem['solved'] + $rankItem['community'];
128
        }
129
        unset($rankItem);
130
        return $rankList;
131
    }
132
133
    private static function parseCoefficient($rankList)
134
    {
135
        $activityCoefficient = self::getRecords(Carbon::parse('-1 months'));
136
        $activityCoefficientDivider = collect($activityCoefficient)->max('tot');
137
        if(blank($activityCoefficientDivider)) {
138
            $activityCoefficientDivider = 1;
139
        }
140
        foreach ($rankList as $uid => $rankItem) {
141
            if(isset($activityCoefficient[$uid])){
142
                $activityTot = $activityCoefficient[$uid]['tot'];
143
            } else {
144
                $activityTot = 0;
145
            }
146
            $rankList[$uid]["activityCoefficient"] = ($activityTot / $activityCoefficientDivider) + 0.5;
147
            $rankList[$uid]["points"] = $rankList[$uid]["tot"] * $rankList[$uid]["activityCoefficient"];
148
        }
149
        usort($rankList, function($a, $b) {
150
            return $b['points'] <=> $a['points'];
151
        });
152
        return collect($rankList);
153
    }
154
155
    public static function rankList()
156
    {
157
        Cache::tags(['rank'])->flush();
158
        $rankList = self::getRecords();
159
        $totUsers = count($rankList);
160
        if ($totUsers > 0) {
161
            // $rankList = DB::select("SELECT *,solvedCount+communityCount as totValue, 1 as activityCoefficient FROM (SELECT uid,sum(solvedCount) as solvedCount,sum(communityCount) as communityCount FROM ((SELECT uid,count(DISTINCT submission.pid) as solvedCount,0 as communityCount from submission where verdict=\"Accepted\" group by uid) UNION (SELECT uid,0 as solvedCount,count(DISTINCT pid) from problem_solution where audit=1 group by uid)) as temp GROUP BY uid) as temp2 ORDER BY solvedCount+communityCount DESC");
162
            $rankList = self::parseCoefficient($rankList);
163
            $rankIter = 1;
164
            $rankValue = 1;
165
            $rankSolved = -1;
166
            $rankListCached = [];
167
            self::procRankingPer($totUsers);
168
            foreach ($rankList as $rankItem) {
169
                if ($rankSolved != $rankItem["points"]) {
170
                    $rankValue = $rankIter;
171
                    $rankSolved = $rankItem["points"];
172
                }
173
                $rankTitle = self::getRankTitle($rankValue);
174
                Cache::tags(['rank', $rankItem["uid"]])->put("rank", $rankValue, 86400);
175
                Cache::tags(['rank', $rankItem["uid"]])->put("title", $rankTitle, 86400);
176
                $rankListCached[] = [
177
                    "uid" => $rankItem["uid"],
178
                    "rank" => $rankValue,
179
                    "title" => $rankTitle,
180
                    "titleColor" => self::getColor($rankTitle),
181
                    "solved" => $rankItem["solved"],
182
                    "community" => $rankItem["community"],
183
                    "activityCoefficient" => $rankItem["activityCoefficient"],
184
                ];
185
                $rankIter++;
186
            }
187
            Cache::tags(['rank'])->put("general", $rankListCached, 86400);
188
        }
189
    }
190
191
    public static function getProfessionalRanking()
192
    {
193
        $professionalRankList = [];
194
        $verifiedUsers = User::all();
195
        $rankIter = 0;
196
        foreach ($verifiedUsers as $user) {
197
            $rankVal = $user->professional_rate;
198
            $rankTitle = self::getProfessionalTitle($rankVal);
199
            $titleColor = self::getProfessionalColor($rankTitle);
200
            $professionalRankList[$rankIter++] = [
201
                "name" => $user->name,
202
                "uid" => $user->id,
203
                "avatar" => $user->avatar,
204
                "professionalRate" => $user->professional_rate,
205
                "rankTitle" => $rankTitle,
206
                "titleColor" => $titleColor
207
            ];
208
        }
209
        return $professionalRankList;
210
    }
211
212
    private static function procRankingPer($totUsers)
213
    {
214
        if ($totUsers > 0) {
215
            $tot = 0;
216
            $cur = 0;
217
            foreach (self::$casualRankingPer as $c) {
218
                $tot += $c;
219
            }
220
            foreach (self::$casualRankingPer as &$c) {
221
                $c = round($c * $totUsers / $tot);
222
                $cur += $c;
223
                $c = $cur;
224
            }
225
            $c = $totUsers;
226
            unset($c);
227
        }
228
    }
229
230
    public static function getRankTitle($rankVal)
231
    {
232
        foreach (self::$casualRankingPer as $title => $c) {
233
            if ($rankVal <= $c) {
234
                return $title;
235
            }
236
        }
237
        return Arr::last(self::$casualRankingPer);
238
    }
239
240
    public static function getProfessionalTitle($rankVal)
241
    {
242
        foreach (self::$professionalRankingPer as $title => $point) {
243
            if ($rankVal >= $point) {
244
                return $title;
245
            }
246
        }
247
        return Arr::last(self::$professionalRankingPer);
248
    }
249
}
250