GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 1d7aa8...8bbd62 )
by Ronald
06:21 queued 02:36
created

AbstractCalendar::getMatchingUnits()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 35
rs 6.7272
cc 7
eloc 20
nc 5
nop 5
1
<?php
2
3
/**
4
 * @file
5
 * Class AbstractCalendar
6
 */
7
8
namespace Roomify\Bat\Calendar;
9
10
use Roomify\Bat\Event\Event;
11
use Roomify\Bat\Unit\Unit;
12
use Roomify\Bat\Calendar\CalendarInterface;
13
use Roomify\Bat\Calendar\CalendarResponse;
14
use Roomify\Bat\Event\EventItemizer;
15
16
/**
17
 * Handles querying and updating state stores
18
 */
19
abstract class AbstractCalendar implements CalendarInterface {
20
21
  /**
22
   * The units we are dealing with. If no unit ids set the calendar will return
23
   * results for date range and all units within that range.
24
   *
25
   * @var array
26
   */
27
  protected $units;
28
29
30
  /**
31
   * The class that will access the actual event store where event data is held.
32
   *
33
   * @var
34
   */
35
  protected $store;
36
37
  /**
38
   * The default value for events. In the event store this is represented by 0 which is then
39
   * replaced by the default value provided in the constructor.
40
   *
41
   * @var
42
   */
43
  protected $default_value;
44
45
46
  /**
47
   * {@inheritdoc}
48
   */
49
  public function addEvents($events, $granularity) {
50
51
    $added = TRUE;
52
53
    foreach ($events as $event) {
54
      // Events save themselves so here we cycle through each and return true if all events
55
      // were saved
56
57
      $check = $event->saveEvent($this->store, $granularity);
58
59
      if ($check == FALSE) {
60
        $added = FALSE;
61
        break;
62
      }
63
    }
64
65
    return $added;
66
  }
67
68
  /**
69
   * Given a start and end time will retrieve events from the defined store.
70
   *
71
   * If unit_ids where defined it will filter for those unit ids.
72
   *
73
   * @param \DateTime $start_date
74
   * @param \DateTime $end_date
75
   * @return array
76
   */
77
  public function getEvents(\DateTime $start_date, \DateTime $end_date) {
78
    // We first get events in the itemized format
79
    $itemized_events = $this->getEventsItemized($start_date, $end_date);
80
81
    // We then normalize those events to create Events that get added to an array
82
    $events = $this->getEventsNormalized($start_date, $end_date, $itemized_events);
83
84
    return $events;
85
  }
86
87
  /**
88
   * Given a start and end time this will return the states units find themselves in for that range.
89
   *
90
   * @param \DateTime $start_date
91
   * @param \DateTime $end_date
92
   *
93
   * @return array
94
   *  An array of states keyed by unit
95
   */
96
  public function getStates(\DateTime $start_date, \DateTime $end_date) {
97
    $events = $this->getEvents($start_date, $end_date);
98
    $states = array();
99
    foreach ($events as $unit => $unit_events) {
100
      foreach ($unit_events as $event) {
101
        $states[$unit][$event->getValue()] = $event->getValue();
102
      }
103
    }
104
105
    return $states;
106
  }
107
108
  /**
109
   * Given a date range and a set of valid states it will return all units within the
110
   * set of valid states.
111
   *
112
   * @param \DateTime $start_date
113
   * @param \DateTime $end_date
114
   * @param $valid_states
115
   * @param $constraints
116
   * @param $intersect
117
   *
118
   * @return CalendarResponse
119
   */
120
  public function getMatchingUnits(\DateTime $start_date, \DateTime $end_date, $valid_states, $constraints = array(), $intersect = FALSE) {
121
    $units = array();
122
    $response = new CalendarResponse($start_date, $end_date, $valid_states);
123
    $keyed_units = $this->keyUnitsById();
124
125
    $states = $this->getStates($start_date, $end_date);
126
    foreach ($states as $unit => $unit_states) {
127
      // Create an array with just the states
128
      $current_states = array_keys($unit_states);
129
130
      // Compare the current states with the set of valid states
131
      if ($intersect) {
132
        $remaining_states = array_intersect($current_states, $valid_states);
133
      }
134
      else {
135
        $remaining_states = array_diff($current_states, $valid_states);
136
      }
137
138
      if ((count($remaining_states) == 0 && !$intersect) || (count($remaining_states) > 0 && $intersect)) {
139
        // Unit is in a state that is within the set of valid states so add to result set
140
        $units[$unit] = $unit;
141
        $response->addMatch($keyed_units[$unit], CalendarResponse::VALID_STATE);
142
      }
143
      else {
144
        $response->addMiss($keyed_units[$unit], CalendarResponse::INVALID_STATE);
145
      }
146
147
      $unit_constraints = $keyed_units[$unit]->getConstraints();
148
      $response->applyConstraints($unit_constraints);
149
    }
150
151
    $response->applyConstraints($constraints);
152
153
    return $response;
154
  }
155
156
  /**
157
   * Provides an itemized array of events keyed by the unit_id and divided by day,
158
   * hour and minute.
159
   *
160
   * @param \DateTime $start_date
161
   * @param \DateTime $end_date
162
   * @param String $granularity
163
   *
164
   * @return array
165
   */
166
  public function getEventsItemized(\DateTime $start_date, \DateTime $end_date, $granularity = Event::BAT_HOURLY) {
167
    // The final events we will return
168
    $events = array();
169
170
    $keyed_units = $this->keyUnitsById();
171
172
    $db_events = $this->store->getEventData($start_date, $end_date, array_keys($keyed_units));
173
174
    // Create a mock itemized event for the period in question - since event data is either
175
    // in the database or the default value we first create a mock event and then fill it in
176
    // accordingly
177
    $mock_event = new Event($start_date, $end_date, new Unit(0,0), $this->default_value);
178
    $itemized = $mock_event->itemize(new EventItemizer($mock_event, $granularity));
179
180
    // Cycle through each unit retrieved and provide it with a fully configured itemized mock event
181
    foreach ($db_events as $unit => $event) {
182
      // Add the mock event
183
      $events[$unit] = $itemized;
184
185
      $events[$unit][Event::BAT_DAY] = $this->itemizeDays($db_events, $itemized, $unit, $keyed_units);
186
187
      // Handle hours
188 View Code Duplication
      if (isset($itemized[Event::BAT_HOUR]) || isset($db_events[$unit][Event::BAT_HOUR])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
189
        $events[$unit][Event::BAT_HOUR] = $this->itemizeHours($db_events, $itemized, $unit, $keyed_units);
190
      } else {
191
        // No hours - set an empty array
192
        $events[$unit][Event::BAT_HOUR] = array();
193
      }
194
195
      // Handle minutes
196 View Code Duplication
      if (isset($itemized[Event::BAT_MINUTE]) || isset($db_events[$unit][Event::BAT_MINUTE])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
        $events[$unit][Event::BAT_MINUTE] = $this->itemizeMinutes($db_events, $itemized, $unit, $keyed_units);
198
      } else {
199
        // No minutes - set an empty array
200
        $events[$unit][Event::BAT_MINUTE] = array();
201
      }
202
203
    }
204
205
    // Check to see if any events came back from the db
206
    foreach ($keyed_units as $id => $unit) {
207
      // If we don't have any db events add mock events (itemized)
208
      if ((isset($events[$id]) && count($events[$id]) == 0) || !isset($events[$id])) {
209
        $empty_event = new Event($start_date, $end_date, $unit, $unit->getDefaultValue());
210
        $events[$id] = $empty_event->itemize(new EventItemizer($empty_event, $granularity));
211
      }
212
    }
213
214
    return $events;
215
  }
216
217
  /**
218
   * Helper function that cycles through db results and setups the BAT_DAY itemized array
219
   *
220
   * @param $db_events
221
   * @param $itemized
222
   * @param $unit
223
   * @param $keyed_units
224
   *
225
   * @return array
226
   */
227
  private function itemizeDays($db_events, $itemized, $unit, $keyed_units) {
228
    $result = array();
229
230
    foreach ($itemized[Event::BAT_DAY] as $year => $months) {
231
      foreach ($months as $month => $days) {
232
        // Check if month is defined in DB otherwise set to default value
233
        if (isset($db_events[$unit][Event::BAT_DAY][$year][$month])) {
234
          foreach ($days as $day => $value) {
235
            $result[$year][$month][$day] = ((int)$db_events[$unit][Event::BAT_DAY][$year][$month][$day] == 0 ? $keyed_units[$unit]->getDefaultValue() : (int)$db_events[$unit][Event::BAT_DAY][$year][$month][$day]);
236
          }
237
        }
238 View Code Duplication
        else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
          foreach ($days as $day => $value) {
240
            $result[$year][$month][$day] = $keyed_units[$unit]->getDefaultValue();
241
          }
242
        }
243
      }
244
    }
245
246
    return $result;
247
  }
248
249
  /**
250
   * Helper function that cycles through db results and setups the BAT_HOUR itemized array
251
   * @param $db_events
252
   * @param $itemized
253
   * @param $unit
254
   * @param $keyed_units
255
   *
256
   * @return array
257
   */
258
  private function itemizeHours($db_events, $itemized, $unit, $keyed_units) {
259
260
    $result = array();
261
262
    if (isset($itemized[Event::BAT_HOUR])) {
263
      foreach ($itemized[Event::BAT_HOUR] as $year => $months) {
264
        foreach ($months as $month => $days) {
265
          foreach ($days as $day => $hours) {
266
            foreach ($hours as $hour => $value) {
267
              if (isset($db_events[$unit][Event::BAT_HOUR][$year][$month][$day][$hour])) {
268
                $result[$year][$month][$day][$hour] = ((int) $db_events[$unit][Event::BAT_HOUR][$year][$month][$day][$hour] == 0 ? $keyed_units[$unit]->getDefaultValue() : (int) $db_events[$unit][Event::BAT_HOUR][$year][$month][$day][$hour]);
269
              }
270 View Code Duplication
              else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
271
                // If nothing from db - then revert to the defaults
272
                $result[$year][$month][$day][$hour] = (int) $keyed_units[$unit]->getDefaultValue();
273
              }
274
            }
275
          }
276
        }
277
      }
278
    }
279
280
    // Now fill in hour data coming from the database which the mock event did *not* cater for in the data structure
281
    if (isset($db_events[$unit][Event::BAT_HOUR])) {
282 View Code Duplication
      foreach ($db_events[$unit][Event::BAT_HOUR] as $year => $months) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
283
        foreach ($months as $month => $days) {
284
          foreach ($days as $day => $hours) {
285
            foreach ($hours as $hour => $value) {
286
              $result[$year][$month][$day][$hour] = ((int) $value == 0 ? $keyed_units[$unit]->getDefaultValue() : (int) $value);
287
            }
288
            ksort($result[$year][$month][$day], SORT_NATURAL);
289
          }
290
        }
291
      }
292
    }
293
294
    return $result;
295
  }
296
297
  /**
298
   * Helper function that cycles through db results and setups the BAT_MINUTE itemized array
299
   *
300
   * @param $db_events
301
   * @param $itemized
302
   * @param $unit
303
   * @param $keyed_units
304
   *
305
   * @return array
306
   */
307
  private function itemizeMinutes($db_events, $itemized, $unit, $keyed_units) {
308
    $result = array();
309
310
    if (isset($itemized[Event::BAT_MINUTE])) {
311
      foreach ($itemized[Event::BAT_MINUTE] as $year => $months) {
312
        foreach ($months as $month => $days) {
313
          foreach ($days as $day => $hours) {
314
            foreach ($hours as $hour => $minutes) {
315
              foreach ($minutes as $minute => $value) {
316
                if (isset($db_events[$unit][Event::BAT_MINUTE][$year][$month][$day][$hour][$minute])) {
317
                  $result[$year][$month][$day][$hour][$minute] = ((int) $db_events[$unit][Event::BAT_MINUTE][$year][$month][$day][$hour][$minute] == 0 ? $keyed_units[$unit]->getDefaultValue() : (int) $db_events[$unit][Event::BAT_MINUTE][$year][$month][$day][$hour][$minute]);
318
                }
319 View Code Duplication
                else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
                  // If nothing from db - then revert to the defaults
321
                  $result[$year][$month][$day][$hour][$minute] = (int) $keyed_units[$unit]->getDefaultValue();
322
                }
323
              }
324
            }
325
          }
326
        }
327
      }
328
    }
329
330
    // Now fill in minute data coming from the database which the mock event did *not* cater for
331
    if (isset($db_events[$unit][Event::BAT_MINUTE])) {
332
      foreach ($db_events[$unit][Event::BAT_MINUTE] as $year => $months) {
333 View Code Duplication
        foreach ($months as $month => $days) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
334
          foreach ($days as $day => $hours) {
335
            foreach ($hours as $hour => $minutes) {
336
              foreach ($minutes as $minute => $value) {
337
                $result[$year][$month][$day][$hour][$minute] = ((int) $value == 0 ? $keyed_units[$unit]->getDefaultValue() : (int) $value);
338
              }
339
              ksort($result[$year][$month][$day][$hour], SORT_NATURAL);
340
            }
341
          }
342
        }
343
      }
344
    }
345
346
    return $result;
347
  }
348
349
  /**
350
   * Given an itemized set of event data it will return an array of Events
351
   *
352
   * @param \DateTime $start_date
353
   * @param \DateTime $end_date
354
   * @param $events
355
   *
356
   * @return array
357
   */
358
  public function getEventsNormalized(\DateTime $start_date, \DateTime $end_date, $events) {
359
360
    $normalized_events = array();
361
362
    $events_copy = $events;
363
364
    foreach ($events_copy as $unit_id => $data) {
365
366
      // Make sure years are sorted
367
      ksort($data[Event::BAT_DAY]);
368
      ksort($data[Event::BAT_HOUR]);
369
      ksort($data[Event::BAT_MINUTE]);
370
371
      // Set up variables to keep track of stuff
372
      $current_value = NULL;
373
      $start_event = new \DateTime();
374
      $end_event = new \DateTime();
375
376
      foreach ($data[Event::BAT_DAY] as $year => $months) {
377
        // Make sure months are in right order
378
        ksort($months);
379
        foreach ($months as $month => $days) {
380
          foreach ($days as $day => $value) {
381
            if ($value == -1) {
382
              // Retrieve hour data
383
              $hour_data = $events[$unit_id][Event::BAT_HOUR][$year][$month][$day];
384
              ksort($hour_data, SORT_NATURAL);
385
              foreach ($hour_data as $hour => $hour_value) {
386
                if ($hour_value == -1) {
387
                  // We are going to need minute values
388
                  $minute_data = $events[$unit_id][Event::BAT_MINUTE][$year][$month][$day][$hour];
389
                  ksort($minute_data, SORT_NATURAL);
390
                  foreach ($minute_data as $minute => $minute_value) {
391
                    if ($current_value === $minute_value) {
392
                      // We are still in minutes and going through so add a minute
393
                      $end_event->add(new \DateInterval('PT1M'));
394
                    }
395 View Code Duplication
                    elseif (($current_value != $minute_value) && ($current_value !== NULL)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
396
                      // Value just switched - let us wrap up with current event and start a new one
397
                      $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
398
                      $start_event = clone($end_event->add(new \DateInterval('PT1M')));
399
                      $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':' . substr($minute,1));
400
                      $current_value = $minute_value;
401
                    }
402
                    if ($current_value === NULL) {
403
                      // We are down to minutes and haven't created and event yet - do one now
404
                      $start_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':' . substr($minute,1));
405
                      $end_event = clone($start_event);
406
                    }
407
                    $current_value = $minute_value;
408
                  }
409
                }
410
                elseif ($current_value === $hour_value) {
411
                  // We are in hours and can add something
412
                  $end_event->add(new \DateInterval('PT1H'));
413
                }
414 View Code Duplication
                elseif (($current_value != $hour_value) && ($current_value !== NULL)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
415
                  // Value just switched - let us wrap up with current event and start a new one
416
                  $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
417
                  // Start event becomes the end event with a minute added
418
                  $start_event = clone($end_event->add(new \DateInterval('PT1M')));
419
                  // End event comes the current point in time
420
                  $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':59');
421
                  $current_value = $hour_value;
422
                }
423
                if ($current_value === NULL) {
424
                  // Got into hours and still haven't created an event so
425
                  $start_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':00');
426
                  // We will be occupying at least this hour so might as well mark it
427
                  $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':59');
428
                  $current_value = $hour_value;
429
                }
430
              }
431
            }
432
            elseif ($current_value === $value) {
433
              // We are adding a whole day so the end event gets moved to the end of the day we are adding
434
              $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '23:59');
435
            }
436 View Code Duplication
            elseif (($current_value !== $value) && ($current_value !== NULL)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
437
              // Value just switched - let us wrap up with current event and start a new one
438
              $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
439
              // Start event becomes the end event with a minute added
440
              $start_event = clone($end_event->add(new \DateInterval('PT1M')));
441
              // End event becomes the current day which we have not account for yet
442
              $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '23:59');
443
              $current_value = $value;
444
            }
445
            if ($current_value === NULL) {
446
              // We have not created an event yet so let's do it now
447
              $start_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '00:00');
448
              $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '23:59');
449
              $current_value = $value;
450
            }
451
          }
452
        }
453
      }
454
455
      // Add the last event in for which there is nothing in the loop to catch it
456
      $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
457
    }
458
459
    // Given the database structure we may get events that are not with the date ranges we were looking for
460
    // We get rid of them here so that the user has a clean result.
461
    foreach ($normalized_events as $unit_id => $events) {
462
      foreach ($events as $key => $event) {
463
        if ($event->overlaps($start_date, $end_date)) {
464
          // Adjust start or end dates of events so everything is within range
465
          if ($event->startsEarlier($start_date)) {
466
            $event->setStartDate($start_date);
467
          }
468
          if ($event->endsLater($end_date)) {
469
            $event->setEndDate($end_date);
470
          }
471
        }
472
        else {
473
          // Event completely not in range so unset it
474
          unset($normalized_events[$unit_id][$key]);
475
        }
476
      }
477
    }
478
479
    return $normalized_events;
480
  }
481
482
  /**
483
   * A simple utility function that given an array of datum=>value will group results based on
484
   * those that have the same value. Useful for grouping events based on state.
485
   *
486
   * @param $data
487
   * @param $length
488
   */
489
  public function groupData($data, $length) {
490
    $flipped = array();
491
    $e = 0;
492
    $j = 0;
493
    $old_value = NULL;
494
495
    foreach ($data as $datum => $value) {
496
      $j++;
497
      if ($j <= $length) {
498
        // If the value has changed and we are not just starting
499
        if (($value != $old_value)) {
500
          $e++;
501
          $flipped[$e][$value][$datum] = $datum;
502
          $old_value = $value;
503
        }
504
        else {
505
          $flipped[$e][$value][$datum] = $datum;
506
        }
507
      }
508
    }
509
  }
510
511
  /**
512
   * Return an array of unit ids from the set of units
513
   * supplied to the Calendar.
514
   *
515
   * @return array
516
   */
517
  protected function getUnitIds() {
518
    $unit_ids = array();
519
    foreach ($this->units as $unit) {
520
      $unit_ids[] = $unit->getUnitId();
521
    }
522
523
    return $unit_ids;
524
  }
525
526
  /**
527
   * Return an array of units keyed by unit id
528
   *
529
   * @return array
530
   */
531
  protected function keyUnitsById() {
532
    $keyed_units = array();
533
    foreach ($this->units as $unit) {
534
      $keyed_units[$unit->getUnitId()] = $unit;
535
    }
536
537
    return $keyed_units;
538
  }
539
540
  /**
541
   * Returns the unit object.
542
   *
543
   * @param $unit_id
544
   * @return Unit
545
   */
546
  protected function getUnit($unit_id) {
547
    $keyed =  $this->keyUnitsById();
548
    return $keyed[$unit_id];
549
  }
550
551
}
552