StatDataset   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 421
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 52
eloc 165
c 1
b 0
f 0
dl 0
loc 421
rs 7.44

20 Methods

Rating   Name   Duplication   Size   Complexity  
A getPercentValues() 0 18 4
A setDelimiter() 0 6 2
A getResults() 0 3 1
A getPieData() 0 12 2
A getAxis() 0 28 3
A getDelimiterPresentationPie() 0 15 3
A getFileSlot() 0 3 1
A combine() 0 3 1
A getDebugData() 0 14 2
B loadData() 0 30 7
A getSummary() 0 3 1
A aggregateSummary() 0 15 4
A __construct() 0 38 4
A getTimeRes() 0 3 1
A getMax() 0 3 1
A getDelimiterPresentation() 0 26 4
A calculateMax() 0 10 3
A getDelimiter() 0 6 2
A availDelimiters() 0 7 2
A getTopDelimiters() 0 15 4

How to fix   Complexity   

Complex Class

Complex classes like StatDataset often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StatDataset, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\statistics;
6
7
use Exception;
8
use SimpleSAML\Configuration;
9
use SimpleSAML\Error;
10
use SimpleSAML\Module;
11
use SimpleSAML\XHTML\Template;
12
13
/**
14
 * @package SimpleSAMLphp
15
 */
16
class StatDataset
17
{
18
    /** @var \SimpleSAML\Configuration */
19
    protected Configuration $statconfig;
20
21
    /** @var \SimpleSAML\Configuration */
22
    protected Configuration $ruleconfig;
23
24
    /** @var \SimpleSAML\Configuration */
25
    protected Configuration $timeresconfig;
26
27
    /** @var array */
28
    protected array $ruleid;
29
30
    /** @var string */
31
    protected string $fileslot;
32
33
    /** @var string */
34
    protected string $timeres;
35
36
    /** @var string */
37
    protected string $delimiter;
38
39
    /** @var array */
40
    protected array $results;
41
42
    /** @var array */
43
    protected array $summary;
44
45
    /** @var int */
46
    protected int $max;
47
48
    /** @var \SimpleSAML\Module\statistics\DateHandler */
49
    protected DateHandler $datehandlerFile;
50
51
    /** @var \SimpleSAML\Module\statistics\DateHandler */
52
    protected DateHandler $datehandlerTick;
53
54
55
    /**
56
     * Constructor
57
     *
58
     * @param \SimpleSAML\Configuration $statconfig
59
     * @param \SimpleSAML\Configuration $ruleconfig
60
     * @param array $ruleid
61
     * @param string $timeres
62
     * @param string $fileslot
63
     */
64
    public function __construct(
65
        Configuration $statconfig,
66
        Configuration $ruleconfig,
67
        array $ruleid,
68
        string $timeres,
69
        string $fileslot,
70
    ) {
71
        $this->statconfig = $statconfig;
72
        $this->ruleconfig = $ruleconfig;
73
74
        $timeresconfigs = $statconfig->getOptionalConfigItem('timeres', null);
75
        if ($timeresconfigs === null) {
76
            throw new Error\ConfigurationError('Missing \'timeres\' in module configuration.');
77
        }
78
79
        $timeresconfig = $timeresconfigs->getOptionalConfigItem($timeres, null);
80
        if ($timeresconfig === null) {
81
            throw new Error\ConfigurationError('Missing \'timeres.' . $timeres . '\' in module configuration.');
82
        }
83
84
        $this->timeresconfig = $timeresconfig;
85
        $this->ruleid = $ruleid;
86
        $this->fileslot = $fileslot;
87
        $this->timeres = $timeres;
88
89
        $this->delimiter = '_';
90
        $this->max = 0;
91
        $this->results = [];
92
        $this->summary = [];
93
94
        $this->datehandlerTick = new DateHandler($this->statconfig->getOptionalValue('offset', 0));
95
        if ($this->timeresconfig->getOptionalValue('customDateHandler', 'default') === 'month') {
96
            $this->datehandlerFile = new DateHandlerMonth(0);
97
        } else {
98
            $this->datehandlerFile = $this->datehandlerTick;
99
        }
100
101
        $this->loadData();
102
    }
103
104
105
    /**
106
     * @return string
107
     */
108
    public function getFileSlot(): string
109
    {
110
        return $this->fileslot;
111
    }
112
113
114
    /**
115
     * @return string
116
     */
117
    public function getTimeRes(): string
118
    {
119
        return $this->timeres;
120
    }
121
122
123
    /**
124
     * @param string|null $delimiter
125
     */
126
    public function setDelimiter(?string $delimiter = '_'): void
127
    {
128
        if (empty($delimiter)) {
129
            $delimiter = '_';
130
        }
131
        $this->delimiter = $delimiter;
132
    }
133
134
135
    /**
136
     * @return string|null
137
     */
138
    public function getDelimiter(): ?string
139
    {
140
        if ($this->delimiter === '_') {
141
            return null;
142
        }
143
        return $this->delimiter;
144
    }
145
146
147
    /**
148
     */
149
    public function calculateMax(): void
150
    {
151
        $maxvalue = 0;
152
        foreach ($this->results as $slot => &$res) {
153
            if (!array_key_exists($this->delimiter, $res)) {
154
                $res[$this->delimiter] = 0;
155
            }
156
            $maxvalue = max($res[$this->delimiter], $maxvalue);
157
        }
158
        $this->max = Graph\GoogleCharts::roof($maxvalue);
159
    }
160
161
162
    /**
163
     * @return array
164
     */
165
    public function getDebugData(): array
166
    {
167
        $debugdata = [];
168
169
        $slotsize = $this->timeresconfig->getValue('slot');
170
        $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
171
172
        foreach ($this->results as $slot => &$res) {
173
            $debugdata[$slot] = [
174
                $this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra),
175
                $res[$this->delimiter],
176
            ];
177
        }
178
        return $debugdata;
179
    }
180
181
182
    /**
183
     */
184
    public function aggregateSummary(): void
185
    {
186
        // aggregate summary table from dataset. To be used in the table view
187
        $this->summary = [];
188
        foreach ($this->results as $slot => $res) {
189
            foreach ($res as $key => $value) {
190
                if (array_key_exists($key, $this->summary)) {
191
                    $this->summary[$key] += $value;
192
                } else {
193
                    $this->summary[$key] = $value;
194
                }
195
            }
196
        }
197
        asort($this->summary);
198
        $this->summary = array_reverse($this->summary, true);
199
    }
200
201
202
    /**
203
     * @return array
204
     */
205
    public function getTopDelimiters(): array
206
    {
207
        // create a list of delimiter keys that has the highest total summary in this period
208
        $topdelimiters = [];
209
        $maxdelimiters = 4;
210
        $i = 0;
211
        foreach ($this->summary as $key => $value) {
212
            if ($key !== '_') {
213
                $topdelimiters[] = $key;
214
            }
215
            if ($i++ >= $maxdelimiters) {
216
                break;
217
            }
218
        }
219
        return $topdelimiters;
220
    }
221
222
223
    /**
224
     * @return array
225
     */
226
    public function availDelimiters(): array
227
    {
228
        $availDelimiters = [];
229
        foreach ($this->summary as $key => $value) {
230
            $availDelimiters[$key] = 1;
231
        }
232
        return array_keys($availDelimiters);
233
    }
234
235
236
    /**
237
     * @return array
238
     */
239
    public function getPieData(): array
240
    {
241
        $piedata = [];
242
        $sum = 0;
243
        $topdelimiters = $this->getTopDelimiters();
244
245
        foreach ($topdelimiters as $td) {
246
            $sum += $this->summary[$td];
247
            $piedata[] = number_format(100 * $this->summary[$td] / $this->summary['_'], 2);
248
        }
249
        $piedata[] = number_format(100 - 100 * ($sum / $this->summary['_']), 2);
250
        return $piedata;
251
    }
252
253
254
    /**
255
     * @return int
256
     */
257
    public function getMax(): int
258
    {
259
        return $this->max;
260
    }
261
262
263
    /**
264
     * @return array
265
     */
266
    public function getSummary(): array
267
    {
268
        return $this->summary;
269
    }
270
271
272
    /**
273
     * @return array
274
     */
275
    public function getResults(): array
276
    {
277
        return $this->results;
278
    }
279
280
281
    /**
282
     * @return array
283
     */
284
    public function getAxis(): array
285
    {
286
        $slotsize = $this->timeresconfig->getValue('slot');
287
        $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
288
        $axislabelint = $this->timeresconfig->getValue('axislabelint');
289
290
        $axis = [];
291
        $axispos = [];
292
        $xentries = count($this->results);
293
        $lastslot = 0;
294
        $i = 0;
295
296
        foreach ($this->results as $slot => $res) {
297
            $slot = intval($slot);
298
299
            // check if there should be an axis here...
300
            if ($slot % $axislabelint == 0) {
301
                $axis[] = $this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra);
302
                $axispos[] = (($i) / ($xentries - 1));
303
            }
304
305
            $lastslot = $slot;
306
            $i++;
307
        }
308
309
        $axis[] = $this->datehandlerTick->prettyDateSlot($lastslot + 1, $slotsize, $dateformat_intra);
310
311
        return ['axis' => $axis, 'axispos' => $axispos];
312
    }
313
314
315
    /**
316
     * Walk through dataset to get percent values from max into dataset[].
317
     * @return array
318
     */
319
    public function getPercentValues(): array
320
    {
321
        $i = 0;
322
        $dataset = [];
323
        foreach ($this->results as $slot => $res) {
324
            if (array_key_exists($this->delimiter, $res)) {
325
                if ($res[$this->delimiter] === null) {
326
                    $dataset[] = -1;
327
                } else {
328
                    $dataset[] = number_format(100 * $res[$this->delimiter] / $this->max, 2);
329
                }
330
            } else {
331
                $dataset[] = '0';
332
            }
333
            $i++;
334
        }
335
336
        return $dataset;
337
    }
338
339
340
    /**
341
     * @return array
342
     * @throws \Exception
343
     */
344
    public function getDelimiterPresentation(): array
345
    {
346
        $config = Configuration::getInstance();
347
        $t = new Template($config, 'statistics:statistics.twig');
348
349
        $availdelimiters = $this->availDelimiters();
350
351
        // create a delimiter presentation filter for this rule...
352
        if ($this->ruleconfig->hasValue('fieldPresentation')) {
353
            $fieldpresConfig = $this->ruleconfig->getOptionalConfigItem('fieldPresentation', null);
354
            if ($fieldpresConfig === null) {
355
                throw new Error\ConfigurationError('Missing \'fieldPresentation\' in module configuration.');
356
            }
357
            $classname = Module::resolveClass(
358
                $fieldpresConfig->getValue('class'),
359
                'Statistics\FieldPresentation',
360
            );
361
            if (!class_exists($classname)) {
362
                throw new Exception('Could not find field presentation plugin [' . $classname . ']: No class found');
363
            }
364
            $presentationHandler = new $classname($availdelimiters, $fieldpresConfig->getValue('config'), $t);
365
366
            return $presentationHandler->getPresentation();
367
        }
368
369
        return [];
370
    }
371
372
373
    /**
374
     * @return array
375
     */
376
    public function getDelimiterPresentationPie(): array
377
    {
378
        $topdelimiters = $this->getTopDelimiters();
379
        $delimiterPresentation = $this->getDelimiterPresentation();
380
381
        $pieaxis = [];
382
        foreach ($topdelimiters as $key) {
383
            $keyName = $key;
384
            if (array_key_exists($key, $delimiterPresentation)) {
385
                $keyName = $delimiterPresentation[$key];
386
            }
387
            $pieaxis[] = $keyName;
388
        }
389
        $pieaxis[] = 'Others';
390
        return $pieaxis;
391
    }
392
393
394
    /**
395
     */
396
    public function loadData(): void
397
    {
398
        $statdir = $this->statconfig->getValue('statdir');
399
        $resarray = [];
400
        $rules = $this->ruleid;
401
        foreach ($rules as $rule) {
402
            // Get file and extract results.
403
            $resultFileName = $statdir . '/' . $rule . '-' . $this->timeres . '-' . $this->fileslot . '.stat';
404
            if (!file_exists($resultFileName)) {
405
                throw new Exception('Aggregated statitics file [' . $resultFileName . '] not found.');
406
            }
407
            if (!is_readable($resultFileName)) {
408
                throw new Exception('Could not read statitics file [' . $resultFileName . ']. Bad file permissions?');
409
            }
410
            $resultfile = file_get_contents($resultFileName);
411
            $newres = unserialize($resultfile);
412
            if (empty($newres)) {
413
                throw new Exception('Aggregated statistics in file [' . $resultFileName . '] was empty.');
414
            }
415
            $resarray[] = $newres;
416
        }
417
418
        $combined = $resarray[0];
419
        $count = count($resarray);
420
        if ($count > 1) {
421
            for ($i = 1; $i < $count; $i++) {
422
                $combined = $this->combine($combined, $resarray[$i]);
423
            }
424
        }
425
        $this->results = $combined;
426
    }
427
428
429
    /**
430
     * @param array $result1
431
     * @param array $result2
432
     * @return array
433
     */
434
    public function combine(array $result1, array $result2): array
435
    {
436
        return array_merge($result1, $result2);
437
    }
438
}
439