Alert   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 164
Duplicated Lines 0 %

Test Coverage

Coverage 16.28%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 113
dl 0
loc 164
rs 10
c 2
b 0
f 1
ccs 7
cts 43
cp 0.1628
wmc 29

4 Methods

Rating   Name   Duplication   Size   Complexity  
A detailsToCriteria() 0 17 4
A sectionToTitle() 0 19 1
F prettifyCriteria() 0 74 17
B forUser() 0 48 7
1
<?php
2
3
namespace MySociety\TheyWorkForYou\Utility;
4
5
/**
6
 * Alert Utilities
7
 *
8
 * Utility functions related to alerts
9
 */
10
11
class Alert {
12
    public static function sectionToTitle($section) {
13
        $section_map = [
14 3
            "uk" => gettext('All UK'),
15 3
            "debates" => gettext('House of Commons debates'),
16
            "whalls" => gettext('Westminster Hall debates'),
17 3
            "lords" => gettext('House of Lords debates'),
18 3
            "wrans" => gettext('Written answers'),
19
            "wms" => gettext('Written ministerial statements'),
20
            "standing" => gettext('Bill Committees'),
21 3
            "future" => gettext('Future Business'),
22
            "ni" => gettext('Northern Ireland Assembly Debates'),
23
            "scotland" => gettext('All Scotland'),
24
            "sp" => gettext('Scottish Parliament Debates'),
25 3
            "spwrans" => gettext('Scottish Parliament Written answers'),
26 3
            "wales" => gettext('Welsh parliament record'),
27
            "lmqs" => gettext('Questions to the Mayor of London'),
28
        ];
29
30
        return $section_map[$section];
31
    }
32
    public static function detailsToCriteria($details) {
33
        $criteria = [];
34
35
        if (!empty($details['keyword'])) {
36
            $criteria[] = $details['keyword'];
37
        }
38
39
        if (!empty($details['pid'])) {
40
            $criteria[] = 'speaker:' . $details['pid'];
41
        }
42
43
        if (!empty($details['search_section'])) {
44
            $criteria[] = 'section:' . $details['search_section'];
45
        }
46
47
        $criteria = join(' ', $criteria);
48
        return $criteria;
49
    }
50
51
    public static function forUser($email) {
52
        $db = new \ParlDB();
53
        $q = $db->query('SELECT * FROM alerts WHERE email = :email
54
            AND deleted != 1 ORDER BY created', [
55
            ':email' => $email,
56
        ]);
57
58
        $alerts = [];
59
        foreach ($q as $row) {
60
            $criteria = self::prettifyCriteria($row['criteria'], $row['ignore_speaker_votes']);
61
            $parts = self::prettifyCriteria($row['criteria'], $row['ignore_speaker_votes'], true);
62
            $token = $row['alert_id'] . '-' . $row['registrationtoken'];
63
            // simple_criteria is "First term, second, third (5 keywords)" or "First term"
64
            $num_terms = count($parts['words']);
65
            if ($num_terms > 3) {
66
                $extras = $num_terms - 3;
67
                $simple_criteria = implode(', ', array_slice($parts['words'], 0, 3)) . ' (' . sprintf(ngettext('+ %d more keyword', '+ %d more keywords', $extras), $extras) . ')';
68
            } elseif ($num_terms > 0 && $num_terms <= 3) {
69
                $simple_criteria = implode(', ', array_slice($parts['words'], 0, 3));
70
            } else {
71
                $simple_criteria = $criteria;
72
            }
73
74
            $status = 'confirmed';
75
            if (!$row['confirmed']) {
76
                $status = 'unconfirmed';
77
            } elseif ($row['deleted'] == 2) {
78
                $status = 'suspended';
79
            }
80
81
            $alert = [
82
                'token' => $token,
83
                'status' => $status,
84
                'criteria' => $criteria,
85
                'simple_criteria' => $simple_criteria,
86
                'raw' => $row['criteria'],
87
                'ignore_speaker_votes' => $row['ignore_speaker_votes'],
88
                'keywords' => [],
89
                'exclusions' => [],
90
                'sections' => [],
91
            ];
92
93
            $alert = array_merge($alert, $parts);
94
95
            $alerts[] = $alert;
96
        }
97
98
        return $alerts;
99
    }
100
101
    public static function prettifyCriteria($alert_criteria, $ignore_speaker_votes = false, $as_parts = false) {
102
        $text = '';
103
        $parts = ['words' => [], 'sections' => [], 'exclusions' => [], 'match_all' => true, 'pid' => false];
104
        if ($alert_criteria) {
105
            # check for phrases
106
            if (strpos($alert_criteria, ' OR ') !== false) {
107
                $parts['match_all'] = false;
108
            }
109
            $alert_criteria = str_replace(' OR ', ' ', $alert_criteria);
110
            $alert_criteria = str_replace(['(', ')'], '', $alert_criteria);
111
            if (strpos($alert_criteria, '"') !== false) {
112
                # match phrases
113
                preg_match_all('/"([^"]*)"/', $alert_criteria, $phrases);
114
                # and then remove them from the criteria
115
                $alert_criteria = trim(preg_replace('/ +/', ' ', str_replace($phrases[0], "", $alert_criteria)));
116
117
                # and then create an array with the words and phrases
118
                $criteria = [];
119
                if ($alert_criteria != "") {
120
                    $criteria = explode(' ', $alert_criteria);
121
                }
122
                $criteria = array_merge($criteria, $phrases[1]);
123
            } else {
124
                $criteria = explode(' ', $alert_criteria);
125
            }
126
            $words = [];
127
            $exclusions = [];
128
            $sections = [];
129
            $sections_verbose = [];
130
            $speaker_parts = \MySociety\TheyWorkForYou\Utility\Search::speakerNamesForIDs($alert_criteria);
131
            $pids = array_keys($speaker_parts);
132
            $spokenby = array_values($speaker_parts);
133
134
            if (count($pids) == 1) {
135
                $parts['pid'] = $pids[0];
136
            }
137
138
            foreach ($criteria as $c) {
139
                if (preg_match('#^section:(\w+)#', $c, $m)) {
140
                    $sections[] = $m[1];
141
                    $sections_verbose[] = self::sectionToTitle($m[1]);
142
                } elseif (strpos($c, '-') === 0) {
143
                    $exclusions[] = str_replace('-', '', $c);
144
                } elseif (!preg_match('#^speaker:(\d+)#', $c, $m)) {
145
                    $words[] = $c;
146
                }
147
            }
148
            if ($spokenby && count($words)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $spokenby of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
149
                $text = implode(' or ', $spokenby) . ' mentions [' . implode(' ', $words) . ']';
150
                $parts['spokenby'] = $spokenby;
151
                $parts['words'] = $words;
152
            } elseif (count($words)) {
153
                $text = '[' . implode(' ', $words) . ']' . ' is mentioned';
154
                $parts['words'] = $words;
155
            } elseif ($spokenby) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $spokenby of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
156
                $text = implode(' or ', $spokenby) . " speaks";
157
                if ($ignore_speaker_votes) {
158
                    $text .= " (excluding votes)";
159
                }
160
                $parts['spokenby'] = $spokenby;
161
            }
162
163
            if ($sections) {
164
                $text = $text . " in " . implode(' or ', $sections_verbose);
165
                $parts['sections'] = $sections;
166
                $parts['sections_verbose'] = $sections_verbose;
167
            }
168
169
            $parts['exclusions'] = $exclusions;
170
        }
171
        if ($as_parts) {
172
            return $parts;
173
        }
174
        return $text;
175
    }
176
177
}
178