Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.72% covered (warning)
87.72%
150 / 171
88.24% covered (warning)
88.24%
15 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
Exchange
87.72% covered (warning)
87.72%
150 / 171
88.24% covered (warning)
88.24%
15 / 17
105.00
0.00% covered (danger)
0.00%
0 / 1
 getDefaults
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 setPeriod
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addDictionaryEntry
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 addDataSet
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 withLessData
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getPositionByName
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 withCalculatedTotals
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 withMaxByHour
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
8
 withRequestsSum
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 withAverage
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
132
 withUncapturedRequestRowSortedLast
96.00% covered (success)
96.00%
24 / 25
0.00% covered (danger)
0.00%
0 / 1
9
 withMaxAndAverageFromWaitingTime
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
10
 getCalculatedTotals
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 toHashed
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getHashData
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
8
 toGrouped
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getGroupedHashSet
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
9
1<?php
2
3namespace BO\Zmsentities;
4
5/**
6 * @SuppressWarnings(Complexity)
7 * @SuppressWarnings(PublicMethod)
8 *
9 */
10class Exchange extends Schema\Entity
11{
12    public const PRIMARY = 'firstDay';
13
14    /**
15     * Statistik CASE labels (must match warehouse SQL in ExchangeRequest* queries).
16     */
17    public const REQUEST_STAT_NAME_UNCATEGORIZED = 'uncategorized';
18
19    public const REQUEST_STAT_NAME_NONEXISTENT = 'nonexistent';
20
21    public static $schema = "exchange.json";
22
23    public function getDefaults()
24    {
25        return [
26            'firstDay' => new Day(),
27            'lastDay' => new Day(),
28            'period' => 'day',
29            'dictionary' => [ ],
30            'data' => [ ]
31        ];
32    }
33
34    public function setPeriod(\DateTimeInterface $firstDay, \DateTimeInterface $lastDay, $period = 'day')
35    {
36        $this->firstDay = (new Day())->setDateTime($firstDay);
37        $this->lastDay = (new Day())->setDateTime($lastDay);
38        $this->period = $period;
39        return $this;
40    }
41
42    public function addDictionaryEntry($variable, $type = 'string', $description = '', $reference = '')
43    {
44        $position = count($this['dictionary']);
45        $this['dictionary'][$position] = [
46            'position' => $position,
47            'variable' => $variable,
48            'type' => $type,
49            'description' => $description,
50            'reference' => $reference
51        ];
52        return $this;
53    }
54
55    public function addDataSet($values)
56    {
57        if (!is_array($values) && !$values instanceof \Traversable) {
58            throw new \Exception("Values have to be of type array");
59        }
60        if (count($this->dictionary) != count($values)) {
61            throw new \Exception("Mismatching dictionary settings for values (count mismatch)");
62        }
63        $this->data[] = $values;
64    }
65
66    public function withLessData()
67    {
68        $entity = clone $this;
69        if (isset($entity['firstDay'])) {
70            unset($entity['firstDay']);
71        }
72        if (isset($entity['lastDay'])) {
73            unset($entity['lastDay']);
74        }
75        if (isset($entity['period'])) {
76            unset($entity['period']);
77        }
78        return $entity;
79    }
80
81    public function getPositionByName($name)
82    {
83        if (isset($this->dictionary)) {
84            foreach ($this->dictionary as $entry) {
85                if (isset($entry['variable']) && $entry['variable'] == $name) {
86                    return $entry['position'];
87                }
88            }
89        }
90        return false;
91    }
92
93    public function withCalculatedTotals(array $keysToCalculate = ['count'], $dateName = 'name')
94    {
95        $entity = clone $this;
96        $namePosition = $this->getPositionByName($dateName);
97        if ($namePosition) {
98            $totals = array_fill(0, count($entity->data[0]), 0);
99            $totals[$namePosition] = 'totals';
100            foreach ($keysToCalculate as $name) {
101                $calculatePosition = $this->getPositionByName($name);
102                foreach ($this->data as $item) {
103                    foreach ($item as $position => $data) {
104                        if (is_numeric($data) && $calculatePosition == $position) {
105                            $totals[$position] += $data;
106                        }
107                    }
108                }
109            }
110            $entity->addDataSet($totals);
111        }
112        return $entity;
113    }
114
115    public function withMaxByHour(array $keysToCalculate = ['count'])
116    {
117        $entity = clone $this;
118        $maxima = [];
119        foreach ($entity->data as $dateItems) {
120            foreach ($dateItems as $hour => $hourItems) {
121                foreach ($hourItems as $key => $value) {
122                    if (is_numeric($value) && in_array($key, $keysToCalculate)) {
123                        $maxima[$hour][$key] = (
124                          isset($maxima[$hour][$key]) && $maxima[$hour][$key] > $value
125                        ) ? $maxima[$hour][$key] : $value;
126                    }
127                }
128            }
129            $entity->data['max'] = $maxima;
130        }
131        return $entity;
132    }
133
134    public function withRequestsSum($keysToCalculate = ['requestscount'])
135    {
136        $entity = clone $this;
137        $sum = [];
138        foreach ($entity->data as $name => $entry) {
139            $sum[$name] = 0;
140            foreach ($entry as $dateItem) {
141                foreach ($dateItem as $key => $value) {
142                    if (is_numeric($value) && in_array($key, $keysToCalculate)) {
143                        $sum[$name] += $value;
144                    }
145                }
146            }
147        }
148        $entity->data['sum'] = $sum;
149        return $entity;
150    }
151
152    public function withAverage($keyToCalculate)
153    {
154        $entity = clone $this;
155        $average = [];
156
157        foreach ($entity->data as $name => $entry) {
158            if (!is_array($entry) && !($entry instanceof \Traversable)) {
159                // Skip or handle non-iterable $entry appropriately.
160                continue;
161            }
162
163            $average[$name . '_sum'] = 0;
164            $average[$name . '_count'] = 0;
165
166            foreach ($entry as $dateItem) {
167                if (!is_array($dateItem) && !($dateItem instanceof \Traversable)) {
168                    // Skip or handle non-iterable $dateItem appropriately.
169                    continue;
170                }
171
172                foreach ($dateItem as $key => $value) {
173                    if (!is_numeric($value) || $key !== $keyToCalculate) {
174                        // Skip non-numeric values or when the key doesn't match.
175                        continue;
176                    }
177
178                    $average[$name . '_sum'] += $value;
179                    $average[$name . '_count']++;
180                }
181            }
182
183            $average[$name] = $average[$name . '_count'] > 0
184                ? round($average[$name . '_sum'] / $average[$name . '_count'], 2)
185                : null;
186        }
187
188        $entity->data['average_' . $keyToCalculate] = $average;
189        return $entity;
190    }
191
192    /**
193     * Sort service rows alphabetically; place synthetic stat rows last
194     * (before aggregated keys sum / average_*).
195     */
196    public function withUncapturedRequestRowSortedLast(): self
197    {
198        $entity = clone $this;
199        if (!is_array($entity->data)) {
200            return $entity;
201        }
202
203        $reserved = ['sum', 'average_processingtime'];
204        $tailStatNames = [
205            self::REQUEST_STAT_NAME_UNCATEGORIZED,
206            self::REQUEST_STAT_NAME_NONEXISTENT,
207        ];
208        $serviceRows = [];
209        foreach ($entity->data as $key => $value) {
210            if (in_array($key, $reserved, true) || in_array($key, $tailStatNames, true)) {
211                continue;
212            }
213            $serviceRows[$key] = $value;
214        }
215
216        uksort($serviceRows, static function ($a, $b) {
217            return strnatcasecmp((string) $a, (string) $b);
218        });
219
220        $ordered = $serviceRows;
221        foreach ($tailStatNames as $tailKey) {
222            if (array_key_exists($tailKey, $entity->data)) {
223                $ordered[$tailKey] = $entity->data[$tailKey];
224            }
225        }
226        foreach ($reserved as $key) {
227            if (array_key_exists($key, $entity->data)) {
228                $ordered[$key] = $entity->data[$key];
229            }
230        }
231        $entity->data = $ordered;
232        return $entity;
233    }
234
235    public function withMaxAndAverageFromWaitingTime()
236    {
237        $entity = clone $this;
238        foreach ($entity->data as $date => $dateItems) {
239            $maxima = 0;
240            $total = 0;
241            $count = 0;
242            foreach ($dateItems as $hourItems) {
243                foreach ($hourItems as $key => $value) {
244                    if (is_numeric($value) && 'waitingtime' == $key && 0 < $value) {
245                        $total += $value;
246                        $count += 1;
247                        $maxima = ($maxima > $value) ? $maxima : $value;
248                    }
249                }
250            }
251            $entity->data[$date]['max'] = $maxima;
252            $entity->data[$date]['average'] = (! $total || ! $count) ? 0 : floor($total / $count);
253        }
254        return $entity;
255    }
256
257    public function getCalculatedTotals()
258    {
259        foreach (array_reverse($this->data) as $item) {
260            foreach ($item as $data) {
261                if ($data == 'totals') {
262                    return $item;
263                }
264            }
265        }
266        return null;
267    }
268
269    public function toHashed(array $hashfields = [])
270    {
271        $entity = clone $this;
272        $entity->data = $this->getHashData($hashfields);
273        unset($entity->dictionary);
274        return $entity;
275    }
276
277    public function getHashData(array $hashfields = [], $first = false)
278    {
279        $hash = [];
280        foreach ($this->dictionary as $entry) {
281            foreach ($this->data as $key => $item) {
282                foreach ($item as $position => $data) {
283                    if ($entry['position'] == $position) {
284                        if (count($hashfields) && in_array($entry['variable'], $hashfields)) {
285                            $hash[$key][$entry['variable']] = $data;
286                        } else {
287                            $hash[$key][$entry['variable']] = $data;
288                        }
289                    }
290                }
291            }
292        }
293        return ($first) ? reset($hash) : $hash;
294    }
295
296    public function toGrouped(array $fields, array $hashfields)
297    {
298        $entity = clone $this;
299        $entity->data = $this->getGroupedHashSet($fields, $hashfields);
300        unset($entity->dictionary);
301        return $entity;
302    }
303
304    public function getGroupedHashSet(array $fields, array $hashfields)
305    {
306        $list = [];
307        if (count($fields)) {
308            $field = array_shift($fields);
309            $fieldposition = $this->getPositionByName($field);
310
311            $requestscountPosition = $this->getPositionByName('requestscount');
312
313
314            foreach ($this->data as $element) {
315                if (isset($element[$fieldposition])) {
316                    if (!isset($list[$element[$fieldposition]])) {
317                        $list[$element[$fieldposition]] = clone $this;
318                        $list[$element[$fieldposition]]->data = [];
319                    }
320                    if ($requestscountPosition !== false && isset($element[$requestscountPosition])) {
321                        $element[$requestscountPosition] = (int) $element[$requestscountPosition];
322                    }
323
324                    $list[$element[$fieldposition]]->data[] = $element;
325                }
326            }
327
328            foreach ($list as $key => $row) {
329                if ($row instanceof Exchange) {
330                    $list[$key] = $row->getGroupedHashSet($fields, $hashfields);
331                }
332            }
333        } else {
334            return $this->getHashData($hashfields, true);
335        }
336        return $list;
337    }
338}