Passed
Push — master ( 2630eb...9b6994 )
by Struan
05:22
created

Member::getShortParty()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
ccs 0
cts 7
cp 0
crap 6
1
<?php
2
/**
3
 * Member Class
4
 *
5
 * @package TheyWorkForYou
6
 */
7
8
namespace MySociety\TheyWorkForYou;
9
10
/**
11
 * Member
12
 */
13
14
class Member extends \MEMBER {
15
16
    /**
17
     * Is Dead
18
     *
19
     * Determine if the member has died or not.
20
     *
21
     * @return boolean If the member is dead or not.
22
     */
23
24
    public function isDead() {
25
26
        $left_house = $this->left_house();
27
28
        if ($left_house) {
29
30
            // This member has left a house, and might be dead. See if they are.
31
32
            // Types of house to test for death.
33
            $house_types = array(
34
                HOUSE_TYPE_COMMONS,
35
                HOUSE_TYPE_LORDS,
36
                HOUSE_TYPE_SCOTLAND,
37
                HOUSE_TYPE_NI,
38
            );
39
40
            foreach ($house_types as $house_type) {
41
42
                if (in_array($house_type, $left_house) AND
43
                    $left_house[$house_type]['reason'] AND
44
                    $left_house[$house_type]['reason'] == 'Died'
45
                ) {
46
47
                    // This member has left a house because of death.
48
                    return TRUE;
49
                }
50
51
            }
52
53
        }
54
55
        // If we get this far the member hasn't left a house due to death, and
56
        // is presumably alive.
57
        return FALSE;
58
59
    }
60
61
    /*
62
     * Determine if the member is a new member of a house where new is
63
     * within the last 6 months.
64
     *
65
     * @param int house - identifier for the house, defaults to 1 for westminster
0 ignored issues
show
Bug introduced by
The type MySociety\TheyWorkForYou\house was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
66
     *
67
     * @return boolean
68
     */
69
70 1
    public function isNew($house = HOUSE_TYPE_COMMONS) {
71 1
        $date_entered = $this->getEntryDate($house);
72
73 1
        if ($date_entered) {
74 1
            $date_entered = new \DateTime($date_entered);
75 1
            $now = new \DateTime();
76
77 1
            $diff = $date_entered->diff($now);
78 1
            if ( $diff->y == 0 && $diff->m <= 6 ) {
79 1
                return TRUE;
80
            }
81 1
        }
82
83 1
        return FALSE;
84
    }
85
86
    /*
87
     * Get the date the person first entered a house. May not be for their current seat.
88
     *
89
     * @param int house - identifier for the house, defaults to 1 for westminster
90
     *
91
     * @return string - blank if no entry date for that house otherwise in YYYY-MM-DD format
92
     */
93
94 2
    public function getEntryDate($house = HOUSE_TYPE_COMMONS) {
95 2
        $date_entered = '';
96
97 2
        $entered_house = $this->entered_house($house);
98
99 2
        if ( $entered_house ) {
100 2
            $date_entered = $entered_house['date'];
101 2
        }
102
103 2
        return $date_entered;
104
    }
105
106
    /*
107
     * Get the date the person last left the house.
108
     *
109
     * @param int house - identifier for the house, defaults to 1 for westminster
110
     *
111
     * @return string - 9999-12-31 if they are still in that house otherwise in YYYY-MM-DD format
112
     */
113
114
    public function getLeftDate($house = HOUSE_TYPE_COMMONS) {
115
        $date_left = '';
116
117
        $left_house = $this->left_house($house);
118
119
        if ( $left_house ) {
120
            $date_left = $left_house['date'];
121
        }
122
123
        return $date_left;
124
    }
125
126
127
    public function getEUStance() {
128
        if (array_key_exists('eu_ref_stance', $this->extra_info())) {
129
            return $this->extra_info()['eu_ref_stance'];
130
        }
131
132
        return FALSE;
133
    }
134
135
    /**
136
    * Image
137
    *
138
    * Return a URL for the member's image.
139
    *
140
    * @return string The URL of the member's image.
141
    */
142
143
    public function image() {
144
145
        $is_lord = $this->house(HOUSE_TYPE_LORDS);
146
        if ($is_lord) {
147
            list($image,$size) = Utility\Member::findMemberImage($this->person_id(), false, 'lord');
148
        } else {
149
            list($image,$size) = Utility\Member::findMemberImage($this->person_id(), false, true);
150
        }
151
152
        // We can determine if the image exists or not by testing if size is set
153
        if ($size !== NULL) {
154
            $exists = TRUE;
155
        } else {
156
            $exists = FALSE;
157
        }
158
159
        return array(
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('url' => $i...e, 'exists' => $exists) returns the type array<string,boolean|null|string> which is incompatible with the documented return type string.
Loading history...
160
            'url' => $image,
161
            'size' => $size,
162
            'exists' => $exists
163
        );
164
165
    }
166
167
    public function getMostRecentMembership() {
168
        $departures = $this->left_house();
169
170
        usort(
171
            $departures,
0 ignored issues
show
Bug introduced by
It seems like $departures can also be of type null; however, parameter $array of usort() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

171
            /** @scrutinizer ignore-type */ $departures,
Loading history...
172
            function ($a, $b) {
173
                if ( $a['date'] == $b['date'] ) {
174
                    return 0;
175
                } else if ( $a['date'] < $b['date'] ) {
176
                    return -1;
177
                } else {
178
                    return 1;
179
                }
180
            }
181
        );
182
183
        $latest_membership = array_slice($departures, -1)[0];
184
        $latest_membership['current'] = ($latest_membership['date'] == '9999-12-31');
185
        $latest_entrance = $this->entered_house($latest_membership['house']);
186
        $latest_membership['start_date'] = $latest_entrance['date'];
187
        $latest_membership['end_date'] = $latest_membership['date'];
188
        $latest_membership['rep_name'] = $this->getRepNameForHouse($latest_membership['house']);
189
190
        return $latest_membership;
191
    }
192
193
    /**
194
    * Offices
195
    *
196
    * Return an array of Office objects held (or previously held) by the member.
197
    *
198
    * @param string $include_only  Restrict the list to include only "previous" or "current" offices.
199
    * @param bool   $ignore_committees Ignore offices that appear to be committee memberships.
200
    *
201
    * @return array An array of Office objects.
202
    */
203
204
    public function offices($include_only = NULL, $ignore_committees = FALSE) {
205
206
        $out = array();
207
208
        if (array_key_exists('office', $this->extra_info())) {
209
            $office = $this->extra_info();
210
            $office = $office['office'];
211
212
            foreach ($office as $row) {
213
                if ( $officeObject = $this->getOfficeObject($include_only, $ignore_committees, $row) ) {
214
                    $out[] = $officeObject;
215
                }
216
            }
217
        }
218
219
        return $out;
220
221
    }
222
223
    private function getOfficeObject($include_only, $ignore_committees, $row) {
224
        if (!$this->includeOffice($include_only, $row['to_date'])) {
225
            return null;
226
        }
227
        if ($ignore_committees && strpos($row['moffice_id'], 'Committee')) {
228
            return null;
229
        }
230
231
        $officeObject = new Office;
232
        $officeObject->title = prettify_office($row['position'], $row['dept']);
233
        $officeObject->from_date = $row['from_date'];
234
        $officeObject->to_date = $row['to_date'];
235
        $officeObject->source = $row['source'];
236
        return $officeObject;
237
    }
238
239
    private function includeOffice($include_only, $to_date) {
240
        $include_office = TRUE;
241
242
        // If we should only include previous offices, and the to date is in the future, suppress this office.
243
        if ($include_only == 'previous' AND $to_date == '9999-12-31') {
244
            $include_office = FALSE;
245
        }
246
247
        // If we should only include previous offices, and the to date is in the past, suppress this office.
248
        if ($include_only == 'current' AND $to_date != '9999-12-31') {
249
            $include_office = FALSE;
250
        }
251
252
        return $include_office;
253
    }
254
255
    /**
256
    * Get Other Parties String
257
    *
258
    * Return a readable list of party changes for this member.
259
    *
260
    * @return string|null A readable list of the party changes for this member.
261
    */
262
263
    public function getOtherPartiesString() {
264
265
        if (!empty($this->other_parties) && $this->party != 'Speaker' && $this->party != 'Deputy Speaker') {
266
            $output = 'Party was ';
267
            $other_parties = array();
268
            foreach ($this->other_parties as $r) {
269
                $other_parties[] = $r['from'] . ' until ' . format_date($r['date'], SHORTDATEFORMAT);
270
            }
271
            $output .= join('; ', $other_parties);
272
            return $output;
273
        } else {
274
            return NULL;
275
        }
276
277
    }
278
279
    /**
280
    * Get Other Constituencies String
281
    *
282
    * Return a readable list of other constituencies for this member.
283
    *
284
    * @return string|null A readable list of the other constituencies for this member.
285
    */
286
287
    public function getOtherConstituenciesString() {
288
289
        if ($this->other_constituencies) {
290
            return 'Also represented ' . join('; ', array_keys($this->other_constituencies));
291
        } else {
292
            return NULL;
293
        }
294
295
    }
296
297
    /**
298
    * Get Entered/Left Strings
299
    *
300
    * Return an array of readable strings covering when people entered or left
301
    * various houses. Returns an array since it's possible for a member to have
302
    * done several of these things.
303
    *
304
    * @return array An array of strings of when this member entered or left houses.
305
    */
306 1
    public function getEnterLeaveStrings() {
307 1
        $output = array();
308
309 1
        $output[] = $this->entered_house_line(HOUSE_TYPE_LORDS, 'House of Lords');
310
311 1
        if (isset($this->left_house[HOUSE_TYPE_COMMONS]) && isset($this->entered_house[HOUSE_TYPE_LORDS])) {
312
            $string = '<strong>Previously MP for ';
313
            $string .= $this->left_house[HOUSE_TYPE_COMMONS]['constituency'] . ' until ';
314
            $string .= $this->left_house[HOUSE_TYPE_COMMONS]['date_pretty'] . '</strong>';
315
            if ($this->left_house[HOUSE_TYPE_COMMONS]['reason']) {
316
                $string .= ' &mdash; ' . $this->left_house[HOUSE_TYPE_COMMONS]['reason'];
317
            }
318
            $output[] = $string;
319
        }
320
321 1
        $output[] = $this->left_house_line(HOUSE_TYPE_LORDS, 'House of Lords');
322
323 1
        if (isset($this->extra_info['lordbio'])) {
324
            $output[] = '<strong>Positions held at time of appointment:</strong> ' . $this->extra_info['lordbio'] .
325
                ' <small>(from <a href="' .
326
                $this->extra_info['lordbio_from'] . '">Number 10 press release</a>)</small>';
327
        }
328
329 1
        $output[] = $this->entered_house_line(HOUSE_TYPE_COMMONS, 'House of Commons');
330
331
        # If they became a Lord, we handled this above.
332 1
        if ($this->house(HOUSE_TYPE_COMMONS) && !$this->current_member(HOUSE_TYPE_COMMONS) && !$this->house(HOUSE_TYPE_LORDS)) {
333
            $output[] = $this->left_house_line(HOUSE_TYPE_COMMONS, 'House of Commons');
334
        }
335
336 1
        $output[] = $this->entered_house_line(HOUSE_TYPE_NI, 'Assembly');
337 1
        $output[] = $this->left_house_line(HOUSE_TYPE_NI, 'Assembly');
338 1
        $output[] = $this->entered_house_line(HOUSE_TYPE_SCOTLAND, 'Scottish Parliament');
339 1
        $output[] = $this->left_house_line(HOUSE_TYPE_SCOTLAND, 'Scottish Parliament');
340
341 1
        $output = array_values(array_filter($output));
342 1
        return $output;
343
    }
344
345 1
    private function entered_house_line($house, $house_name) {
346 1
        if (isset($this->entered_house[$house]['date'])) {
347 1
            $string = "<strong>Entered the $house_name ";
348 1
            $string .= strlen($this->entered_house[$house]['date_pretty'])==HOUSE_TYPE_SCOTLAND ? 'in ' : 'on ';
349 1
            $string .= $this->entered_house[$house]['date_pretty'] . '</strong>';
350 1
            if ($this->entered_house[$house]['reason']) {
351 1
                $string .= ' &mdash; ' . $this->entered_house[$house]['reason'];
352 1
            }
353 1
            return $string;
354
        }
355 1
    }
356
357 1
    private function left_house_line($house, $house_name) {
358 1
        if ($this->house($house) && !$this->current_member($house)) {
359 1
            $string = "<strong>Left the $house_name ";
360 1
            $string .= strlen($this->left_house[$house]['date_pretty'])==4 ? 'in ' : 'on ';
361 1
            $string .= $this->left_house[$house]['date_pretty'] . '</strong>';
362 1
            if ($this->left_house[$house]['reason']) {
363 1
                $string .= ' &mdash; ' . $this->left_house[$house]['reason'];
364 1
            }
365 1
            return $string;
366
        }
367 1
    }
368
369
    public function getPartyPolicyDiffs($party, $policiesList, $positions, $only_diffs = false) {
370
        $policy_diffs = array();
371
        $party_positions = $party->getAllPolicyPositions($policiesList);
372
373
        if ( !$party_positions ) {
374
            return $policy_diffs;
375
        }
376
377
        foreach ( $positions->positionsById as $policy_id => $details ) {
378
            if ( $details['has_strong'] && $details['score'] != -1 && isset($party_positions[$policy_id])) {
379
                $mp_score = $details['score'];
380
                $party_score = $party_positions[$policy_id]['score'];
381
382
                $score_diff = $this->calculatePolicyDiffScore($mp_score, $party_score);
383
384
                // skip anything that isn't a yes vs no diff
385
                if ( $only_diffs && $score_diff < 2 ) {
386
                    continue;
387
                }
388
                $policy_diffs[$policy_id] = $score_diff;
389
            }
390
        }
391
392
        arsort($policy_diffs);
393
394
        return $policy_diffs;
395
    }
396
397
    private function calculatePolicyDiffScore( $mp_score, $party_score ) {
398
        $score_diff = abs($mp_score - $party_score);
399
        // if they are on opposite sides of mixture of for and against
400
        if (
401
            ( $mp_score < 0.4 && $party_score > 0.6 ) ||
402
            ( $mp_score > 0.6 && $party_score < 0.4 )
403
        ) {
404
            $score_diff += 2;
405
        // if on is mixture of for and against and one is for/against
406
        } else if (
407
            ( $mp_score > 0.4 && $mp_score < 0.6 && ( $party_score > 0.6 || $party_score < 0.4 ) ) ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($mp_score > 0.4 && $mp_... 0.6 || $mp_score < 0.4, Probably Intended Meaning: $mp_score > 0.4 && $mp_s...0.6 || $mp_score < 0.4)
Loading history...
408
            ( $party_score > 0.4 && $party_score < 0.6 && ( $mp_score > 0.6 || $mp_score < 0.4 ) )
409
        ) {
410
            $score_diff += 1;
411
        }
412
413
        return $score_diff;
414
    }
415
416 1
    public static function getRegionalList($postcode, $house, $type) {
417 1
        $db = new \ParlDB;
418
419 1
        $mreg = array();
420 1
        $constituencies = \MySociety\TheyWorkForYou\Utility\Postcode::postcodeToConstituencies($postcode);
421 1
        if ( isset($constituencies[$type]) ) {
422 1
            $cons_name = $constituencies[$type];
423
            $query_base = "SELECT member.person_id, title, lordofname, given_name, family_name, constituency, house
424
                FROM member, person_names
425
                WHERE
426
                member.person_id = person_names.person_id
427
                AND person_names.type = 'name'
428
                AND constituency = :cons_name
429
                AND house = :house
430
                AND left_house >= start_date
431 1
                AND left_house <= end_date";
432 1
            $q = $db->query("$query_base AND left_reason = 'still_in_office'",
433
                array(
434 1
                    ':house' => $house,
435
                    ':cons_name' => $cons_name
436 1
                )
437 1
            );
438 1
            if ( !$q->rows() && ($dissolution = Dissolution::db()) ) {
439
                $q = $db->query("$query_base AND $dissolution[query]",
440
                    array(
441
                        ':house' => $house,
442
                        ':cons_name' => $cons_name,
443
                    ) + $dissolution['params']
444
                );
445
            }
446
447 1
            for ($i = 0; $i < $q->rows; $i++) {
448 1
                $name = member_full_name($house, $q->field($i, 'title'),
449 1
                    $q->field($i, 'given_name'), $q->field($i, 'family_name'),
450 1
                    $q->field($i, 'lordofname'));
451
452 1
                $mreg[] = array(
453 1
                    'person_id' 	=> $q->field($i, 'person_id'),
454 1
                    'name' => $name,
455 1
                    'house' => $q->field($i, 'house'),
456 1
                    'constituency' 	=> $q->field($i, 'constituency')
457 1
                );
458 1
            }
459 1
        }
460
461 1
        return $mreg;
462
    }
463
464
    public static function getRepNameForHouse($house) {
465
        switch ( $house ) {
466
        case HOUSE_TYPE_COMMONS:
467
            $name = 'MP';
468
            break;
469
        case HOUSE_TYPE_LORDS:
470
            $name = 'Peer';
471
            break;
472
        case HOUSE_TYPE_NI:
473
            $name = 'MLA';
474
            break;
475
        case HOUSE_TYPE_SCOTLAND:
476
            $name = 'MSP';
477
            break;
478
        case HOUSE_TYPE_ROYAL:
479
            $name = 'Member of royalty';
480
            break;
481
        default:
482
            $name = '';
483
        }
484
        return $name;
485
    }
486
487
}
488