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