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 ( ce734d...5ed6c6 )
by
unknown
03:41
created

AbstractCalendar::getEventsItemized()   C

Complexity

Conditions 8
Paths 15

Size

Total Lines 50
Code Lines 22

Duplication

Lines 12
Ratio 24 %

Importance

Changes 6
Bugs 3 Features 0
Metric Value
c 6
b 3
f 0
dl 12
loc 50
rs 6.3636
cc 8
eloc 22
nc 15
nop 3
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
   * @return array
93
   *  An array of states keyed by unit
94
   */
95
  public function getStates(\DateTime $start_date, \DateTime $end_date) {
96
    $events = $this->getEvents($start_date, $end_date);
97
    $states = array();
98
    foreach ($events as $unit => $unit_events) {
99
      foreach ($unit_events as $event) {
100
        $states[$unit][$event->getValue()] = $event->getValue();
101
      }
102
    }
103
104
    return $states;
105
  }
106
107
  /**
108
   * Given a date range and a set of valid states it will return then units that are withing that
109
   * set of valid states.
110
   *
111
   * @param \DateTime $start_date
112
   * @param \DateTime $end_date
113
   * @param $valid_states
114
   *
115
   * @return CalendarResponse
116
   */
117
  public function getMatchingUnits(\DateTime $start_date, \DateTime $end_date, $valid_states, $constraints) {
118
    $units = array();
119
    $response = new CalendarResponse($start_date, $end_date, $valid_states);
120
    $keyed_units = $this->keyUnitsById();
121
122
    $states = $this->getStates($start_date, $end_date);
123
    foreach ($states as $unit => $unit_states) {
124
      // Create an array with just the states
125
      $current_states = array_keys($unit_states);
126
      // Compare the current states with the set of valid states
127
      $remaining_states = array_diff($current_states, $valid_states);
128
      if (count($remaining_states) == 0 ) {
129
        // Unit is in a state that is within the set of valid states so add to result set
130
        $units[$unit] = $unit;
131
        $response->addMatch($keyed_units[$unit], CalendarResponse::VALID_STATE);
132
      }
133
      else {
134
        $response->addMiss($keyed_units[$unit], CalendarResponse::INVALID_STATE);
135
      }
136
137
      $unit_constraints = $keyed_units[$unit]->getConstraints();
138
      $response->applyConstraints($unit_constraints);
139
    }
140
141
    $response->applyConstraints($constraints);
142
143
    return $response;
144
  }
145
146
  /**
147
   * Provides an itemized array of events keyed by the unit_id and divided by day,
148
   * hour and minute.
149
   *
150
   * @param \DateTime $start_date
151
   * @param \DateTime $end_date
152
   * @param String $granularity
153
   *
154
   * @return array
155
   */
156
    public function getEventsItemized(\DateTime $start_date, \DateTime $end_date, $granularity = Event::BAT_HOURLY) {
157
    // The final events we will return
158
    $events = array();
159
160
    $keyed_units = $this->keyUnitsById();
161
162
    $db_events = $this->store->getEventData($start_date, $end_date, array_keys($keyed_units));
163
164
    // Create a mock itemized event for the period in question - since event data is either
165
    // in the database or the default value we first create a mock event and then fill it in
166
    // accordingly
167
    $mock_event = new Event($start_date, $end_date, new Unit(0,0,null), $this->default_value);
168
    $itemized = $mock_event->itemize(new EventItemizer($mock_event, $granularity));
169
170
    // Cycle through each unit retrieved and provide it with a fully configured itemized mock event
171
    foreach ($db_events as $unit => $event) {
172
      // Add the mock event
173
      $events[$unit] = $itemized;
174
175
      $events[$unit][Event::BAT_DAY] = $this->itemizeDays($db_events, $itemized, $unit, $keyed_units);
176
177
      // Handle hours
178 View Code Duplication
      if (isset($itemized[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...
179
        $events[$unit][Event::BAT_HOUR] = $this->itemizeHours($db_events, $itemized, $unit, $keyed_units);
180
      } else {
181
        // No hours - set an empty array
182
        $events[$unit][Event::BAT_HOUR] = array();
183
      }
184
185
      // Handle minutes
186 View Code Duplication
      if (isset($itemized[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...
187
        $events[$unit][Event::BAT_MINUTE] = $this->itemizeMinutes($db_events, $itemized, $unit, $keyed_units);
188
      } else {
189
        // No minutes - set an empty array
190
        $events[$unit][Event::BAT_MINUTE] = array();
191
      }
192
193
    }
194
195
    // Check to see if any events came back from the db
196
    foreach ($keyed_units as $id => $unit) {
197
      // If we don't have any db events add mock events (itemized)
198
      if ((isset($events[$id]) && count($events[$id]) == 0) || !isset($events[$id])) {
199
        $empty_event = new Event($start_date, $end_date, $unit, $unit->getDefaultValue());
200
        $events[$id] = $empty_event->itemize(new EventItemizer($empty_event, $granularity));
201
      }
202
    }
203
204
    return $events;
205
  }
206
207
  /**
208
   * Helper function that cycles through db results and setups the BAT_DAY itemized array
209
   *
210
   * @param $db_events
211
   * @param $itemized
212
   * @param $unit
213
   * @param $keyed_units
214
   *
215
   * @return array
216
   */
217
  private function itemizeDays($db_events, $itemized, $unit, $keyed_units) {
218
    $result = array();
219
220
    foreach ($itemized[Event::BAT_DAY] as $year => $months) {
221
      foreach ($months as $month => $days) {
222
        // Check if month is defined in DB otherwise set to default value
223
        if (isset($db_events[$unit][Event::BAT_DAY][$year][$month])) {
224
          foreach ($days as $day => $value) {
225
            $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]);
226
          }
227
        }
228 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...
229
          foreach ($days as $day => $value) {
230
            $result[$year][$month][$day] = $keyed_units[$unit]->getDefaultValue();
231
          }
232
        }
233
      }
234
    }
235
236
    return $result;
237
  }
238
239
  /**
240
   * Helper function that cycles through db results and setups the BAT_HOUR itemized array
241
   * @param $db_events
242
   * @param $itemized
243
   * @param $unit
244
   * @param $keyed_units
245
   *
246
   * @return array
247
   */
248
  private function itemizeHours($db_events, $itemized, $unit, $keyed_units) {
249
250
    $result = array();
251
252
    foreach ($itemized[Event::BAT_HOUR] as $year => $months) {
253
      foreach ($months as $month => $days) {
254
        foreach ($days as $day => $hours) {
255
          foreach ($hours as $hour => $value) {
256
            if (isset($db_events[$unit][Event::BAT_HOUR][$year][$month][$day][$hour])) {
257
              $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]);
258
            }
259 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...
260
              // If nothing from db - then revert to the defaults
261
              $result[$year][$month][$day][$hour] = (int) $keyed_units[$unit]->getDefaultValue();
262
            }
263
          }
264
        }
265
      }
266
    }
267
268
    // Now fill in hour data coming from the database which the mock event did *not* cater for in the data structure
269
    if (isset($db_events[$unit][Event::BAT_HOUR])) {
270 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...
271
        foreach ($months as $month => $days) {
272
          foreach ($days as $day => $hours) {
273
            foreach ($hours as $hour => $value) {
274
              $result[$year][$month][$day][$hour] = ((int) $value == 0 ? $keyed_units[$unit]->getDefaultValue() : (int) $value);
275
            }
276
            ksort($result[$year][$month][$day], SORT_NATURAL);
277
          }
278
        }
279
      }
280
    }
281
282
    return $result;
283
  }
284
285
  /**
286
   * Helper function that cycles through db results and setups the BAT_MINUTE itemized array
287
   *
288
   * @param $db_events
289
   * @param $itemized
290
   * @param $unit
291
   * @param $keyed_units
292
   *
293
   * @return array
294
   */
295
  private function itemizeMinutes($db_events, $itemized, $unit, $keyed_units) {
296
    $result = array();
297
298
    foreach ($itemized[Event::BAT_MINUTE] as $year => $months) {
299
      foreach ($months as $month => $days) {
300
        foreach ($days as $day => $hours) {
301
          foreach ($hours as $hour => $minutes) {
302
            foreach ($minutes as $minute => $value) {
303
              if (isset($db_events[$unit][Event::BAT_MINUTE][$year][$month][$day][$hour][$minute])) {
304
                $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]);
305
              }
306 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...
307
                // If nothing from db - then revert to the defaults
308
                $result[$year][$month][$day][$hour][$minute] = (int) $keyed_units[$unit]->getDefaultValue();
309
              }
310
            }
311
          }
312
        }
313
      }
314
    }
315
316
    // Now fill in minute data coming from the database which the mock event did *not* cater for
317
    if (isset($db_events[$unit][Event::BAT_MINUTE])) {
318
      foreach ($db_events[$unit][Event::BAT_MINUTE] as $year => $months) {
319 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...
320
          foreach ($days as $day => $hours) {
321
            foreach ($hours as $hour => $minutes) {
322
              foreach ($minutes as $minute => $value) {
323
                $result[$year][$month][$day][$hour][$minute] = ((int) $value == 0 ? $keyed_units[$unit]->getDefaultValue() : (int) $value);
324
              }
325
              ksort($result[$year][$month][$day][$hour], SORT_NATURAL);
326
            }
327
          }
328
        }
329
      }
330
    }
331
332
    return $result;
333
  }
334
335
  /**
336
   * Given an itemized set of event data it will return an array of Events
337
   *
338
   * @param \DateTime $start_date
339
   * @param \DateTime $end_date
340
   * @param $events
341
   *
342
   * @return array
343
   */
344
  public function getEventsNormalized(\DateTime $start_date, \DateTime $end_date, $events) {
345
346
    $normalized_events = array();
347
348
    $events_copy = $events;
349
350
    foreach ($events_copy as $unit_id => $data) {
351
352
      // Make sure years are sorted
353
      ksort($data[Event::BAT_DAY]);
354
      ksort($data[Event::BAT_HOUR]);
355
      ksort($data[Event::BAT_MINUTE]);
356
357
      // Set up variables to keep track of stuff
358
      $current_value = NULL;
359
      $start_event = new \DateTime();
360
      $end_event = new \DateTime();
361
362
      foreach ($data[Event::BAT_DAY] as $year => $months) {
363
        // Make sure months are in right order
364
        ksort($months);
365
        foreach ($months as $month => $days) {
366
          foreach ($days as $day => $value) {
367
            if ($value == -1) {
368
              // Retrieve hour data
369
              $hour_data = $events[$unit_id][Event::BAT_HOUR][$year][$month][$day];
370
              ksort($hour_data, SORT_NATURAL);
371
              foreach ($hour_data as $hour => $hour_value) {
372
                if ($hour_value == -1) {
373
                  // We are going to need minute values
374
                  $minute_data = $events[$unit_id][Event::BAT_MINUTE][$year][$month][$day][$hour];
375
                  ksort($minute_data, SORT_NATURAL);
376
                  foreach ($minute_data as $minute => $minute_value) {
377
                    if ($current_value === $minute_value) {
378
                      // We are still in minutes and going through so add a minute
379
                      $end_event->add(new \DateInterval('PT1M'));
380
                    }
381 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...
382
                      // Value just switched - let us wrap up with current event and start a new one
383
                      $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
384
                      $start_event = clone($end_event->add(new \DateInterval('PT1M')));
385
                      $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':' . substr($minute,1));
386
                      $current_value = $minute_value;
387
                    }
388
                    if ($current_value === NULL) {
389
                      // We are down to minutes and haven't created and event yet - do one now
390
                      $start_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':' . substr($minute,1));
391
                      $end_event = clone($start_event);
392
                    }
393
                    $current_value = $minute_value;
394
                  }
395
                }
396
                elseif ($current_value === $hour_value) {
397
                  // We are in hours and can add something
398
                  $end_event->add(new \DateInterval('PT1H'));
399
                }
400 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...
401
                  // Value just switched - let us wrap up with current event and start a new one
402
                  $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
403
                  // Start event becomes the end event with a minute added
404
                  $start_event = clone($end_event->add(new \DateInterval('PT1M')));
405
                  // End event comes the current point in time
406
                  $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':00');
407
                  $current_value = $hour_value;
408
                }
409
                if ($current_value === NULL) {
410
                  // Got into hours and still haven't created an event so
411
                  $start_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':00');
412
                  // We will be occupying at least this hour so might as well mark it
413
                  $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . substr($hour, 1) . ':59');
414
                  $current_value = $hour_value;
415
                }
416
              }
417
            }
418
            elseif ($current_value === $value) {
419
              // We are adding a whole day so the end event gets moved to the end of the day we are adding
420
              $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '23:59');
421
            }
422 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...
423
              // Value just switched - let us wrap up with current event and start a new one
424
              $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
425
              // Start event becomes the end event with a minute added
426
              $start_event = clone($end_event->add(new \DateInterval('PT1M')));
427
              // End event becomes the current day which we have not account for yet
428
              $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '23:59');
429
              $current_value = $value;
430
            }
431
            if ($current_value === NULL) {
432
              // We have not created an event yet so let's do it now
433
              $start_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '00:00');
434
              $end_event = new \DateTime($year . '-' . $month . '-' . substr($day, 1) . ' ' . '23:59');
435
              $current_value = $value;
436
            }
437
          }
438
        }
439
      }
440
441
      // Add the last event in for which there is nothing in the loop to catch it
442
      $normalized_events[$unit_id][] = new Event($start_event, $end_event, $this->getUnit($unit_id), $current_value);
443
    }
444
445
    // Given the database structure we may get events that are not with the date ranges we were looking for
446
    // We get rid of them here so that the user has a clean result.
447
    foreach ($normalized_events as $unit_id => $events) {
448
      foreach ($events as $key => $event) {
449
        if ($event->overlaps($start_date, $end_date)) {
450
          // Adjust start or end dates of events so everything is within range
451
          if ($event->startsEarlier($start_date)) {
452
            $event->setStartDate($start_date);
453
          }
454
          if ($event->endsLater($end_date)) {
455
            $event->setEndDate($end_date);
456
          }
457
        }
458
        else {
459
          // Event completely not in range so unset it
460
          unset($normalized_events[$unit_id][$key]);
461
        }
462
      }
463
    }
464
465
    return $normalized_events;
466
  }
467
468
  /**
469
   * A simple utility function that given an array of datum=>value will group results based on
470
   * those that have the same value. Useful for grouping events based on state.
471
   *
472
   * @param $data
473
   * @param $length
474
   */
475
  public function groupData($data, $length) {
476
    $flipped = array();
477
    $e = 0;
478
    $j = 0;
479
    $old_value = NULL;
480
481
    foreach ($data as $datum => $value) {
482
      $j++;
483
      if ($j <= $length) {
484
        // If the value has changed and we are not just starting
485
        if (($value != $old_value)) {
486
          $e++;
487
          $flipped[$e][$value][$datum] = $datum;
488
          $old_value = $value;
489
        }
490
        else {
491
          $flipped[$e][$value][$datum] = $datum;
492
        }
493
      }
494
    }
495
  }
496
497
  /**
498
   * Return an array of unit ids from the set of units
499
   * supplied to the Calendar.
500
   *
501
   * @return array
502
   */
503
  protected function getUnitIds() {
504
    $unit_ids = array();
505
    foreach ($this->units as $unit) {
506
      $unit_ids[] = $unit->getUnitId();
507
    }
508
509
    return $unit_ids;
510
  }
511
512
  /**
513
   * Return an array of units keyed by unit id
514
   *
515
   * @return array
516
   */
517
  protected function keyUnitsById() {
518
    $keyed_units = array();
519
    foreach ($this->units as $unit) {
520
      $keyed_units[$unit->getUnitId()] = $unit;
521
    }
522
523
    return $keyed_units;
524
  }
525
526
  /**
527
   * Returns the unit object.
528
   *
529
   * @param $unit_id
530
   * @return Unit
531
   */
532
  protected function getUnit($unit_id) {
533
    $keyed =  $this->keyUnitsById();
534
    return $keyed[$unit_id];
535
  }
536
537
538
}
539