Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.28% covered (warning)
69.28%
627 / 905
20.00% covered (danger)
20.00%
4 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
SetsController
68.97% covered (warning)
68.97%
618 / 896
20.00% covered (danger)
20.00%
4 / 20
1581.85
0.00% covered (danger)
0.00%
0 / 1
 sandbox
87.18% covered (warning)
87.18%
68 / 78
0.00% covered (danger)
0.00%
0 / 1
19.76
 create
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 1
42
 remove
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
56
 add
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 index
100.00% covered (success)
100.00%
73 / 73
100.00% covered (success)
100.00%
1 / 1
15
 getDifficultyAndSolved
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
90
 getFirstUnsolvedSetConnectionId
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
4.01
 ui
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
42
 decodeQueryType
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 addTsumego
86.21% covered (warning)
86.21%
25 / 29
0.00% covered (danger)
0.00%
0 / 1
6.09
 view
71.54% covered (warning)
71.54%
264 / 369
0.00% covered (danger)
0.00%
0 / 1
313.79
 updateAchievementConditions
84.00% covered (warning)
84.00%
21 / 25
0.00% covered (danger)
0.00%
0 / 1
5.10
 findUt
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getDifficultyColor
54.55% covered (warning)
54.55%
6 / 11
0.00% covered (danger)
0.00%
0 / 1
9.38
 getSizeColor
53.33% covered (warning)
53.33%
8 / 15
0.00% covered (danger)
0.00%
0 / 1
7.54
 getDateColor
23.53% covered (danger)
23.53%
4 / 17
0.00% covered (danger)
0.00%
0 / 1
36.62
 getSolvedColor
95.83% covered (success)
95.83%
46 / 48
0.00% covered (danger)
0.00%
0 / 1
3
 getExistingRanksArray
100.00% covered (success)
100.00%
50 / 50
100.00% covered (success)
100.00%
1 / 1
1
 resetProgress
92.59% covered (success)
92.59%
25 / 27
0.00% covered (danger)
0.00%
0 / 1
6.01
 changeCollectionSize
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2
3use function PHPUnit\Framework\isNull;
4
5App::uses('SgfParser', 'Utility');
6App::uses('TsumegoUtil', 'Utility');
7App::uses('AppException', 'Utility');
8App::uses('TsumegoButton', 'Utility');
9App::uses('TsumegoButtons', 'Utility');
10App::uses('SetsSelector', 'Utility');
11App::uses('AdminActivityLogger', 'Utility');
12App::uses('AdminActivityType', 'Model');
13App::uses('Progress', 'Utility');
14
15class SetsController extends AppController
16{
17    public $helpers = ['Html', 'Form'];
18
19    public $title = 'tsumego-hero.com';
20
21    /**
22     * @return void
23     */
24    public function sandbox()
25    {
26        $this->loadModel('User');
27        $this->loadModel('Tsumego');
28        $this->loadModel('TsumegoStatus');
29        $this->loadModel('Favorite');
30        $this->loadModel('SetConnection');
31
32        $this->set('_page', 'sandbox');
33        $this->set('_title', 'Tsumego Hero - Collections');
34        $setsNew = [];
35
36        if (isset($this->params['url']['restore']))
37        {
38            $restore = $this->Set->findById($this->params['url']['restore']);
39            if ($restore['Set']['public'] == -1)
40            {
41                $restore['Set']['public'] = 0;
42                $this->Set->save($restore);
43            }
44        }
45
46        $sets = $this->Set->find('all', [
47            'order' => ['Set.order'],
48            'conditions' => ['public' => 0],
49        ]) ?: [];
50        $u = $this->User->findById(Auth::getUserID());
51
52        if (Auth::isLoggedIn())
53        {
54            $uts = $this->TsumegoStatus->find('all', ['conditions' => ['user_id' => Auth::getUserID()]]);
55            if (!$uts)
56                $uts = [];
57            $tsumegoStatusMap = [];
58            $utsCount4 = count($uts);
59            for ($l = 0; $l < $utsCount4; $l++)
60                $tsumegoStatusMap[$uts[$l]['TsumegoStatus']['tsumego_id']] = $uts[$l]['TsumegoStatus']['status'];
61        }
62        $overallCounter = 0;
63
64        $setsCount = count($sets);
65        for ($i = 0; $i < $setsCount; $i++)
66        {
67            $ts = TsumegoUtil::collectTsumegosFromSet($sets[$i]['Set']['id']);
68            $sets[$i]['Set']['anz'] = count($ts);
69            $counter = 0;
70            $elo = 0;
71            $tsCount3 = count($ts);
72            for ($k = 0; $k < $tsCount3; $k++)
73            {
74
75                $elo += $ts[$k]['Tsumego']['rating'];
76                if (Auth::isLoggedIn())
77                    if (isset($tsumegoStatusMap[$ts[$k]['Tsumego']['id']]))
78                        if ($tsumegoStatusMap[$ts[$k]['Tsumego']['id']] == 'S' || $tsumegoStatusMap[$ts[$k]['Tsumego']['id']] == 'W' || $tsumegoStatusMap[$ts[$k]['Tsumego']['id']] == 'C')
79                            $counter++;
80            }
81            if (count($ts) > 0)
82                $elo = $elo / count($ts);
83            else
84                $elo = 0;
85            $date = new DateTime($sets[$i]['Set']['created']);
86            $month = date('F', strtotime($sets[$i]['Set']['created']));
87            $setday = $date->format('d. ');
88            $setyear = $date->format('Y');
89            if ($setday[0] == 0)
90                $setday = substr($setday, -3);
91            $sets[$i]['Set']['created'] = $date->format('Ymd');
92            $sets[$i]['Set']['createdDisplay'] = $setday . $month . ' ' . $setyear;
93            $percent = 0;
94            if (count($ts) > 0)
95                $percent = $counter / count($ts) * 100;
96            $overallCounter += count($ts);
97            $sets[$i]['Set']['solvedNum'] = $counter;
98            $sets[$i]['Set']['solved'] = round($percent, 1);
99            $sets[$i]['Set']['solvedColor'] = $this->getSolvedColor($sets[$i]['Set']['solved']);
100            $sets[$i]['Set']['topicColor'] = $sets[$i]['Set']['color'];
101            $sets[$i]['Set']['difficultyColor'] = $this->getDifficultyColor($sets[$i]['Set']['difficulty']);
102            $sets[$i]['Set']['sizeColor'] = $this->getSizeColor($sets[$i]['Set']['anz']);
103            $sets[$i]['Set']['dateColor'] = $this->getDateColor($sets[$i]['Set']['created']);
104
105            $sn = [];
106            $sn['id'] = $sets[$i]['Set']['id'];
107            $sn['name'] = $sets[$i]['Set']['title'];
108            $sn['amount'] = count($ts);
109            $sn['color'] = $sets[$i]['Set']['color'];
110            $sn['difficulty'] = Rating::getReadableRankFromRating($elo);
111            $sn['solved'] = round($percent, 1);
112            array_push($setsNew, $sn);
113        }
114
115        $adminsList = $this->User->find('all', ['order' => 'id ASC', 'conditions' => ['isAdmin >' => 0]]) ?: [];
116        $admins = [];
117        foreach ($adminsList as $item)
118            $admins[] = $item['User']['name'];
119
120        $this->set('admins', $admins);
121        $this->set('sets', $sets);
122        $this->set('setsNew', $setsNew);
123        $this->set('overallCounter', $overallCounter);
124    }
125
126    /**
127     * @param int|null $tid Tsumego ID
128     * @return void
129     */
130    public function create($tid = null)
131    {
132        $this->loadModel('Tsumego');
133        $this->loadModel('SetConnection');
134        $redirect = false;
135        $t = [];
136        if (isset($this->data['Set']))
137        {
138            $s = $this->Set->find('all', ['order' => 'id DESC']);
139            if (!$s)
140                $s = [];
141            $ss = [];
142            $sCount = count($s);
143            for ($i = 0; $i < $sCount; $i++)
144                if ($s[$i]['Set']['id'] < 6472)
145                    array_push($ss, $s[$i]);
146
147            $seed = str_split('abcdefghijklmnopqrstuvwxyz0123456789');
148            shuffle($seed);
149            $rand = '';
150            foreach (array_rand($seed, 6) as $k)
151                $rand .= $seed[$k];
152            $hashName = '6473k339312/_' . $rand . '_' . $this->data['Set']['title'];
153            $hashName2 = '_' . $rand . '_' . $this->data['Set']['title'];
154
155            $set = [];
156            $set['Set']['id'] = $ss[0]['Set']['id'] + 1;
157            $set['Set']['title'] = $this->data['Set']['title'];
158            $set['Set']['public'] = 0;
159            $set['Set']['image'] = 'b1.png';
160            $set['Set']['difficulty'] = 4;
161            $set['Set']['author'] = 'various creators';
162            $set['Set']['order'] = Constants::$DEFAULT_SET_ORDER;
163
164            $this->Set->create();
165            $this->Set->save($set);
166
167            $tMax = $this->Tsumego->find('first', ['order' => 'id DESC']);
168
169            $t = [];
170            $t['Tsumego']['id'] = $tMax['Tsumego']['id'] + 1;
171            $t['Tsumego']['difficulty'] = 4;
172            $t['Tsumego']['variance'] = 100;
173            $t['Tsumego']['description'] = 'b to kill';
174            $t['Tsumego']['author'] = Auth::getUser()['name'];
175            $this->Tsumego->create();
176            $this->Tsumego->save($t);
177
178            $sc = [];
179            $sc['SetConnection']['set_id'] = $ss[0]['Set']['id'] + 1;
180            $sc['SetConnection']['tsumego_id'] = $tMax['Tsumego']['id'] + 1;
181            $sc['SetConnection']['num'] = 1;
182            $this->SetConnection->create();
183            $this->SetConnection->save($sc);
184
185            mkdir($hashName, 0777);
186            copy('6473k339312/__new/1.sgf', $hashName . '/1.sgf');
187
188            $redirect = true;
189        }
190        $this->set('t', $t);
191        $this->set('redirect', $redirect);
192    }
193
194    /**
195     * @param int $id Set ID
196     * @return void
197     */
198    public function remove($id)
199    {
200        $this->loadModel('Tsumego');
201        $redirect = false;
202
203        if (isset($this->data['Set']))
204            if (strpos(';' . $this->data['Set']['hash'], '6473k339312-') == 1)
205            {
206                $setID = (int) str_replace('6473k339312-', '', $this->data['Set']['hash']);
207
208                $s = $this->Set->findById($setID);
209                if ($s['Set']['public'] == 0 || $s['Set']['public'] == -1)
210                    $this->Set->delete($setID);
211                $ts = TsumegoUtil::collectTsumegosFromSet($setID);
212                if (count($ts) < 50)
213                    foreach ($ts as $item)
214                        $this->Tsumego->delete($item['Tsumego']['id']);
215                $redirect = true;
216            }
217        //$this->set('t', $t);
218        $this->set('redirect', $redirect);
219    }
220
221    /**
222     * @param int $tid Tsumego ID
223     * @return void
224     */
225    public function add($tid)
226    {
227        $this->loadModel('Tsumego');
228
229        if (isset($this->data['Tsumego']))
230        {
231            $t = [];
232            $t['Tsumego']['difficulty'] = $this->data['Tsumego']['difficulty'];
233            $t['Tsumego']['variance'] = $this->data['Tsumego']['variance'];
234            $t['Tsumego']['description'] = $this->data['Tsumego']['description'];
235            $this->Tsumego->save($t);
236        }
237        $ts = TsumegoUtil::collectTsumegosFromSet($tid);
238        $this->set('t', $ts[0]);
239    }
240
241    public function index(): void
242    {
243        $this->loadModel('User');
244        $this->loadModel('Tsumego');
245        $this->loadModel('Favorite');
246        $this->loadModel('AchievementCondition');
247        $this->loadModel('TsumegoStatus');
248        $this->loadModel('SetConnection');
249        $this->loadModel('UserContribution');
250        $this->set('_page', 'set');
251        $this->set('_title', 'Tsumego Hero - Collections');
252
253        $setTiles = [];
254        $difficultyTiles = [];
255        $sets = [];
256        $tagList = [];
257
258        $overallCounter = 0;
259        $problemsCount = 0;
260        $achievementUpdate = [];
261
262        $tsumegoFilters = new TsumegoFilters();
263        if ($tsumegoFilters->query == 'favorites')
264            $tsumegoFilters->setQuery('topics');
265
266        //setTiles
267        $setsRaw = $this->Set->find('all', [
268            'order' => ['Set.order', 'Set.id'],
269            'conditions' => ['public' => 1],
270        ]) ?: [];
271        foreach ($setsRaw as $set)
272            $setTiles [] = $set['Set']['title'];
273
274        //difficultyTiles
275        $dt = SetsController::getExistingRanksArray();
276        foreach ($dt as $item)
277            $difficultyTiles[] = $item['rank'];
278
279        //tagTiles
280        $tags = $this->Tag->find('all', [
281            'conditions' => [
282                'approved' => 1,
283                'NOT' => ['name' => 'Tsumego'],
284            ],
285        ]);
286
287        $tagTiles = [];
288        foreach ($tags as $tag)
289            $tagTiles[] = $tag['Tag']['name'];
290
291        $setsSelector = new SetsSelector($tsumegoFilters);
292
293        if (Auth::isLoggedIn())
294        {
295            $aCondition = $this->AchievementCondition->find('first', [
296                'order' => 'value DESC',
297                'conditions' => [
298                    'user_id' => Auth::getUserID(),
299                    'category' => 'set']]) ?: [];
300            $aCondition['AchievementCondition']['category'] = 'set';
301            $aCondition['AchievementCondition']['user_id'] = Auth::getUserID();
302            $aCondition['AchievementCondition']['value'] = $overallCounter;
303            ClassRegistry::init('AchievementCondition')->save($aCondition);
304            $achievementChecker = new AchievementChecker();
305            $achievementChecker->checkSetCompletedAchievements();
306            $achievementChecker->finalize();
307            $this->set('achievementUpdate', $achievementChecker->updated);
308            Auth::saveUser();
309        }
310
311        $ranksArray = SetsController::getExistingRanksArray();
312        foreach ($ranksArray as &$rank)
313        {
314            $rank['id'] = $rank['rank'];
315            $rank['name'] = $rank['rank'];
316        }
317
318        if ($tsumegoFilters->query == 'topics' && empty($tsumegoFilters->sets))
319            $queryRefresh = false;
320        elseif ($tsumegoFilters->query == 'difficulty' && empty($tsumegoFilters->ranks))
321            $queryRefresh = false;
322        elseif ($tsumegoFilters->query == 'tags' && empty($tsumegoFilters->tags))
323            $queryRefresh = false;
324        else
325            $queryRefresh = true;
326
327        $this->set('setsSelector', $setsSelector);
328        $this->set('ranksArray', $ranksArray);
329        $this->set('tagList', $tagList);
330        $this->set('setTiles', $setTiles);
331        $this->set('difficultyTiles', $difficultyTiles);
332        $this->set('tagTiles', $tagTiles);
333        $this->set('tsumegoFilters', $tsumegoFilters);
334        $this->set('hasPremium', Auth::hasPremium());
335        $this->set('queryRefresh', $queryRefresh);
336    }
337
338    public static function getDifficultyAndSolved($currentTagIds, $tsumegoStatusMap)
339    {
340        $tagTsumegoDifficulty = ClassRegistry::init('Tsumego')->find('all', ['conditions' => ['id' => $currentTagIds]]);
341        if (!$tagTsumegoDifficulty)
342            $tagTsumegoDifficulty = [];
343        $tagDifficultyResult = 0;
344        $statusCounter = 0;
345        $tagTsumegoDifficultyCount2 = count($tagTsumegoDifficulty);
346        for ($j = 0; $j < $tagTsumegoDifficultyCount2; $j++)
347        {
348            $tagDifficultyResult += $tagTsumegoDifficulty[$j]['Tsumego']['rating'];
349            if (isset($tsumegoStatusMap[$tagTsumegoDifficulty[$j]['Tsumego']['id']]))
350                if ($tsumegoStatusMap[$tagTsumegoDifficulty[$j]['Tsumego']['id']] == 'S' || $tsumegoStatusMap[$tagTsumegoDifficulty[$j]['Tsumego']['id']] == 'W' || $tsumegoStatusMap[$tagTsumegoDifficulty[$j]['Tsumego']['id']] == 'C')
351                    $statusCounter++;
352        }
353        if (count($tagTsumegoDifficulty) > 0)
354            $tagDifficultyResult = $tagDifficultyResult / count($tagTsumegoDifficulty);
355        else
356            $tagDifficultyResult = 0;
357        $tagDifficultyResult = Rating::getReadableRankFromRating($tagDifficultyResult);
358        $return = [];
359        $return['difficulty'] = $tagDifficultyResult;
360        if (count($currentTagIds) > 0)
361            $return['solved'] = round($statusCounter / count($currentTagIds) * 100, 2);
362        else
363            $return['solved'] = 0;
364
365        return $return;
366    }
367
368    /**
369     * Gets the first unsolved set connection ID from a collection of tsumego buttons.
370     * Falls back to the first button if all are solved.
371     *
372     * @param TsumegoButtons $tsumegoButtons Iterator of TsumegoButton objects
373     * @return int|null The setConnectionID of the first unsolved button, or first button if all solved, or null if empty
374     */
375    private function getFirstUnsolvedSetConnectionId($tsumegoButtons)
376    {
377        if (empty($tsumegoButtons))
378            return null;
379        if ($firstUnsolvedButton = array_find((array) $tsumegoButtons, function ($tsumegoButton) {
380            return !TsumegoUtil::isSolvedStatus($tsumegoButton->status);
381        }))
382            return $firstUnsolvedButton->setConnectionID;
383        if ($firstRecentlyUnsolved = array_find((array) $tsumegoButtons, function ($tsumegoButton) {
384            return !TsumegoUtil::isRecentlySolved($tsumegoButton->status);
385        }))
386            return $firstRecentlyUnsolved->setConnectionID;
387        return $tsumegoButtons[0]->setConnectionID;
388    }
389
390    /**
391     * @param int|null $id Set ID
392     * @return void
393     */
394    public function ui($id = null)
395    {
396        $s = $this->Set->findById($id);
397        $redirect = false;
398
399        if (isset($_FILES['adminUpload']))
400        {
401            $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
402            $randstring = 'set_';
403            for ($i = 0; $i < 6; $i++)
404                $randstring .= $characters[rand(0, strlen($characters))];
405            $filename = $randstring . '_' . $_FILES['adminUpload']['name'];
406
407            $errors = [];
408            $file_name = $_FILES['adminUpload']['name'];
409            $file_size = $_FILES['adminUpload']['size'];
410            $file_tmp = $_FILES['adminUpload']['tmp_name'];
411            $file_type = $_FILES['adminUpload']['type'];
412            $array = explode('.', $_FILES['adminUpload']['name']);
413            $file_ext = strtolower(end($array));
414            $extensions = ['png', 'jpg'];
415
416            if (in_array($file_ext, $extensions) === false)
417                $errors[] = 'png/jpg allowed.';
418            if ($file_size > 2097152)
419                $errors[] = 'The file is too large.';
420
421            if (empty($errors) == true)
422            {
423                $uploadfile = $_SERVER['DOCUMENT_ROOT'] . '/app/webroot/img/' . $filename;
424                move_uploaded_file($file_tmp, $uploadfile);
425            }
426
427            $s['Set']['image'] = $filename;
428            $this->Set->save($s);
429
430            $redirect = true;
431        }
432
433        $this->set('id', $id);
434        $this->set('s', $s);
435        $this->set('redirect', $redirect);
436    }
437
438    private function decodeQueryType($input)
439    {
440        if (is_numeric($input))
441            return 'topics';
442        if ($input == 'favorites')
443            return 'favorites';
444        try
445        {
446            Rating::getRankFromReadableRank($input);
447            return 'difficulty';
448        }
449        catch (Exception $e)
450        {
451            return 'tags';
452        }
453    }
454
455    public function addTsumego($setID)
456    {
457        if (!isset($this->data['Tsumego']))
458            return;
459
460        $tsumegoModel = ClassRegistry::init('Tsumego');
461
462        $tsumegoModel->getDataSource()->begin();
463
464        try
465        {
466            $tsumego = [];
467            $tsumego['num'] = $this->data['Tsumego']['num'];
468            $tsumego['difficulty'] = 40;
469            $tsumego['variance'] = $this->data['Tsumego']['variance'];
470            $tsumego['description'] = $this->data['Tsumego']['description'];
471            $tsumego['hint'] = $this->data['Tsumego']['hint'];
472            $tsumego['author'] = $this->data['Tsumego']['author'];
473            $tsumegoModel->create();
474            $tsumegoModel->save($tsumego);
475
476            $tsumego['id'] = $tsumegoModel->id;
477            $setConnection = [];
478            $setConnection['set_id'] = $setID;
479            $setConnection['tsumego_id'] = $tsumego['id'];
480            $setConnection['num'] = $this->data['Tsumego']['num'];
481            ClassRegistry::init('SetConnection')->create();
482            ClassRegistry::init('SetConnection')->save($setConnection);
483
484            // Save SGF if provided (either from textarea or file upload)
485            $fileUpload = isset($_FILES['adminUpload']) && $_FILES['adminUpload']['error'] === UPLOAD_ERR_OK ? $_FILES['adminUpload'] : null;
486            $sgfDataOrFile = $this->data['Tsumego']['sgf'] ?? $fileUpload;
487
488            if ($sgfDataOrFile)
489                ClassRegistry::init('Sgf')->uploadSgf($sgfDataOrFile, $tsumego['id'], Auth::getUserID(), Auth::isAdmin());
490            $tsumegoModel->getDataSource()->commit();
491        }
492        catch (Exception $e)
493        {
494            $tsumegoModel->getDataSource()->rollback();
495            throw $e;
496        }
497        return $this->redirect('/sets/view/' . $setID);
498    }
499
500    public function view(string|int|null $id = null, int $partition = 1): void
501    {
502        // transferring from 1 indexed for humans to 0 indexed for us programmers.
503        $partition = $partition - 1;
504        $this->loadModel('Tsumego');
505        $this->loadModel('TsumegoStatus');
506        $this->loadModel('Favorite');
507        $this->loadModel('AdminActivity');
508        $this->loadModel('TsumegoAttempt');
509        $this->loadModel('ProgressDeletion');
510        $this->loadModel('Achievement');
511        $this->loadModel('AchievementStatus');
512        $this->loadModel('AchievementCondition');
513        $this->loadModel('Sgf');
514        $this->loadModel('SetConnection');
515        $this->loadModel('Tag');
516        $this->loadModel('Tag');
517        $this->loadModel('User');
518        $this->loadModel('UserContribution');
519
520        if (is_null($id))
521            throw new AppException("Set to view not specified");
522
523        if ($id != '1')
524            $this->set('_page', 'set');
525        else
526            $this->set('_page', 'favs');
527        $tsIds = [];
528        $refreshView = false;
529        $avgTime = 0;
530        $accuracy = 0;
531        $allVcActive = false;
532        $allVcInactive = false;
533        $allArActive = false;
534        $allArInactive = false;
535        $allPassActive = false;
536        $allPassInactive = false;
537        $pdCounter = 0;
538        $acS = null;
539        $acA = null;
540
541        $tsumegoFilters = new TsumegoFilters(self::decodeQueryType($id));
542        if (Auth::isLoggedIn())
543            if (Auth::isAdmin())
544            {
545                $aad = $this->AdminActivity->find('first', ['order' => 'id DESC']);
546                // Check if last activity was a problem deletion - if so, actually delete it
547                if (isset($aad['AdminActivity']['type']) && $aad['AdminActivity']['type'] == AdminActivityType::PROBLEM_DELETE)
548                {
549                    $scDelete = $this->SetConnection->find('first', ['order' => 'created DESC', 'conditions' => ['tsumego_id' => $aad['AdminActivity']['tsumego_id']]]);
550                    $this->SetConnection->delete($scDelete['SetConnection']['id']);
551                    $this->Tsumego->delete($aad['AdminActivity']['tsumego_id']);
552                }
553            }
554        if (isset($this->params['url']['add']))
555        {
556            $overallCount = $this->Tsumego->find('first', ['order' => 'id DESC']);
557            $scTcount = $this->SetConnection->find('first', ['conditions' => ['set_id' => $id, 'num' => 1]]);
558            $setCount = $this->Tsumego->findById($scTcount['SetConnection']['tsumego_id']);
559            $setCount['Tsumego']['id'] = $overallCount['Tsumego']['id'] + 1;
560            $setCount['Tsumego']['set_id'] = $scTcount['SetConnection']['set_id'];
561            $setCount['Tsumego']['num'] += 1;
562            $setCount['Tsumego']['variance'] = 100;
563            if (Auth::getUserID() == 72)
564                $setCount['Tsumego']['author'] = 'Joschka Zimdars';
565            elseif (Auth::getUserID() == 1206)
566                $setCount['Tsumego']['author'] = 'Innokentiy Zabirov';
567            elseif (Auth::getUserID() == 3745)
568                $setCount['Tsumego']['author'] = 'Dennis Olevanov';
569            else
570                $setCount['Tsumego']['author'] = Auth::getUser()['name'];
571            $this->Tsumego->create();
572            $this->Tsumego->save($setCount);
573            $set = $this->Set->findById($id);
574            AdminActivityLogger::log(AdminActivityType::PROBLEM_ADD, $this->Tsumego->id, $id, null, $set['Set']['title']);
575        }
576
577        Util::setCookie('lastSet', $id);
578        $tsumegoButtons = new TsumegoButtons($tsumegoFilters, null, $partition, $id);
579        $this->set('startingSetConnectionID', $this->getFirstUnsolvedSetConnectionId($tsumegoButtons));
580
581        if ($tsumegoFilters->query == 'difficulty')
582        {
583            $set = [];
584            $set['Set']['id'] = $id;
585            $set['Set']['title'] = $id . $tsumegoButtons->getPartitionTitleSuffix();
586            $set['Set']['image'] = $id . 'Rank.png';
587            $set['Set']['multiplier'] = 1;
588            $set['Set']['public'] = 1;
589            $elo = Rating::getRankMinimalRatingFromReadableRank($id);
590            $set['Set']['difficulty'] = $elo;
591        }
592        elseif ($tsumegoFilters->query == 'tags')
593        {
594            $set['Set']['id'] = $id;
595            $set['Set']['image'] = '';
596            $set['Set']['multiplier'] = 1;
597            $set['Set']['public'] = 1;
598            $tagName = $this->Tag->findByName($id);
599            if ($tagName && isset($tagName['Tag']['description']))
600                $set['Set']['description'] = $tagName['Tag']['description'];
601            $set['Set']['title'] = $id . $tsumegoButtons->getPartitionTitleSuffix();
602        }
603        elseif ($tsumegoFilters->query == 'topics')
604        {
605            $set = ClassRegistry::init('Set')->findById($id);
606            $set['Set']['title'] = $set['Set']['title'] . $tsumegoButtons->getPartitionTitleSuffix();
607            $allArActive = true;
608            $allArInactive = true;
609            $allPassActive = true;
610            $allPassInactive = true;
611            foreach ($tsumegoButtons as $tsumegoButton)
612            {
613                if (!$tsumegoButton->alternativeResponse)
614                    $allArActive = false;
615                if (!$tsumegoButton->passEnabled)
616                    $allPassActive = false;
617            }
618            foreach ($tsumegoButtons as $tsumegoButton)
619                $tsIds [] = $tsumegoButton->tsumegoID;
620            if ($set['Set']['public'] == 0)
621                $this->set('_page', 'sandbox');
622            $this->set('isFav', false);
623            if (isset($this->data['Set']['title']))
624            {
625                $this->Set->create();
626                $changeSet = $set;
627                $changeSet['Set']['title'] = $this->data['Set']['title'];
628                $changeSet['Set']['title2'] = $this->data['Set']['title2'];
629                $this->set('data', $changeSet['Set']['title']);
630                $this->Set->save($changeSet, true);
631                $oldTitle = $set['Set']['title'];
632                $set = $this->Set->findById($id);
633                AdminActivityLogger::log(AdminActivityType::SET_TITLE_EDIT, null, $id, $oldTitle, $this->data['Set']['title']);
634            }
635            if (isset($this->data['Set']['description']))
636            {
637                $this->Set->create();
638                $changeSet = $set;
639                $changeSet['Set']['description'] = $this->data['Set']['description'];
640                $this->set('data', $changeSet['Set']['description']);
641                $this->Set->save($changeSet, true);
642                $oldDescription = $set['Set']['description'];
643                $set = $this->Set->findById($id);
644                AdminActivityLogger::log(AdminActivityType::SET_DESCRIPTION_EDIT, null, $id, $oldDescription, $this->data['Set']['description']);
645            }
646            if (isset($this->data['Set']['setDifficulty']))
647                if ($this->data['Set']['setDifficulty'] != 1200 && $this->data['Set']['setDifficulty'] >= 900 && $this->data['Set']['setDifficulty'] <= 2900)
648                {
649                    $setDifficultyTsumegoSet = TsumegoUtil::collectTsumegosFromSet($set['Set']['id']);
650                    $setDifficulty = $this->data['Set']['setDifficulty'];
651                    $setDifficultyTsumegoSetCount = count($setDifficultyTsumegoSet);
652                    for ($i = 0; $i < $setDifficultyTsumegoSetCount; $i++)
653                    {
654                        $setDifficultyTsumegoSet[$i]['Tsumego']['rating']
655                            = Util::clampOptional(
656                                $this->data['Set']['setDifficulty'],
657                                $setDifficultyTsumegoSet[$i]['Tsumego']['minimum_rating'],
658                                $setDifficultyTsumegoSet[$i]['Tsumego']['maximum_rating']);
659                        $this->Tsumego->save($setDifficultyTsumegoSet[$i]);
660                    }
661                    AdminActivityLogger::log(AdminActivityType::SET_RATING_EDIT, null, $id);
662                }
663            if (isset($this->data['Set']['color']))
664            {
665                $this->Set->create();
666                $changeSet = $set;
667                $changeSet['Set']['color'] = $this->data['Set']['color'];
668                $this->set('data', $changeSet['Set']['color']);
669                $this->Set->save($changeSet, true);
670                $oldColor = $set['Set']['color'];
671                $set = $this->Set->findById($id);
672                AdminActivityLogger::log(AdminActivityType::SET_COLOR_EDIT, null, $id, $oldColor, $this->data['Set']['color']);
673            }
674            if (isset($this->data['Set']['order']))
675            {
676                $this->Set->create();
677                $changeSet = $set;
678                $changeSet['Set']['order'] = $this->data['Set']['order'];
679                $this->set('data', $changeSet['Set']['order']);
680                $this->Set->save($changeSet, true);
681                $oldOrder = $set['Set']['order'];
682                $set = $this->Set->findById($id);
683                AdminActivityLogger::log(AdminActivityType::SET_ORDER_EDIT, null, $id, $oldOrder, $this->data['Set']['order']);
684            }
685            if (isset($this->data['Settings']))
686            {
687                if ($this->data['Settings']['r39'] == 'on')
688                {
689                    foreach ($tsumegoButtons as $tsumegoButton)
690                    {
691                        $tsumego = ClassRegistry::init('Tsumego')->findById($tsumegoButton->tsumegoID);
692                        $tsumego['alternative_response'] = true;
693                        ClassRegistry::init('Tsumego')->save($tsumego);
694                    }
695                    $allArActive = true;
696                    AdminActivityLogger::log(AdminActivityType::SET_ALTERNATIVE_RESPONSE, null, $id, null, '1');
697                }
698                if ($this->data['Settings']['r39'] == 'off')
699                {
700                    foreach ($tsumegoButtons as $tsumegoButton)
701                    {
702                        $tsumego = ClassRegistry::init('Tsumego')->findById($tsumegoButton->tsumegoID);
703                        $tsumego['alternative_response'] = false;
704                        ClassRegistry::init('Tsumego')->save($tsumego);
705                    }
706                    $allArInactive = true;
707                    AdminActivityLogger::log(AdminActivityType::SET_ALTERNATIVE_RESPONSE, null, $id, null, '0');
708                }
709                if ($this->data['Settings']['r43'] == 'yes')
710                {
711                    foreach ($tsumegoButtons as $tsumegoButton)
712                    {
713                        $tsumego = ClassRegistry::init('Tsumego')->findById($tsumegoButton->tsumegoID);
714                        $tsumego['pass'] = true;
715                        ClassRegistry::init('Tsumego')->save($tsumego);
716                    }
717                    $allPassActive = true;
718                    AdminActivityLogger::log(AdminActivityType::SET_PASS_MODE, null, $id, null, '1');
719                }
720                if ($this->data['Settings']['r43'] == 'no')
721                {
722                    foreach ($tsumegoButtons as $tsumegoButton)
723                    {
724                        $tsumego = ClassRegistry::init('Tsumego')->findById($tsumegoButton->tsumegoID);
725                        $tsumego['pass'] = false;
726                        ClassRegistry::init('Tsumego')->save($tsumego);
727                    }
728                    $allPassInactive = true;
729                    AdminActivityLogger::log(AdminActivityType::SET_PASS_MODE, null, $id, null, '0');
730                }
731                $this->set('formRedirect', true);
732            }
733        }
734        elseif ($tsumegoFilters->query = 'favorites')
735        {
736            $allUts = $this->TsumegoStatus->find('all', ['conditions' => ['user_id' => Auth::getUserID()]]) ?: [];
737            $idMap = [];
738            $statusMap = [];
739            $allUtsCount = count($allUts);
740            for ($i = 0; $i < $allUtsCount; $i++)
741            {
742                array_push($idMap, $allUts[$i]['TsumegoStatus']['tsumego_id']);
743                array_push($statusMap, $allUts[$i]['TsumegoStatus']['status']);
744            }
745            $fav = $this->Favorite->find('all', ['order' => 'created', 'direction' => 'DESC', 'conditions' => ['user_id' => Auth::getUserID()]]) ?: [];
746            if (!empty($fav))
747                $this->set('achievementUpdate', new AchievementChecker()->checkSetAchievements(-1)->finalize()->updated);
748            $ts = [];
749            $difficultyCount = 0;
750            $solvedCount = 0;
751            $sizeCount = 0;
752            $favCount = count($fav);
753            for ($i = 0; $i < $favCount; $i++)
754            {
755                $tx = $this->Tsumego->find('first', ['conditions' => ['id' => $fav[$i]['Favorite']['tsumego_id']]]);
756                $difficultyCount += $tx['Tsumego']['difficulty'];
757                $utx = $this->findUt($fav[$i]['Favorite']['tsumego_id'], $allUts, $idMap);
758                if ($utx['TsumegoStatus']['status'] == 'S' || $utx['TsumegoStatus']['status'] == 'W' || $utx['TsumegoStatus']['status'] == 'C')
759                    $solvedCount++;
760                $sizeCount++;
761                array_push($ts, $tx);
762            }
763            $allUtsCount = count($allUts);
764            for ($i = 0; $i < $allUtsCount; $i++)
765            {
766                $tsCount2 = count($ts);
767                for ($j = 0; $j < $tsCount2; $j++)
768                    if ($allUts[$i]['TsumegoStatus']['tsumego_id'] == $ts[$j]['Tsumego']['id'])
769                        $ts[$j]['Tsumego']['status'] = $allUts[$i]['TsumegoStatus']['status'];
770            }
771            $percent = Util::getPercent($solvedCount, $sizeCount);
772            $set = [];
773            $set['Set']['id'] = 1;
774            $set['Set']['title'] = 'Favorites';
775            $set['Set']['title2'] = null;
776            $set['Set']['author'] = Auth::getUser()['name'];
777            $set['Set']['description'] = '';
778            $set['Set']['image'] = 'fav';
779            $set['Set']['order'] = 0;
780            $set['Set']['public'] = 1;
781            $set['Set']['created'] = 20180322;
782            $set['Set']['createdDisplay'] = '22. March 2018';
783            $set['Set']['solvedNum'] = $sizeCount;
784            $set['Set']['solved'] = round($percent, 1);
785            $set['Set']['solvedColor'] = '#eee';
786            $set['Set']['topicColor'] = '#eee';
787            $set['Set']['difficultyColor'] = '#eee';
788            $set['Set']['sizeColor'] = '#eee';
789            $set['Set']['dateColor'] = '#eee';
790            $this->set('isFav', true);
791        }
792
793        if ($tsumegoButtons->description)
794            $set['Set']['description'] = $tsumegoButtons->description;
795
796        $this->set('_title', $set['Set']['title'] . ' on Tsumego Hero');
797
798        if (Auth::isLoggedIn() && $tsumegoFilters->query == 'topics')
799        {
800            $ur = $this->TsumegoAttempt->find('all', [
801                'order' => 'created DESC',
802                'conditions' => [
803                    'user_id' => Auth::getUserID(),
804                    'tsumego_id' => $tsIds,
805                ],
806            ]) ?: [];
807            foreach ($tsumegoButtons as $tsumegoButton)
808            {
809                $urTemp = [];
810                $urSum = '';
811                $tsumegoButton->seconds = 0;
812                $solvedSeconds = []; // Track all successful solve times to find minimum (best)
813                $urCount2 = count($ur);
814                for ($j = 0; $j < $urCount2; $j++)
815                    if ($tsumegoButton->tsumegoID == $ur[$j]['TsumegoAttempt']['tsumego_id'])
816                    {
817                        array_push($urTemp, $ur[$j]);
818                        if ($ur[$j]['TsumegoAttempt']['solved'])
819                            $solvedSeconds[] = $ur[$j]['TsumegoAttempt']['seconds'];
820
821                        if (!$ur[$j]['TsumegoAttempt']['solved'])
822                        {
823                            $mis = $ur[$j]['TsumegoAttempt']['misplays'];
824                            if ($mis == 0)
825                                $mis = 1;
826                            while ($mis > 0)
827                            {
828                                $urSum .= 'F';
829                                $mis--;
830                            }
831                        }
832                        else
833                            $urSum .= $ur[$j]['TsumegoAttempt']['solved'];
834                    }
835                // Use minimum (best) solve time from all successful attempts
836                if (!empty($solvedSeconds))
837                    $tsumegoButton->seconds = min($solvedSeconds);
838                $tsumegoButton->performance = $urSum;
839            }
840        }
841
842        $problemSolvedPercent = $tsumegoButtons->getProblemsSolvedPercent();
843        $setRating = $tsumegoButtons->getProblemsRating();
844        $this->set('setRating', $setRating);
845
846        $this->set('problemSolvedPercent', $problemSolvedPercent);
847
848        $scoring = true;
849        if (Auth::isLoggedIn() && $tsumegoFilters->query == 'topics')
850        {
851            $pd = $this->ProgressDeletion->find('all', [
852                'conditions' => [
853                    'user_id' => Auth::getUserID(),
854                    'set_id' => $id]]) ?: [];
855            $pdCounter = 0;
856            $pdCount = count($pd);
857            for ($i = 0; $i < $pdCount; $i++)
858            {
859                $date = date_create($pd[$i]['ProgressDeletion']['created']);
860                $pd[$i]['ProgressDeletion']['d'] = $date->format('Y') . '-' . $date->format('m');
861                if (date('Y-m') == $pd[$i]['ProgressDeletion']['d'])
862                    $pdCounter++;
863            }
864            $urSecCounter = 0;
865            $urSecAvg = 0;
866            $pSsum = 0;
867            $pFsum = 0;
868            foreach ($tsumegoButtons as $tsumegoButton)
869            {
870                if ($tsumegoButton->seconds == 0)
871                    if (TsumegoUtil::isSolvedStatus($tsumegoButton->status))
872                        $tss = 60;
873                    else
874                        $tss = 0;
875                else
876                    $tss = $tsumegoButton->seconds;
877                $urSecAvg += $tss;
878                $urSecCounter++;
879
880                if ($tsumegoButton->performance == '')
881                    if (TsumegoUtil::isSolvedStatus($tsumegoButton->status))
882                        $tss2 = 'F';
883                    else
884                        $tss2 = '';
885                else
886                    $tss2 = $tsumegoButton->performance;
887                $pS = substr_count($tss2, '1');
888                $pF = substr_count($tss2, 'F');
889                $pSsum += $pS;
890                $pFsum += $pF;
891            }
892            if ($urSecCounter == 0)
893                $avgTime = 60;
894            else
895                $avgTime = round($urSecAvg / $urSecCounter, 2);
896            if ($pSsum + $pFsum == 0)
897                $accuracy = 0;
898            else
899                $accuracy = round($pSsum / ($pSsum + $pFsum) * 100, 2);
900            $avgTime2 = $avgTime;
901            if ($problemSolvedPercent >= 100)
902            {
903                $achievementChecker = new AchievementChecker();
904                if ($set['Set']['id'] != 210)
905                {
906                    $this->updateAchievementConditions($set['Set']['id'], $avgTime2, $accuracy);
907                    $achievementChecker->checkSetAchievements($set['Set']['id'], $setRating);
908                }
909                if ($id == 50 || $id == 52 || $id == 53 || $id == 54)
910                    $achievementChecker->setAchievementSpecial('cc1');
911                elseif ($id == 41 || $id == 49 || $id == 65 || $id == 66)
912                    $achievementChecker->setAchievementSpecial('cc2');
913                elseif ($id == 186 || $id == 187 || $id == 196 || $id == 203)
914                    $achievementChecker->setAchievementSpecial('cc3');
915                elseif ($id == 190 || $id == 193 || $id == 198)
916                    $achievementChecker->setAchievementSpecial('1000w1');
917                elseif ($id == 216)
918                    $achievementChecker->setAchievementSpecial('1000w2');
919                $achievementChecker->finalize();
920                $this->set('achievementUpdate', $achievementChecker->updated);
921            }
922
923            $acS = $this->AchievementCondition->find('first', [
924                'order' => 'value ASC',
925                'conditions' => [
926                    'set_id' => $id,
927                    'user_id' => Auth::getUserID(),
928                    'category' => 's']]);
929            $acA = $this->AchievementCondition->find('first', [
930                'order' => 'value DESC',
931                'conditions' => [
932                    'set_id' => $id,
933                    'user_id' => Auth::getUserID(),
934                    'category' => '%']]);
935        }
936        else
937            $scoring = false;
938
939        $allTags = $this->Tag->find('all') ?: [];
940        $allTagsSorted = [];
941        $allTagsKeys = [];
942        $allTagsCount = count($allTags);
943        for ($i = 0; $i < $allTagsCount; $i++)
944        {
945            array_push($allTagsSorted, $allTags[$i]['Tag']['name']);
946            $allTagsKeys[$allTags[$i]['Tag']['name']] = $allTags[$i];
947        }
948        sort($allTagsSorted);
949        $s2Tags = [];
950        $allTagsSortedCount = count($allTagsSorted);
951        for ($i = 0; $i < $allTagsSortedCount; $i++)
952            array_push($s2Tags, $allTagsKeys[$allTagsSorted[$i]]);
953
954        $allTags = $s2Tags;
955
956        if ($tsumegoFilters->query == 'topics')
957        {
958            $this->set('allVcActive', $allVcActive);
959            $this->set('allVcInactive', $allVcInactive);
960            $this->set('allArActive', $allArActive);
961            $this->set('allArInactive', $allArInactive);
962            $this->set('allPassActive', $allPassActive);
963            $this->set('allPassInactive', $allPassInactive);
964            $this->set('pdCounter', $pdCounter);
965            $this->set('acS', $acS);
966            $this->set('acA', $acA);
967        }
968
969        $this->set('tsumegoFilters', $tsumegoFilters);
970        $this->set('allTags', $allTags);
971        $this->set('tsumegoButtons', $tsumegoButtons);
972        $this->set('set', $set);
973        $this->set('refreshView', $refreshView);
974        $this->set('avgTime', $avgTime);
975        $this->set('accuracy', $accuracy);
976        $this->set('scoring', $scoring);
977        $this->set('partition', $partition);
978    }
979
980    /**
981     * @param int $sid Set ID
982     * @param float $avgTime Average time
983     * @param float $accuracy Accuracy percentage
984     * @return void
985     */
986    public function updateAchievementConditions($sid, $avgTime, $accuracy)
987    {
988        $uid = Auth::getUserID();
989        $acS = $this->AchievementCondition->find('first', ['order' => 'value ASC', 'conditions' => ['set_id' => $sid, 'user_id' => $uid, 'category' => 's']]);
990        $acA = $this->AchievementCondition->find('first', ['order' => 'value DESC', 'conditions' => ['set_id' => $sid, 'user_id' => $uid, 'category' => '%']]);
991
992        if ($acS == null)
993        {
994            $aCond = [];
995            $aCond['AchievementCondition']['user_id'] = $uid;
996            $aCond['AchievementCondition']['set_id'] = $sid;
997            $aCond['AchievementCondition']['value'] = $avgTime;
998            $aCond['AchievementCondition']['category'] = 's';
999            $this->AchievementCondition->create();
1000            $this->AchievementCondition->save($aCond);
1001        }
1002        elseif ($avgTime < $acS['AchievementCondition']['value'])
1003        {
1004            $acS['AchievementCondition']['value'] = $avgTime;
1005            $this->AchievementCondition->save($acS);
1006        }
1007        if ($acA == null)
1008        {
1009            $aCond = [];
1010            $aCond['AchievementCondition']['user_id'] = $uid;
1011            $aCond['AchievementCondition']['set_id'] = $sid;
1012            $aCond['AchievementCondition']['value'] = $accuracy;
1013            $aCond['AchievementCondition']['category'] = '%';
1014            $this->AchievementCondition->create();
1015            $this->AchievementCondition->save($aCond);
1016        }
1017        elseif ($accuracy > $acA['AchievementCondition']['value'])
1018        {
1019            $acA['AchievementCondition']['value'] = $accuracy;
1020            $this->AchievementCondition->save($acA);
1021        }
1022    }
1023
1024    private function findUt($id = null, $allUts = null, $map = null)
1025    {
1026        $currentUt = array_search($id, $map);
1027        $ut = $allUts[$currentUt];
1028        if ($currentUt == 0)
1029            if ($id != $map[0])
1030                $ut = null;
1031
1032        return $ut;
1033    }
1034
1035    private function getDifficultyColor($difficulty = null)
1036    {
1037        if ($difficulty == 1)
1038            return '#33cc33';
1039        if ($difficulty == 2)
1040            return '#709533';
1041        if ($difficulty == 3)
1042            return '#2e3370';
1043        if ($difficulty == 4)
1044            return '#ac5d33';
1045        if ($difficulty == 5)
1046            return '#e02e33';
1047
1048        return 'white';
1049    }
1050
1051    private function getSizeColor($size = null)
1052    {
1053        $colors = [];
1054        array_push($colors, '#cc6600');
1055        array_push($colors, '#ac4e26');
1056        array_push($colors, '#963e3e');
1057        array_push($colors, '#802e58');
1058        array_push($colors, '#60167d');
1059        if ($size < 30)
1060            return $colors[0];
1061        if ($size < 60)
1062            return $colors[1];
1063        if ($size < 110)
1064            return $colors[2];
1065        if ($size < 202)
1066            return $colors[3];
1067
1068        return $colors[4];
1069    }
1070
1071    private function getDateColor($date = null)
1072    {
1073        $current = '20180705';
1074        $dist = $current - $date;
1075
1076        if ($dist < 7)
1077            return '#0033cc';
1078        if ($dist < 100)
1079            return '#0f33ad';
1080        if ($dist < 150)
1081            return '#1f338f';
1082        if ($dist < 200)
1083            return '#2e3370';
1084        if ($dist < 300)
1085            return '#3d3352';
1086        if ($dist < 400)
1087            return '#4c3333';
1088        if ($dist < 500)
1089            return '#57331f';
1090
1091        return '#663300';
1092    }
1093
1094    private function getSolvedColor($percent = null)
1095    {
1096        $colors = [];
1097
1098        array_push($colors, '#333333');
1099        array_push($colors, '#2e3d47');
1100        array_push($colors, '#2b4252');
1101        array_push($colors, '#29475c');
1102        array_push($colors, '#264c66');
1103        array_push($colors, '#245270');
1104        array_push($colors, '#21577a');
1105        array_push($colors, '#1f5c85');
1106        array_push($colors, '#1c618f');
1107        array_push($colors, '#1a6699');
1108
1109        array_push($colors, '#176ba3');
1110        array_push($colors, '#1470ad');
1111        array_push($colors, '#1275b8');
1112        array_push($colors, '#0f7ac2');
1113        array_push($colors, '#0d80cc');
1114        array_push($colors, '#0a85d6');
1115        array_push($colors, '#088ae0');
1116        array_push($colors, '#058feb');
1117        array_push($colors, '#0394f5');
1118        array_push($colors, '#0099ff');
1119
1120        array_push($colors, '#039cf8');
1121        array_push($colors, '#069ef2');
1122        array_push($colors, '#09a1eb');
1123        array_push($colors, '#0ca4e4');
1124        array_push($colors, '#10a6dd');
1125        array_push($colors, '#13a9d6');
1126        array_push($colors, '#16acd0');
1127        array_push($colors, '#19afc9');
1128        array_push($colors, '#1cb1c2');
1129        array_push($colors, '#1fb4bc');
1130
1131        array_push($colors, '#22b7b5');
1132        array_push($colors, '#25b9ae');
1133        array_push($colors, '#28bca7');
1134        array_push($colors, '#2bbfa0');
1135        array_push($colors, '#2ec29a');
1136        array_push($colors, '#32c493');
1137        array_push($colors, '#35c78c');
1138        array_push($colors, '#38ca86');
1139        array_push($colors, '#3bcc7f');
1140        array_push($colors, '#3ecf78');
1141        $steps = 2.5;
1142        $colorsCount = count($colors);
1143        for ($i = 0; $i < $colorsCount; $i++)
1144        {
1145            if ($percent <= $steps)
1146                return $colors[$i];
1147            $steps += 2.5;
1148        }
1149
1150        return '#333333';
1151    }
1152
1153    public static function getExistingRanksArray()
1154    {
1155        $ranksArray = [];
1156        $ranksArray[0]['rank'] = '15k';
1157        $ranksArray[1]['rank'] = '14k';
1158        $ranksArray[2]['rank'] = '13k';
1159        $ranksArray[3]['rank'] = '12k';
1160        $ranksArray[4]['rank'] = '11k';
1161        $ranksArray[5]['rank'] = '10k';
1162        $ranksArray[6]['rank'] = '9k';
1163        $ranksArray[7]['rank'] = '8k';
1164        $ranksArray[8]['rank'] = '7k';
1165        $ranksArray[9]['rank'] = '6k';
1166        $ranksArray[10]['rank'] = '5k';
1167        $ranksArray[11]['rank'] = '4k';
1168        $ranksArray[12]['rank'] = '3k';
1169        $ranksArray[13]['rank'] = '2k';
1170        $ranksArray[14]['rank'] = '1k';
1171        $ranksArray[15]['rank'] = '1d';
1172        $ranksArray[16]['rank'] = '2d';
1173        $ranksArray[17]['rank'] = '3d';
1174        $ranksArray[18]['rank'] = '4d';
1175        $ranksArray[19]['rank'] = '5d';
1176        $ranksArray[20]['rank'] = '6d';
1177        $ranksArray[21]['rank'] = '7d';
1178        $ranksArray[22]['rank'] = '8d';
1179        $ranksArray[23]['rank'] = '9d';
1180        $ranksArray[0]['color'] = 'rgba(63,  201, 196, [o])';
1181        $ranksArray[1]['color'] = 'rgba(63, 190, 201, [o])';
1182        $ranksArray[2]['color'] = 'rgba(63, 173, 201, [o])';
1183        $ranksArray[3]['color'] = 'rgba(63, 157, 201, [o])';
1184        $ranksArray[4]['color'] = 'rgba(63, 141, 201, [o])';
1185        $ranksArray[5]['color'] = 'rgba(88, 158, 244, [o])';
1186        $ranksArray[6]['color'] = 'rgba(88, 140, 244, [o])';
1187        $ranksArray[7]['color'] = 'rgba(88, 122, 244, [o])';
1188        $ranksArray[8]['color'] = 'rgba(88, 103, 244, [o])';
1189        $ranksArray[9]['color'] = 'rgba(90, 88, 244, [o])';
1190        $ranksArray[10]['color'] = 'rgba(109, 88, 244, [o])';
1191        $ranksArray[11]['color'] = 'rgba(127, 88, 244, [o])';
1192        $ranksArray[12]['color'] = 'rgba(145, 88, 244, [o])';
1193        $ranksArray[13]['color'] = 'rgba(163, 88, 244, [o])';
1194        $ranksArray[14]['color'] = 'rgba(182, 88, 244, [o])';
1195        $ranksArray[15]['color'] = 'rgba(200, 88, 244, [o])';
1196        $ranksArray[16]['color'] = 'rgba(218, 88, 244, [o])';
1197        $ranksArray[17]['color'] = 'rgba(236, 88, 244, [o])';
1198        $ranksArray[18]['color'] = 'rgba(244, 88, 234, [o])';
1199        $ranksArray[19]['color'] = 'rgba(244, 88, 187, [o])';
1200        $ranksArray[20]['color'] = 'rgba(244, 88, 145, [o])';
1201        $ranksArray[21]['color'] = 'rgba(244, 88, 127, [o])';
1202        $ranksArray[22]['color'] = 'rgba(244, 88, 101, [o])';
1203        $ranksArray[23]['color'] = 'rgba(244, 88, 88, [o])';
1204
1205        return $ranksArray;
1206    }
1207
1208    public function resetProgress(int $setID, int $partition): mixed
1209    {
1210        if (!Auth::isLoggedIn())
1211            return $this->redirect('/sets/view/' . $setID);
1212
1213        if ($this->data['reset-check'] != 'reset')
1214        {
1215            CookieFlash::set('Reset check wasn\'t correctly typed', 'error');
1216            return $this->redirect('/sets/view/' . $setID);
1217        }
1218
1219        $partition = $partition - 1;
1220
1221        $tsumegoFilters = new TsumegoFilters();
1222        if ($tsumegoFilters->collectionSize != 200)
1223        {
1224            CookieFlash::set('Reset is only possible for collection size 200', 'error');
1225            return $this->redirect('/sets/view/' . $setID);
1226        }
1227        $tsumegoFilters->query = 'topics';
1228        $tsumegoButtons = new TsumegoButtons($tsumegoFilters, null, $partition, $setID);
1229        $tsumegoIDToClear = [];
1230        foreach ($tsumegoButtons as $tsumegoButton)
1231            $tsumegoIDToClear[] = $tsumegoButton->tsumegoID;
1232
1233        $problemsInSet = Util::query("
1234SELECT
1235    COUNT(DISTINCT tsumego.id) AS total
1236FROM tsumego
1237    JOIN set_connection ON set_connection.tsumego_id = tsumego.id AND set_connection.set_id = ?", [$setID])[0]["total"];
1238        if (TsumegoStatus::getProblemsSolvedInSet($setID) < $problemsInSet * 0.5)
1239            return $this->redirect('sets/' . $setID);
1240
1241        Util::query("
1242DELETE tsumego_status FROM tsumego_status
1243WHERE tsumego_status.user_id = ? AND tsumego_status.tsumego_id IN(" . implode(',', $tsumegoIDToClear) . ")", [Auth::getUserID()]);
1244        $progresDeletion = [];
1245        $progresDeletion['user_id'] = Auth::getUserID();
1246        $progresDeletion['set_id'] = $setID;
1247        ClassRegistry::init('ProgressDeletion')->create();
1248        ClassRegistry::init('ProgressDeletion')->save($progresDeletion);
1249        return $this->redirect('/sets/view/' . $setID);
1250    }
1251
1252    public function changeCollectionSize(): mixed
1253    {
1254        $collectionSize = $this->data['collection_size'];
1255        if (is_null($collectionSize))
1256        {
1257            CookieFlash::set('Collection size to change not provided', 'error');
1258            return $this->redirect('/sets');
1259        }
1260        Preferences::set('collection_size', $this->data['collection_size']);
1261        return $this->redirect('/sets');
1262    }
1263}