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

Divisions::_division_data()   C

Complexity

Conditions 12
Paths 66

Size

Total Lines 86
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 60
nc 66
nop 1
dl 0
loc 86
rs 6.446
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Policy Positions
4
 *
5
 * @package TheyWorkForYou
6
 */
7
8
namespace MySociety\TheyWorkForYou;
9
10
class Divisions {
11
12
    /**
13
     * Member
14
     */
15
16
    private $member;
17
18
    /**
19
     * DB handle
20
     */
21
    private $db;
22
23
    private $positions;
24
    private $policies;
25
26
    /**
27
     * Constructor
28
     *
29
     * @param Member   $member   The member to get positions for.
30
     */
31
32
    public function __construct(Member $member = NULL, PolicyPositions $positions = NULL, Policies $policies = NULL)
33
    {
34
        $this->member = $member;
35
        $this->positions = $positions;
36
        $this->policies = $policies;
37
        $this->db = new \ParlDB;
38
    }
39
40
    public static function getMostRecentDivisionDate() {
41
        $db = new \ParlDB;
42
        $q = $db->query(
43
            "SELECT policy_id, max(division_date) as recent
44
            FROM policydivisions
45
                JOIN divisions USING(division_id)
46
            GROUP BY policy_id"
47
        );
48
49
        $policy_maxes = array();
50
        $row_count = $q->rows();
51
        for ($n = 0; $n < $row_count; $n++) {
52
            $policy_maxes[$q->field($n, 'policy_id')] = $q->field( $n, 'recent' );
53
        }
54
        $policy_maxes['latest'] = $policy_maxes ? max(array_values($policy_maxes)) : '';
55
        return $policy_maxes;
56
    }
57
58
    public function getRecentDivisions($number = 20) {
59
        $q = $this->db->query(
60
          "SELECT * FROM divisions ORDER BY division_date DESC, division_number DESC LIMIT :count",
61
            array(
62
                ':count' => $number
63
            )
64
        );
65
66
        $divisions = array();
67
        foreach ($q->data as $division) {
68
            $divisions[] = $this->getParliamentDivisionDetails($division);
69
        }
70
71
        return array('divisions' => $divisions);
72
    }
73
74
    public function getRecentDivisionsForPolicies($policies, $number = 20) {
75
        $args = array(':number' => $number);
76
77
        $quoted = array();
78
        foreach ($policies as $policy) {
79
            $quoted[] = $this->db->quote($policy);
80
        }
81
        $policies_str = implode(',', $quoted);
82
83
        $q = $this->db->query(
84
            "SELECT divisions.*
85
            FROM policydivisions
86
                JOIN divisions USING(division_id)
87
            WHERE policy_id in ($policies_str)
88
            GROUP BY division_id
89
            ORDER by division_date DESC LIMIT :number",
90
            $args
91
        );
92
93
        $divisions = array();
94
        $row_count = $q->rows();
95
        for ($n = 0; $n < $row_count; $n++) {
96
          $divisions[] = $this->getParliamentDivisionDetails($q->row($n));
97
        }
98
99
        return $divisions;
100
    }
101
102
    /**
103
     *
104
     * Get a list of division votes related to a policy
105
     *
106
     * Returns an array with one key ( the policyID ) containing a hash
107
     * with a policy_id key and a divisions key which contains an array
108
     * with details of all the divisions.
109
     *
110
     * Each division is a hash with the following fields:
111
     *    division_id, date, vote, gid, url, text, strong
112
     *
113
     * @param int|null $policyId The ID of the policy to get divisions for
114
     */
115
116
    public function getMemberDivisionsForPolicy($policyID = null) {
117
        $where_extra = '';
118
        $args = array(':person_id' => $this->member->person_id);
119
        if ( $policyID ) {
120
            $where_extra = 'AND policy_id = :policy_id';
121
            $args[':policy_id'] = $policyID;
122
        }
123
        $q = $this->db->query(
124
            "SELECT policy_id, division_id, division_title, yes_text, no_text, division_date, division_number, vote, gid, direction
125
            FROM policydivisions JOIN persondivisionvotes USING(division_id)
126
                JOIN divisions USING(division_id)
127
            WHERE person_id = :person_id AND direction <> 'abstention' $where_extra
128
            ORDER by policy_id, division_date DESC",
129
            $args
130
        );
131
132
        return $this->divisionsByPolicy($q);
133
    }
134
135
    public function getMemberDivisionDetails() {
136
        $args = array(':person_id' => $this->member->person_id);
137
138
        $policy_divisions = array();
139
140
        $q = $this->db->query(
141
            "SELECT policy_id, policy_vote, vote, count(division_id) as total,
142
            max(year(division_date)) as latest, min(year(division_date)) as earliest
143
            FROM policydivisions JOIN persondivisionvotes USING(division_id)
144
                JOIN divisions USING(division_id)
145
            WHERE person_id = :person_id AND direction <> 'abstention'
146
            GROUP BY policy_id, policy_vote, vote",
147
            $args
148
        );
149
150
        $row_count = $q->rows();
151
        for ($n = 0; $n < $row_count; $n++) {
152
          $policy_id = $q->field($n, 'policy_id');
153
154
          if (!array_key_exists($policy_id, $policy_divisions)) {
155
            $summary = array(
156
              'max' => $q->field($n, 'latest'),
157
              'min' => $q->field($n, 'earliest'),
158
              'total' => $q->field($n, 'total'),
159
              'for' => 0, 'against' => 0, 'absent' => 0, 'both' => 0, 'tell' => 0
160
            );
161
162
            $policy_divisions[$policy_id] = $summary;
163
          }
164
165
          $summary = $policy_divisions[$policy_id];
166
167
          $summary['total'] += $q->field($n, 'total');
168
          if ($summary['max'] < $q->field($n, 'latest')) {
169
              $summary['max'] = $q->field($n, 'latest');
170
          }
171
          if ($summary['min'] > $q->field($n, 'latest')) {
172
              $summary['min'] = $q->field($n, 'latest');
173
          }
174
175
          $vote = $q->field($n, 'vote');
176
          $policy_vote = str_replace('3', '', $q->field($n, 'policy_vote'));
177
          if ( $vote == 'absent' ) {
178
              $summary['absent'] += $q->field($n, 'total');
179
          } else if ( $vote == 'both' ) {
180
              $summary['both'] += $q->field($n, 'total');
181
          } else if ( strpos($vote, 'tell') !== FALSE ) {
182
              $summary['tell'] += $q->field($n, 'total');
183
          } else if ( $policy_vote == $vote ) {
184
              $summary['for'] += $q->field($n, 'total');
185
          } else if ( $policy_vote != $vote ) {
186
              $summary['against'] += $q->field($n, 'total');
187
          }
188
189
          $policy_divisions[$policy_id] = $summary;
190
        }
191
192
        return $policy_divisions;
193
    }
194
195
    public function getDivisionByGid($gid) {
196
        $args = array(
197
            ':gid' => $gid
198
        );
199
        $q = $this->db->query("SELECT * FROM divisions WHERE gid = :gid", $args);
200
201
        if ($q->rows == 0) {
202
            return false;
203
        }
204
205
        return $this->_division_data($q);
206
    }
207
208
    public function getDivisionResults($division_id) {
209
        $args = array(
210
            ':division_id' => $division_id
211
        );
212
        $q = $this->db->query("SELECT * FROM divisions WHERE division_id = :division_id", $args);
213
214
        if ($q->rows == 0) {
215
            return false;
216
        }
217
218
        return $this->_division_data($q);
219
220
    }
221
222
    private function _division_data($q) {
223
224
        $details = $this->getParliamentDivisionDetails($q->row(0));
225
226
227
        $house = $q->row(0)['house'];
228
        $args['division_id'] = $q->row(0)['division_id'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$args was never initialized. Although not strictly required by PHP, it is generally a good practice to add $args = array(); before regardless.
Loading history...
229
        $args['division_date'] = $q->row(0)['division_date'];
230
        $args['house'] = \MySociety\TheyWorkForYou\Utility\House::house_name_to_number($house);
231
232
        $q = $this->db->query(
233
            "SELECT pdv.person_id, vote, given_name, family_name, party
234
            FROM persondivisionvotes AS pdv JOIN person_names AS pn ON (pdv.person_id = pn.person_id)
235
            JOIN member AS m ON (pdv.person_id = m.person_id)
236
            WHERE division_id = :division_id
237
            AND house = :house AND entered_house <= :division_date AND left_house >= :division_date
238
            AND start_date <= :division_date AND end_date >= :division_date
239
            ORDER by family_name",
240
            $args
241
        );
242
243
        $votes = array(
244
          'yes_votes' => array(),
245
          'no_votes' => array(),
246
          'absent_votes' => array(),
247
          'both_votes' => array()
248
        );
249
250
        $party_breakdown = array(
251
          'yes_votes' => array(),
252
          'no_votes' => array(),
253
          'absent_votes' => array(),
254
          'both_votes' => array()
255
        );
256
257
        foreach ($q->data as $vote) {
258
            $detail = array(
259
              'person_id' => $vote['person_id'],
260
              'name' => $vote['given_name'] . ' ' . $vote['family_name'],
261
              'party' => $vote['party'],
262
              'teller' => false
263
            );
264
265
            if (strpos($vote['vote'], 'tell') !== FALSE) {
266
                $detail['teller'] = true;
267
            }
268
269
            if ($vote['vote'] == 'aye' or $vote['vote'] == 'tellaye') {
270
              $votes['yes_votes'][] = $detail;
271
              @$party_breakdown['yes_votes'][$detail['party']]++;
272
            } else if ($vote['vote'] == 'no' or $vote['vote'] == 'tellno') {
273
              $votes['no_votes'][] = $detail;
274
              @$party_breakdown['no_votes'][$detail['party']]++;
275
            } else if ($vote['vote'] == 'absent') {
276
              $votes['absent_votes'][] = $detail;
277
              @$party_breakdown['absent_votes'][$detail['party']]++;
278
            } else if ($vote['vote'] == 'both') {
279
              $votes['both_votes'][] = $detail;
280
              @$party_breakdown['both_votes'][$detail['party']]++;
281
            }
282
        }
283
284
        foreach ($votes as $vote => $count) { // array('yes_votes', 'no_votes', 'absent_votes', 'both_votes') as $vote) {
285
          $votes[$vote . '_by_party'] = $votes[$vote];
286
          usort($votes[$vote . '_by_party'], function ($a, $b) {
287
                return $a['party']>$b['party'];
288
            });
289
        }
290
291
        foreach ($party_breakdown as $vote => $parties) {
292
            $summary = array();
293
            foreach ($parties as $party => $count) {
294
                array_push($summary, "$party: $count");
295
            }
296
297
            sort($summary);
298
            $party_breakdown[$vote] = implode(', ', $summary);
299
        }
300
301
        $details = array_merge($details, $votes);
302
        $details['party_breakdown'] = $party_breakdown;
303
        $details['members'] = \MySociety\TheyWorkForYou\Utility\House::house_to_members($args['house']);
304
        $details['house'] = $house;
305
        $details['house_number'] = $args['house'];
306
307
        return $details;
308
    }
309
310
    public function getDivisionResultsForMember($division_id, $person_id) {
311
        $args = array(
312
            ':division_id' => $division_id,
313
            ':person_id' => $person_id
314
        );
315
        $q = $this->db->query(
316
            "SELECT division_id, division_title, yes_text, no_text, division_date, division_number, gid, vote
317
            FROM divisions JOIN persondivisionvotes USING(division_id)
318
            WHERE division_id = :division_id AND person_id = :person_id",
319
            $args
320
        );
321
322
        // if the vote was before or after the MP was in Parliament
323
        // then there won't be a row
324
        if ($q->rows == 0) {
325
            return false;
326
        }
327
328
        $details = $this->getDivisionDetails($q->row(0));
329
        return $details;
330
    }
331
332
    public function generateSummary($votes) {
333
        $max = $votes['max'];
334
        $min = $votes['min'];
335
336
        $actions = array(
337
            $votes['for'] . ' ' . make_plural('vote', $votes['for']) . ' for',
338
            $votes['against'] . ' ' . make_plural('vote', $votes['against']) . ' against'
339
        );
340
341
        if ( $votes['both'] ) {
342
            $actions[] = $votes['both'] . ' ' . make_plural('abstention', $votes['both']);
343
        }
344
        if ( $votes['absent'] ) {
345
            $actions[] = $votes['absent'] . ' ' . make_plural('absence', $votes['absent']);
346
        }
347
        if ($max == $min) {
348
            return join(', ', $actions) . ', in ' . $max;
349
        } else {
350
            return join(', ', $actions) . ', between ' . $min . '&ndash;' . $max;
351
        }
352
    }
353
354
    /**
355
     *
356
     * Get all the divisions a member has voted in keyed by policy
357
     *
358
     * Returns an array with keys for each policyID, each of these contains
359
     * the same structure as getMemberDivisionsForPolicy
360
     *
361
     */
362
363
    public function getAllMemberDivisionsByPolicy() {
364
        return $this->getMemberDivisionsForPolicy();
365
    }
366
367
368
    /**
369
     * Get the last n votes for a member
370
     *
371
     * @param $number int - How many divisions to return. Defaults to 20
372
     * @param $context string - The context of the page the results are being presented in.
373
     *    This affects the summary details and can either be 'Parliament' in which case the
374
     *    overall vote for all MPs is returned, plus additional information on how the MP passed
375
     *    in to the constructor voted, or the default of 'MP' which is just the vote of the
376
     *    MP passed in to the constructor.
377
     *
378
     * Returns an array of divisions
379
     */
380
    public function getRecentMemberDivisions($number = 20, $context = 'MP') {
381
        $args = array(':person_id' => $this->member->person_id, ':number' => $number);
382
        $q = $this->db->query(
383
            "SELECT division_id, division_title, yes_text, no_text, division_date, division_number, vote, gid,
384
            yes_total, no_total, absent_total, both_total, majority_vote
385
            FROM persondivisionvotes
386
                JOIN divisions USING(division_id)
387
            WHERE person_id = :person_id
388
            GROUP BY division_id
389
            ORDER by division_date DESC, division_id DESC LIMIT :number",
390
            $args
391
        );
392
393
        $divisions = array();
394
        $row_count = $q->rows();
395
        for ($n = 0; $n < $row_count; $n++) {
396
          if ($context == 'Parliament') {
397
              $divisions[] = $this->getParliamentDivisionDetails($q->row($n));
398
          } else {
399
              $divisions[] = $this->getDivisionDetails($q->row($n));
400
          }
401
        }
402
403
        return $divisions;
404
    }
405
406
407
    private function constructYesNoVoteDescription($direction, $title, $short_text) {
408
        $text = ' voted ';
409
        if ( $short_text ) {
410
            $text .= $short_text;
411
        } else {
412
            $text .= "$direction on <em>$title</em>";
413
        }
414
415
        return $text;
416
    }
417
418
419
    private function constructVoteDescription($vote, $yes_text, $no_text, $division_title) {
420
        /*
421
         * for most post 2010 votes we have nice single sentence summaries of
422
         * what voting for or against means so we use that if it's there, however
423
         * we don't have anything nice for people being absent or for pre 2010
424
         * votes so we need to generate some text using the title of the division
425
         */
426
427
        switch ( strtolower($vote) ) {
428
            case 'yes':
429
            case 'aye':
430
                $description = $this->constructYesNoVoteDescription('yes', $division_title, $yes_text);
431
                break;
432
            case 'no':
433
                $description = $this->constructYesNoVoteDescription('no', $division_title, $no_text);
434
                break;
435
            case 'absent':
436
                $description = ' was absent for a vote on <em>' . $division_title . '</em>';
437
                break;
438
            case 'both':
439
                $description = ' abstained on a vote on <em>' . $division_title . '</em>';
440
                break;
441
            case 'tellyes':
442
            case 'tellno':
443
            case 'tellaye':
444
                $description = ' acted as teller for a vote on <em>' . $division_title . '</em>';
445
                break;
446
            default:
447
                $description = $division_title;
448
        }
449
450
        return $description;
451
    }
452
453
    private function getBasicDivisionDetails($row, $vote) {
454
        $yes_text = $row['yes_text'];
455
        $no_text = $row['no_text'];
456
457
        $division = array(
458
            'division_id' => $row['division_id'],
459
            'date' => $row['division_date'],
460
            'gid' => fix_gid_from_db($row['gid']),
461
            'number' => $row['division_number'],
462
            'text' => $this->constructVoteDescription($vote, $yes_text, $no_text, $row['division_title']),
463
            'has_description' => $yes_text && $no_text,
464
            'vote' => $vote,
465
        );
466
467
        if ($row['gid']) {
468
            $division['debate_url'] = $this->divisionUrlFromGid($row['gid']);
469
        }
470
471
        # Policy-related information
472
473
        if (array_key_exists('direction', $row)) {
474
            $division['direction'] = $row['direction'];
475
            if ( strpos( $row['direction'], 'strong') !== FALSE ) {
476
                $division['strong'] = TRUE;
477
            } else {
478
                $division['strong'] = FALSE;
479
            }
480
        }
481
482
        return $division;
483
    }
484
485
    private function getDivisionDetails($row) {
486
        return $this->getBasicDivisionDetails($row, $row['vote']);
487
    }
488
489
    private function getParliamentDivisionDetails($row) {
490
        $division = $this->getBasicDivisionDetails($row, $row['majority_vote']);
491
492
        $division['mp_vote'] = '';
493
        if (array_key_exists('vote', $row)) {
494
          $mp_vote = ' was absent';
495
          if ($row['vote'] == 'aye') {
496
              $mp_vote = 'voted in favour';
497
          } else if ($row['vote'] == 'no') {
498
              $mp_vote = 'voted against';
499
          }
500
          $division['mp_vote'] = $mp_vote;
501
        }
502
        $division['division_title'] = $row['division_title'];
503
504
        $division['for'] = $row['yes_total'];
505
        $division['against'] = $row['no_total'];
506
        $division['both'] = $row['both_total'];
507
        $division['absent'] = $row['absent_total'];
508
509
        return $division;
510
    }
511
512
    private function divisionsByPolicy($q) {
513
        $policies = array();
514
515
        $row_count = $q->rows();
516
        for ($n = 0; $n < $row_count; $n++) {
517
            $policy_id = $q->field($n, 'policy_id');
518
519
            if ( !array_key_exists($policy_id, $policies) ) {
520
                $policies[$policy_id] = array(
521
                    'policy_id' => $policy_id,
522
                    'weak_count' => 0,
523
                    'divisions' => array()
524
                );
525
                if ( $this->policies ) {
526
                    $policies[$policy_id]['desc'] = $this->policies->getPolicies()[$policy_id];
527
                    $policies[$policy_id]['header'] = $this->policies->getPolicyDetails($policy_id);
528
                }
529
                if ( $this->positions ) {
530
                    $policies[$policy_id]['position'] = $this->positions->positionsById[$policy_id];
531
                }
532
            }
533
534
            $division = $this->getDivisionDetails($q->row($n));
535
536
            if ( !$division['strong'] ) {
537
                $policies[$policy_id]['weak_count']++;
538
            }
539
540
            $policies[$policy_id]['divisions'][] = $division;
541
        };
542
543
        return $policies;
544
    }
545
546
    private function divisionUrlFromGid($gid) {
547
        global $hansardmajors;
548
549
        $gid = get_canonical_gid($gid);
550
551
        $q = $this->db->query("SELECT gid, major FROM hansard WHERE epobject_id = ( SELECT subsection_id FROM hansard WHERE gid = :gid )", array( ':gid' => $gid ));
552
        $parent_gid = $q->field(0, 'gid');
553
        if ( !$parent_gid ) {
554
            return '';
555
        }
556
        $parent_gid = fix_gid_from_db($parent_gid);
557
        $url = new Url($hansardmajors[$q->field(0, 'major')]['page']);
558
        $url->insert(array('gid' => $parent_gid));
559
        return $url->generate();
560
    }
561
}
562