Passed
Push — master ( 0643ea...01e983 )
by Struan
04:28
created

Member::getOfficeObject()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 3
nop 3
dl 0
loc 14
rs 9.9332
c 0
b 0
f 0
ccs 0
cts 11
cp 0
crap 20
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
                HOUSE_TYPE_LONDON_ASSEMBLY,
39
            );
40
41
            foreach ($house_types as $house_type) {
42
43
                if (in_array($house_type, $left_house) AND
44
                    $left_house[$house_type]['reason'] AND
45
                    $left_house[$house_type]['reason'] == 'Died'
46
                ) {
47
48
                    // This member has left a house because of death.
49
                    return TRUE;
50
                }
51
52
            }
53
54
        }
55
56
        // If we get this far the member hasn't left a house due to death, and
57
        // is presumably alive.
58
        return FALSE;
59
60
    }
61
62
    /*
63
     * Determine if the member is a new member of a house where new is
64
     * within the last 6 months.
65
     *
66
     * @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...
67
     *
68
     * @return boolean
69
     */
70
71 1
    public function isNew($house = HOUSE_TYPE_COMMONS) {
72 1
        $date_entered = $this->getEntryDate($house);
73
74 1
        if ($date_entered) {
75 1
            $date_entered = new \DateTime($date_entered);
76 1
            $now = new \DateTime();
77
78 1
            $diff = $date_entered->diff($now);
79 1
            if ( $diff->y == 0 && $diff->m <= 6 ) {
80 1
                return TRUE;
81
            }
82
        }
83
84 1
        return FALSE;
85
    }
86
87
    /*
88
     * Get the date the person first entered a house. May not be for their current seat.
89
     *
90
     * @param int house - identifier for the house, defaults to 1 for westminster
91
     *
92
     * @return string - blank if no entry date for that house otherwise in YYYY-MM-DD format
93
     */
94
95 2
    public function getEntryDate($house = HOUSE_TYPE_COMMONS) {
96 2
        $date_entered = '';
97
98 2
        $entered_house = $this->entered_house($house);
99
100 2
        if ( $entered_house ) {
101 2
            $date_entered = $entered_house['date'];
102
        }
103
104 2
        return $date_entered;
105
    }
106
107
    /*
108
     * Get the date the person last left the house.
109
     *
110
     * @param int house - identifier for the house, defaults to 1 for westminster
111
     *
112
     * @return string - 9999-12-31 if they are still in that house otherwise in YYYY-MM-DD format
113
     */
114
115
    public function getLeftDate($house = HOUSE_TYPE_COMMONS) {
116
        $date_left = '';
117
118
        $left_house = $this->left_house($house);
119
120
        if ( $left_house ) {
121
            $date_left = $left_house['date'];
122
        }
123
124
        return $date_left;
125
    }
126
127
128
    public function getEUStance() {
129
        if (array_key_exists('eu_ref_stance', $this->extra_info())) {
130
            return $this->extra_info()['eu_ref_stance'];
131
        }
132
133
        return FALSE;
134
    }
135
136
    /**
137
    * Image
138
    *
139
    * Return a URL for the member's image.
140
    *
141
    * @return string The URL of the member's image.
142
    */
143
144
    public function image() {
145
146
        $is_lord = $this->house(HOUSE_TYPE_LORDS);
147
        if ($is_lord) {
148
            list($image,$size) = Utility\Member::findMemberImage($this->person_id(), false, 'lord');
149
        } else {
150
            list($image,$size) = Utility\Member::findMemberImage($this->person_id(), false, true);
151
        }
152
153
        // We can determine if the image exists or not by testing if size is set
154
        if ($size !== NULL) {
155
            $exists = TRUE;
156
        } else {
157
            $exists = FALSE;
158
        }
159
160
        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...
161
            'url' => $image,
162
            'size' => $size,
163
            'exists' => $exists
164
        );
165
166
    }
167
168
    public function getMostRecentMembership() {
169
        $departures = $this->left_house();
170
171
        usort(
172
            $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

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