Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
96.24% |
128 / 133 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
| SetsSelector | |
96.21% |
127 / 132 |
|
80.00% |
4 / 5 |
18 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
| selectByTags | |
100.00% |
27 / 27 |
|
100.00% |
1 / 1 |
3 | |||
| getTagColor | |
100.00% |
27 / 27 |
|
100.00% |
1 / 1 |
1 | |||
| selectByTopics | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
3 | |||
| selectByDifficulty | |
89.36% |
42 / 47 |
|
0.00% |
0 / 1 |
7.06 | |||
| 1 | <?php |
| 2 | |
| 3 | App::uses('Query', 'Utility'); |
| 4 | |
| 5 | class SetsSelector |
| 6 | { |
| 7 | public function __construct($tsumegoFilters) |
| 8 | { |
| 9 | $this->tsumegoFilters = $tsumegoFilters; |
| 10 | if ($this->tsumegoFilters->query == 'tags') |
| 11 | $this->selectByTags(); |
| 12 | elseif ($this->tsumegoFilters->query == 'topics') |
| 13 | $this->selectByTopics(); |
| 14 | elseif ($this->tsumegoFilters->query == 'difficulty') |
| 15 | $this->selectByDifficulty(); |
| 16 | $this->problemsFound = $this->tsumegoFilters->calculateCount(); |
| 17 | } |
| 18 | |
| 19 | private function selectByTags() |
| 20 | { |
| 21 | $innerQuery = new Query('FROM tsumego'); |
| 22 | $innerQuery->selects[] = 'tag.id AS tag_id'; |
| 23 | $innerQuery->selects[] = 'tag.name AS tag_name'; |
| 24 | $innerQuery->selects[] = 'tag.color AS tag_color'; |
| 25 | $innerQuery->selects[] = 'COUNT(tsumego.id) AS total_count'; |
| 26 | $innerQuery->groupBy[] = 'tag.id'; |
| 27 | $innerQuery->query .= ' JOIN tag_connection ON tag_connection.tsumego_id = tsumego.id'; |
| 28 | $innerQuery->query .= ' JOIN tag ON tag_connection.tag_id = tag.id'; |
| 29 | $this->tsumegoFilters->addConditionsToQuery($innerQuery); |
| 30 | |
| 31 | $query = " |
| 32 | WITH tag_counts AS (" . $innerQuery->str() . "), |
| 33 | numbered AS ( |
| 34 | SELECT |
| 35 | tag.id AS tag_id, |
| 36 | tag.name AS tag_name, |
| 37 | tag.color AS tag_color, |
| 38 | tsumego.id AS tsumego_id, |
| 39 | ROW_NUMBER() OVER (PARTITION BY tag.id ORDER BY tsumego.id) AS rn, |
| 40 | tsumego_status.status |
| 41 | FROM tsumego |
| 42 | JOIN tag_connection ON tag_connection.tsumego_id = tsumego.id |
| 43 | JOIN tag ON tag.id = tag_connection.tag_id |
| 44 | LEFT JOIN tsumego_status |
| 45 | ON tsumego_status.user_id = " . Auth::getUserID() . " |
| 46 | AND tsumego_status.tsumego_id = tsumego.id |
| 47 | ), |
| 48 | partitioned AS ( |
| 49 | SELECT |
| 50 | n.tag_name AS name, |
| 51 | n.tag_color AS color, |
| 52 | t.total_count, |
| 53 | CASE |
| 54 | WHEN t.total_count <= " . $this->tsumegoFilters->collectionSize . " THEN -1 |
| 55 | ELSE FLOOR((n.rn - 1) / " . $this->tsumegoFilters->collectionSize . ") |
| 56 | END AS partition_number, |
| 57 | COUNT(*) AS usage_count, |
| 58 | COUNT(CASE WHEN n.status IN ('S', 'W', 'C') THEN 1 END) AS solved_count |
| 59 | FROM numbered n |
| 60 | JOIN tag_counts t ON t.tag_id = n.tag_id |
| 61 | GROUP BY n.tag_name, n.tag_color, t.total_count, partition_number |
| 62 | ) |
| 63 | SELECT * |
| 64 | FROM partitioned |
| 65 | ORDER BY total_count DESC, partition_number"; |
| 66 | |
| 67 | $tagsRaw = Util::query($query); |
| 68 | foreach ($tagsRaw as $key => $tagRaw) |
| 69 | { |
| 70 | $tag = []; |
| 71 | $tag['id'] = $tagRaw['name']; |
| 72 | $tag['amount'] = $tagRaw['usage_count']; |
| 73 | $tag['name'] = $tagRaw['name']; |
| 74 | $partition = $tagRaw['partition_number']; |
| 75 | $colorValue = 1 - (($partition == -1) ? 0 : -($partition * 0.15)); |
| 76 | $tag['color'] = str_replace('[o]', (string) $colorValue, SetsSelector::getTagColor($tagRaw['color'])); |
| 77 | $tag['solved_percent'] = round(Util::getPercent($tagRaw['solved_count'], $tagRaw['usage_count'])); |
| 78 | $tag['partition'] = $partition; |
| 79 | $this->sets [] = $tag; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | private static function getTagColor($pos) |
| 84 | { |
| 85 | $c = []; |
| 86 | $c[0] = 'rgba(217, 135, 135, [o])'; |
| 87 | $c[1] = 'rgba(135, 149, 101, [o])'; |
| 88 | $c[2] = 'rgba(190, 151, 131, [o])'; |
| 89 | $c[3] = 'rgba(188, 116, 45, [o])'; |
| 90 | $c[4] = 'rgba(153, 111, 31, [o])'; |
| 91 | $c[5] = 'rgba(159, 54, 0, [o])'; |
| 92 | $c[6] = 'rgba(153, 151, 31, [o])'; |
| 93 | $c[7] = 'rgba(114, 9, 183, [o])'; |
| 94 | $c[8] = 'rgba(149, 77, 63, [o])'; |
| 95 | $c[9] = 'rgba(179, 181, 37, [o])'; |
| 96 | $c[10] = 'rgba(137, 153, 31, [o])'; |
| 97 | $c[11] = 'rgba(145, 61, 91, [o])'; |
| 98 | $c[12] = 'rgba(79, 68, 68, [o])'; |
| 99 | $c[13] = 'rgba(182, 137, 199, [o])'; |
| 100 | $c[14] = 'rgba(166, 88, 125, [o])'; |
| 101 | $c[15] = 'rgba(45, 37, 79, [o])'; |
| 102 | $c[16] = 'rgba(154, 50, 138, [o])'; |
| 103 | $c[17] = 'rgba(102, 51, 122, [o])'; |
| 104 | $c[18] = 'rgba(184, 46, 126, [o])'; |
| 105 | $c[19] = 'rgba(119, 50, 154, [o])'; |
| 106 | $c[20] = 'rgba(187, 70, 196, [o])'; |
| 107 | $c[21] = 'rgba(125, 8, 8, [o])'; |
| 108 | $c[22] = 'rgba(136, 67, 56, [o])'; |
| 109 | $c[23] = 'rgba(190, 165, 136, [o])'; |
| 110 | $c[24] = 'rgba(128, 118, 123, [o])'; |
| 111 | |
| 112 | return $c[$pos]; |
| 113 | } |
| 114 | |
| 115 | private function selectByTopics() |
| 116 | { |
| 117 | $filteredTsumego = new Query('FROM tsumego'); |
| 118 | $filteredTsumego->selects [] = 'DISTINCT tsumego.id'; |
| 119 | $filteredTsumego->selects [] = 'tsumego.rating'; |
| 120 | $this->tsumegoFilters->addConditionsToQuery($filteredTsumego); |
| 121 | |
| 122 | $query = " |
| 123 | WITH filtered_tsumego AS (" . $filteredTsumego->str() . "), |
| 124 | |
| 125 | set_counts AS ( |
| 126 | SELECT |
| 127 | s.id AS set_id, |
| 128 | s.title AS set_title, |
| 129 | s.color AS set_color, |
| 130 | COUNT(sc.tsumego_id) AS total_count |
| 131 | FROM filtered_tsumego ft |
| 132 | JOIN set_connection sc ON sc.tsumego_id = ft.id |
| 133 | JOIN `set` s ON s.id = sc.set_id |
| 134 | WHERE s.public = 1 |
| 135 | GROUP BY s.id |
| 136 | ), |
| 137 | |
| 138 | numbered AS ( |
| 139 | SELECT |
| 140 | s.`order` AS set_order, |
| 141 | s.id AS set_id, |
| 142 | s.title AS set_title, |
| 143 | s.color AS set_color, |
| 144 | ft.rating AS rating, |
| 145 | ft.id AS tsumego_id, |
| 146 | ROW_NUMBER() OVER ( |
| 147 | PARTITION BY s.id |
| 148 | ORDER BY sc.num, ft.id |
| 149 | ) AS rn, |
| 150 | ts.status as status |
| 151 | FROM filtered_tsumego ft |
| 152 | JOIN set_connection sc ON sc.tsumego_id = ft.id |
| 153 | JOIN `set` s ON s.id = sc.set_id AND s.public = 1 |
| 154 | LEFT JOIN tsumego_status ts |
| 155 | ON ts.user_id = " . Auth::getUserID() . " |
| 156 | AND ts.tsumego_id = ft.id |
| 157 | " . (empty($this->tsumegoFilters->setIDs) ? '' : (' WHERE s.id IN (' . implode(',', $this->tsumegoFilters->setIDs) . ')')) . " |
| 158 | ), |
| 159 | |
| 160 | partitioned AS ( |
| 161 | SELECT |
| 162 | numbered.set_order as order_value, |
| 163 | numbered.set_id as id, |
| 164 | numbered.set_title AS title, |
| 165 | numbered.set_color AS color, |
| 166 | sc.total_count, |
| 167 | CASE |
| 168 | WHEN sc.total_count <= " . $this->tsumegoFilters->collectionSize . " THEN -1 |
| 169 | ELSE FLOOR((numbered.rn - 1) / " . $this->tsumegoFilters->collectionSize . ") |
| 170 | END AS partition_number, |
| 171 | COUNT(*) AS usage_count, |
| 172 | COUNT(CASE WHEN numbered.status IN ('S', 'W', 'C') THEN 1 END) AS solved_count, |
| 173 | SUM(numbered.rating) AS rating_sum |
| 174 | FROM numbered |
| 175 | JOIN set_counts sc ON sc.set_id = numbered.set_id |
| 176 | GROUP BY |
| 177 | numbered.set_order, |
| 178 | numbered.set_id, |
| 179 | numbered.set_title, |
| 180 | numbered.set_color, |
| 181 | sc.total_count, |
| 182 | partition_number |
| 183 | ) |
| 184 | |
| 185 | SELECT * |
| 186 | FROM partitioned |
| 187 | ORDER BY order_value, total_count DESC, partition_number, id |
| 188 | "; |
| 189 | $rows = Util::query($query); |
| 190 | foreach ($rows as $row) |
| 191 | { |
| 192 | $set = []; |
| 193 | $set['id'] = $row['id']; |
| 194 | $set['name'] = $row['title']; |
| 195 | $set['amount'] = $row['usage_count']; |
| 196 | $partition = $row['partition_number']; |
| 197 | $set['color'] = $row['color']; |
| 198 | $set['solved_percent'] = round(Util::getPercent($row['solved_count'], $row['usage_count'])); |
| 199 | $set['difficulty'] = Rating::getReadableRankFromRating($row['rating_sum'] / $row['usage_count']); |
| 200 | $set['partition'] = $partition; |
| 201 | $this->sets[] = $set; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | private function selectByDifficulty() |
| 206 | { |
| 207 | $ranks = SetsController::getExistingRanksArray(); |
| 208 | |
| 209 | if (!empty($this->tsumegoFilters->ranks)) |
| 210 | $ranks = array_values(array_filter($ranks, function ($r) { return in_array($r['rank'], $this->tsumegoFilters->ranks); })); |
| 211 | |
| 212 | $rankSelects = []; |
| 213 | $rankOrder = 0; |
| 214 | |
| 215 | foreach ($ranks as $rank) |
| 216 | { |
| 217 | $rankQuery = new Query('FROM tsumego'); |
| 218 | RatingBounds::coverRank($rank['rank'], '15k')->addQueryConditions($rankQuery); |
| 219 | |
| 220 | $rankQuery->conditions[] = 'tsumego.deleted IS NULL'; |
| 221 | $rankQuery->conditions[] = '`set`.public = 1'; |
| 222 | if (!empty($this->tsumegoFilters->setIDs)) |
| 223 | $rankQuery->conditions[] = '`set`.id IN (' . implode(',', $this->tsumegoFilters->setIDs) . ')'; |
| 224 | |
| 225 | if (!empty($this->tsumegoFilters->tagIDs)) |
| 226 | $rankQuery->conditions[] |
| 227 | = 'EXISTS ( |
| 228 | SELECT 1 FROM tag_connection tc |
| 229 | WHERE tc.tsumego_id = tsumego.id |
| 230 | AND tc.tag_id IN (' . implode(',', $this->tsumegoFilters->tagIDs) . ') |
| 231 | )'; |
| 232 | $rankQuery->selects[] = 'DISTINCT tsumego.id AS tsumego_id'; |
| 233 | $rankQuery->selects[] = 'tsumego.rating'; |
| 234 | $rankQuery->selects[] = "'{$rank['rank']}' AS rank_label"; |
| 235 | $rankQuery->selects[] = "{$rankOrder} AS rank_order"; |
| 236 | $rankQuery->selects[] = "'{$rank['color']}' AS rank_color"; |
| 237 | $rankQuery->query .= " JOIN set_connection sc ON sc.tsumego_id = tsumego.id |
| 238 | JOIN `set` ON `set`.id = sc.set_id"; |
| 239 | $rankSelects[] = $rankQuery->str(); |
| 240 | $rankOrder++; |
| 241 | } |
| 242 | |
| 243 | $rankUnion = implode("\nUNION ALL\n", $rankSelects); |
| 244 | |
| 245 | $query = " |
| 246 | WITH ranked_tsumego AS ({$rankUnion}), |
| 247 | |
| 248 | rank_counts AS ( |
| 249 | SELECT |
| 250 | rank_label, |
| 251 | COUNT(*) AS total_count |
| 252 | FROM ranked_tsumego |
| 253 | GROUP BY rank_label |
| 254 | ), |
| 255 | |
| 256 | numbered AS ( |
| 257 | SELECT |
| 258 | rt.rank_label, |
| 259 | rt.rank_order, |
| 260 | rt.rank_color, |
| 261 | rt.tsumego_id, |
| 262 | rt.rating, |
| 263 | ROW_NUMBER() OVER ( |
| 264 | PARTITION BY rt.rank_label |
| 265 | ORDER BY rt.tsumego_id |
| 266 | ) AS rn, |
| 267 | ts.status |
| 268 | FROM ranked_tsumego rt |
| 269 | LEFT JOIN tsumego_status ts |
| 270 | ON ts.user_id = " . Auth::getUserID() . " |
| 271 | AND ts.tsumego_id = rt.tsumego_id |
| 272 | ), |
| 273 | |
| 274 | partitioned AS ( |
| 275 | SELECT |
| 276 | n.rank_label AS id, |
| 277 | n.rank_label AS name, |
| 278 | n.rank_color AS color, |
| 279 | n.rank_order, |
| 280 | rc.total_count, |
| 281 | CASE |
| 282 | WHEN rc.total_count <= {$this->tsumegoFilters->collectionSize} THEN -1 |
| 283 | ELSE FLOOR((n.rn - 1) / {$this->tsumegoFilters->collectionSize}) |
| 284 | END AS partition_number, |
| 285 | COUNT(*) AS usage_count, |
| 286 | COUNT(CASE WHEN n.status IN ('S','W','C') THEN 1 END) AS solved_count, |
| 287 | SUM(n.rating) AS rating_sum |
| 288 | FROM numbered n |
| 289 | JOIN rank_counts rc |
| 290 | ON rc.rank_label = n.rank_label |
| 291 | GROUP BY |
| 292 | n.rank_label, |
| 293 | n.rank_color, |
| 294 | n.rank_order, |
| 295 | rc.total_count, |
| 296 | partition_number |
| 297 | ) |
| 298 | |
| 299 | SELECT * |
| 300 | FROM partitioned |
| 301 | ORDER BY rank_order, partition_number"; |
| 302 | |
| 303 | $rows = Util::query($query); |
| 304 | foreach ($rows as $row) |
| 305 | { |
| 306 | $set = []; |
| 307 | $set['id'] = $row['id']; |
| 308 | $set['name'] = $row['name']; |
| 309 | $set['amount'] = $row['usage_count']; |
| 310 | $set['partition'] = $row['partition_number']; |
| 311 | |
| 312 | $opacity = ($row['partition_number'] === -1) |
| 313 | ? 1 |
| 314 | : 1 - ($row['partition_number'] * 0.15); |
| 315 | |
| 316 | $set['color'] = str_replace('[o]', (string) $opacity, $row['color']); |
| 317 | $set['solved_percent'] = round(Util::getPercent($row['solved_count'], $row['usage_count'])); |
| 318 | $set['difficulty'] = Rating::getReadableRankFromRating($row['rating_sum'] / $row['usage_count']); |
| 319 | |
| 320 | $this->sets[] = $set; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | public TsumegoFilters $tsumegoFilters; |
| 325 | public $sets = []; |
| 326 | public int $problemsFound = 0; |
| 327 | } |