Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.32% covered (success)
96.32%
131 / 136
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SetsSelector
96.30% covered (success)
96.30%
130 / 135
80.00% covered (warning)
80.00%
4 / 5
19
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 selectByTags
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
3
 getTagColor
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
1
 selectByTopics
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
3
 selectByDifficulty
89.80% covered (warning)
89.80%
44 / 49
0.00% covered (danger)
0.00%
0 / 1
8.07
1<?php
2
3App::uses('Query', 'Utility');
4
5class 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 = "
32WITH tag_counts AS (" . $innerQuery->str() . "),
33numbered 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),
48partitioned 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)
63SELECT *
64FROM partitioned
65ORDER BY total_count DESC, name ASC, 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'] = Util::getPercentButAvoid100UntilComplete($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 = "
123WITH filtered_tsumego AS (" . $filteredTsumego->str() . "),
124
125set_counts AS (
126  SELECT
127    s.id AS set_id,
128    s.title AS set_title,
129    s.color AS set_color,
130    s.premium AS set_premium,
131    COUNT(sc.tsumego_id) AS total_count
132  FROM filtered_tsumego ft
133  JOIN set_connection sc ON sc.tsumego_id = ft.id
134  JOIN `set` s ON s.id = sc.set_id
135  WHERE s.public = 1
136  GROUP BY s.id
137),
138
139numbered AS (
140  SELECT
141      s.`order` AS set_order,
142    s.id AS set_id,
143    s.title AS set_title,
144    s.color AS set_color,
145    s.premium AS set_premium,
146    ft.rating AS rating,
147    ft.id AS tsumego_id,
148    ROW_NUMBER() OVER (
149      PARTITION BY s.id
150      ORDER BY sc.num, ft.id
151    ) AS rn,
152    ts.status as status
153  FROM filtered_tsumego ft
154  JOIN set_connection sc ON sc.tsumego_id = ft.id
155  JOIN `set` s ON s.id = sc.set_id AND s.public = 1
156  LEFT JOIN tsumego_status ts
157    ON ts.user_id = " . Auth::getUserID() . "
158    AND ts.tsumego_id = ft.id
159  " . (empty($this->tsumegoFilters->setIDs) ? '' : (' WHERE s.id IN (' . implode(',', $this->tsumegoFilters->setIDs) . ')')) . "
160),
161
162partitioned AS (
163  SELECT
164      numbered.set_order as order_value,
165      numbered.set_id as id,
166    numbered.set_title AS title,
167    numbered.set_color AS color,
168    numbered.set_premium AS premium,
169    sc.total_count,
170    CASE
171      WHEN sc.total_count <= " . $this->tsumegoFilters->collectionSize . " THEN -1
172      ELSE FLOOR((numbered.rn - 1) / " . $this->tsumegoFilters->collectionSize . ")
173    END AS partition_number,
174    COUNT(*) AS usage_count,
175    COUNT(CASE WHEN numbered.status IN ('S', 'W', 'C') THEN 1 END) AS solved_count,
176    SUM(numbered.rating) AS rating_sum
177  FROM numbered
178  JOIN set_counts sc ON sc.set_id = numbered.set_id
179  GROUP BY
180      numbered.set_order,
181      numbered.set_id,
182    numbered.set_title,
183    numbered.set_color,
184    numbered.set_premium,
185    sc.total_count,
186    partition_number
187)
188
189SELECT *
190FROM partitioned
191ORDER BY order_value, total_count DESC, partition_number, id
192";
193        $rows = Util::query($query);
194        foreach ($rows as $row)
195        {
196            $set = [];
197            $set['id'] = $row['id'];
198            $set['name'] = $row['title'];
199            $set['amount'] = $row['usage_count'];
200            $partition = $row['partition_number'];
201            $set['color'] = $row['color'];
202            $set['premium'] = $row['premium'];
203            $set['solved_percent'] = Util::getPercentButAvoid100UntilComplete($row['solved_count'], $row['usage_count']);
204            $set['difficulty'] = Rating::getReadableRankFromRating($row['rating_sum'] / $row['usage_count']);
205            $set['partition'] = $partition;
206            $this->sets[] = $set;
207        }
208    }
209
210    private function selectByDifficulty()
211    {
212        $ranks = SetsController::getExistingRanksArray();
213
214        if (!empty($this->tsumegoFilters->ranks))
215            $ranks = array_values(array_filter($ranks, function ($r) { return in_array($r['rank'], $this->tsumegoFilters->ranks); }));
216
217        $rankSelects = [];
218        $rankOrder = 0;
219
220        foreach ($ranks as $rank)
221        {
222            $rankQuery = new Query('FROM tsumego');
223            RatingBounds::coverRank($rank['rank'], '15k')->addQueryConditions($rankQuery);
224
225            if (!Auth::hasPremium())
226                $rankQuery->conditions[] = '`set`.premium = 0';
227
228            $rankQuery->conditions[] = 'tsumego.deleted IS NULL';
229            $rankQuery->conditions[] = '`set`.public = 1';
230            if (!empty($this->tsumegoFilters->setIDs))
231                $rankQuery->conditions[] = '`set`.id IN (' . implode(',', $this->tsumegoFilters->setIDs) . ')';
232
233            if (!empty($this->tsumegoFilters->tagIDs))
234                $rankQuery->conditions[]
235                = 'EXISTS (
236                        SELECT 1 FROM tag_connection tc
237                        WHERE tc.tsumego_id = tsumego.id
238                        AND tc.tag_id IN (' . implode(',', $this->tsumegoFilters->tagIDs) . ')
239                    )';
240            $rankQuery->selects[] = 'DISTINCT tsumego.id AS tsumego_id';
241            $rankQuery->selects[] = 'tsumego.rating';
242            $rankQuery->selects[] = "'{$rank['rank']}' AS rank_label";
243            $rankQuery->selects[] = "{$rankOrder} AS rank_order";
244            $rankQuery->selects[] = "'{$rank['color']}' AS rank_color";
245            $rankQuery->query .= " JOIN set_connection sc ON sc.tsumego_id = tsumego.id
246                JOIN `set` ON `set`.id = sc.set_id";
247            $rankSelects[] = $rankQuery->str();
248            $rankOrder++;
249        }
250
251        $rankUnion = implode("\nUNION ALL\n", $rankSelects);
252
253        $query = "
254    WITH ranked_tsumego AS ({$rankUnion}),
255
256    rank_counts AS (
257    SELECT
258        rank_label,
259        COUNT(*) AS total_count
260    FROM ranked_tsumego
261    GROUP BY rank_label
262),
263
264    numbered AS (
265        SELECT
266            rt.rank_label,
267            rt.rank_order,
268            rt.rank_color,
269            rt.tsumego_id,
270            rt.rating,
271            ROW_NUMBER() OVER (
272                PARTITION BY rt.rank_label
273                ORDER BY rt.tsumego_id
274            ) AS rn,
275            ts.status
276        FROM ranked_tsumego rt
277        LEFT JOIN tsumego_status ts
278            ON ts.user_id = " . Auth::getUserID() . "
279            AND ts.tsumego_id = rt.tsumego_id
280    ),
281
282    partitioned AS (
283    SELECT
284        n.rank_label AS id,
285        n.rank_label AS name,
286        n.rank_color AS color,
287        n.rank_order,
288        rc.total_count,
289        CASE
290            WHEN rc.total_count <= {$this->tsumegoFilters->collectionSize} THEN -1
291            ELSE FLOOR((n.rn - 1) / {$this->tsumegoFilters->collectionSize})
292        END AS partition_number,
293        COUNT(*) AS usage_count,
294        COUNT(CASE WHEN n.status IN ('S','W','C') THEN 1 END) AS solved_count,
295        SUM(n.rating) AS rating_sum
296    FROM numbered n
297    JOIN rank_counts rc
298        ON rc.rank_label = n.rank_label
299    GROUP BY
300        n.rank_label,
301        n.rank_color,
302        n.rank_order,
303        rc.total_count,
304        partition_number
305)
306
307    SELECT *
308    FROM partitioned
309    ORDER BY rank_order, partition_number";
310
311        $rows = Util::query($query);
312        foreach ($rows as $row)
313        {
314            $set = [];
315            $set['id'] = $row['id'];
316            $set['name'] = $row['name'];
317            $set['amount'] = $row['usage_count'];
318            $set['partition'] = $row['partition_number'];
319
320            $opacity = ($row['partition_number'] === -1)
321                ? 1
322                : 1 - ($row['partition_number'] * 0.15);
323
324            $set['color'] = str_replace('[o]', (string) $opacity, $row['color']);
325            $set['solved_percent'] = Util::getPercentButAvoid100UntilComplete($row['solved_count'], $row['usage_count']);
326            $set['difficulty'] = Rating::getReadableRankFromRating($row['rating_sum'] / $row['usage_count']);
327
328            $this->sets[] = $set;
329        }
330    }
331
332    public TsumegoFilters $tsumegoFilters;
333    public $sets = [];
334    public int $problemsFound = 0;
335}