Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.24% covered (success)
96.24%
128 / 133
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SetsSelector
96.21% covered (success)
96.21%
127 / 132
80.00% covered (warning)
80.00%
4 / 5
18
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%
23 / 23
100.00% covered (success)
100.00%
1 / 1
3
 selectByDifficulty
89.36% covered (warning)
89.36%
42 / 47
0.00% covered (danger)
0.00%
0 / 1
7.06
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, 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 = "
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    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
138numbered 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
160partitioned 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
185SELECT *
186FROM partitioned
187ORDER 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}