Passed
Push — master ( bbb42a...1aee7b )
by Marcel
03:19 queued 13s
created

VariableService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 9
rs 10
1
<?php
2
/**
3
 * Analytics
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the LICENSE.md file.
7
 *
8
 * @author Marcel Scherello <[email protected]>
9
 * @copyright 2019-2022 Marcel Scherello
10
 */
11
12
namespace OCA\Analytics\Service;
13
14
use OCA\Analytics\Db\DatasetMapper;
15
use OCP\IDateTimeFormatter;
16
use Psr\Log\LoggerInterface;
17
18
class VariableService
19
{
20
    private $logger;
21
    private $DatasetMapper;
22
    private $IDateTimeFormatter;
23
24
    public function __construct(
25
        LoggerInterface $logger,
26
        DatasetMapper $DatasetMapper,
27
        IDateTimeFormatter $IDateTimeFormatter
28
    )
29
    {
30
        $this->logger = $logger;
31
        $this->DatasetMapper = $DatasetMapper;
32
        $this->IDateTimeFormatter = $IDateTimeFormatter;
33
    }
34
35
    /**
36
     * replace %*% text variables in thresholds
37
     *
38
     * @param array $thresholds
39
     * @return array
40
     */
41
    public function replaceThresholdsVariables($thresholds)
42
    {
43
        foreach ($thresholds as &$threshold) {
44
            $fields = ['dimension1', 'dimension2'];
45
            foreach ($fields as $field) {
46
                isset($threshold[$field]) ? $name = $threshold[$field] : $name = '';
47
                $parsed = $this->parseFilter($name);
48
                if (!$parsed) break;
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed of type array<string,integer|mixed|string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
49
                $threshold[$field] = $parsed['6$startDate'];
50
            }
51
        }
52
        return $thresholds;
53
    }
54
55
    /**
56
     * replace %*% text variables in name and subheader
57
     *
58
     * @param array $datasetMetadata
59
     * @return array
60
     */
61
    public function replaceTextVariables($datasetMetadata)
62
    {
63
        $fields = ['name', 'subheader'];
64
        foreach ($fields as $field) {
65
            isset($datasetMetadata[$field]) ? $name = $datasetMetadata[$field] : $name = '';
66
67
            preg_match_all("/%.*?%/", $name, $matches);
68
            if (count($matches[0]) > 0) {
69
                foreach ($matches[0] as $match) {
70
                    $replace = null;
71
                    if ($match === '%currentDate%') {
72
                        $replace = $this->IDateTimeFormatter->formatDate(time(), 'short');
73
                    } elseif ($match === '%currentTime%') {
74
                        $replace = $this->IDateTimeFormatter->formatTime(time(), 'short');
75
                    } elseif ($match === '%now%') {
76
                        $replace = time();
77
                    } elseif ($match === '%lastUpdateDate%') {
78
                        $timestamp = $this->DatasetMapper->getLastUpdate($datasetMetadata['dataset']);
79
                        $replace = $this->IDateTimeFormatter->formatDate($timestamp, 'short');
80
                    } elseif ($match === '%lastUpdateTime%') {
81
                        $timestamp = $this->DatasetMapper->getLastUpdate($datasetMetadata['dataset']);
82
                        $replace = $this->IDateTimeFormatter->formatTime($timestamp, 'short');
83
                    } elseif ($match === '%owner%') {
84
                        $owner = $this->DatasetMapper->getOwner($datasetMetadata['dataset']);
85
                        $replace = $owner;
86
                    }
87
                    if ($replace !== null) {
88
                        $datasetMetadata[$field] = preg_replace('/' . $match . '/', $replace, $datasetMetadata[$field]);
89
                    }
90
                }
91
            }
92
        }
93
        return $datasetMetadata;
94
    }
95
96
    /**
97
     * replace variables in single field
98
     * used in: API
99
     *
100
     * @param $field
101
     * @return array
102
     */
103
    public function replaceTextVariablesSingle($field)
104
    {
105
        if ($field !== null) {
106
            preg_match_all("/%.*?%/", $field, $matches);
107
            if (count($matches[0]) > 0) {
108
                foreach ($matches[0] as $match) {
109
                    $replace = null;
110
                    if ($match === '%currentDate%') {
111
                        $replace = $this->IDateTimeFormatter->formatDate(time(), 'short');
112
                    } elseif ($match === '%currentTime%') {
113
                        $replace = $this->IDateTimeFormatter->formatTime(time(), 'short');
114
                    } elseif ($match === '%now%') {
115
                        $replace = time();
116
                    }
117
                    if ($replace !== null) {
118
                        $field = preg_replace('/' . $match . '/', $replace, $field);
119
                    }
120
                }
121
            }
122
        }
123
        return $field;
124
    }
125
126
    /**
127
     * replace variables in single field
128
     * used in: API
129
     *
130
     * @param $columns
131
     * @return string
132
     */
133
    public function replaceDatasourceColumns($columns)
134
    {
135
        $parsed = $this->parseFilter($columns);
136
        $format = $this->parseFormat($columns);
137
        if (!$parsed) return $columns;
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed of type array<string,integer|mixed|string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
introduced by
$parsed is a non-empty array, thus ! $parsed is always false.
Loading history...
138
        return date($format, $parsed['value']);
139
    }
140
141
        /**
142
     * replace variables in filters and apply format
143
     *
144
     * @param $reportMetadata
145
     * @return array
146
     */
147
    public function replaceFilterVariables($reportMetadata)
148
    {
149
        if ($reportMetadata['filteroptions'] !== null) {
150
            $filteroptions = json_decode($reportMetadata['filteroptions'], true);
151
            if (isset($filteroptions['filter'])) {
152
                foreach ($filteroptions['filter'] as $key => $value) {
153
                    $parsed = $this->parseFilter($value['value']);
154
                    $format = $this->parseFormat($value['value']);
155
156
                    if (!$parsed) break;
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed of type array<string,integer|mixed|string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
157
158
                    // if a parser is selected in the chart options, it should also be valid here automatically
159
                    if (isset($reportMetadata['chartoptions'])) {
160
                        $chartOptions = json_decode($reportMetadata['chartoptions'], true);
161
                        if(isset($chartOptions['scales']['xAxes']['time']['parser'])) {
162
                            $format = $chartOptions['scales']['xAxes']['time']['parser'];
163
                        }
164
                    }
165
166
                    // translate commonly known X timestamp format to U for php
167
                    if ($format === 'X') $format = 'U';
168
169
                    $filteroptions['filter'][$key]['value'] = date($format, $parsed['value']);
170
                    //$filteroptions['filter'][$key]['option'] = $parsed['option'];
171
                }
172
            }
173
            $reportMetadata['filteroptions'] = json_encode($filteroptions);
174
        }
175
        return $reportMetadata;
176
    }
177
178
    /**
179
     * parsing of %*% variables
180
     *
181
     * @param $filter
182
     * @return array|bool
183
     */
184
    private function parseFilter($filter) {
185
        preg_match_all("/(?<=%).*(?=%)/", $filter, $matches);
186
        if (count($matches[0]) > 0) {
187
            $filter = $matches[0][0];
188
            preg_match('/(last|next|current|to|yester)?/', $filter, $directionMatch); // direction
189
            preg_match('/[0-9]+/', $filter, $offsetMatch); // how much
190
            preg_match('/(day|days|week|weeks|month|months|year|years)$/', $filter, $unitMatch); // unit
191
192
            if (!$directionMatch[0] || !$unitMatch[0]) {
193
                // no known text variables found
194
                return false;
195
            }
196
197
            // if no offset is specified, apply 1 as default
198
            !isset($offsetMatch[0]) ? $offset = 1: $offset = $offsetMatch[0];
199
200
            // remove "s" to unify e.g. weeks => week
201
            $unit = rtrim($unitMatch[0], 's');
202
203
            if ($directionMatch[0] === "last" || $directionMatch[0] === "yester") {
204
                // go back
205
                $direction = '-';
206
            } elseif ($directionMatch[0] === "next") {
207
                // go forward
208
                $direction = '+';
209
            } else {
210
                // current
211
                $direction = '+';
212
                $offset = 0;
213
            }
214
215
            // create a usable string for php like "+3 days"
216
            $timeString = $direction . $offset . ' ' . $unit;
217
            // get a timestamp of the target date
218
            $baseDate = strtotime($timeString);
219
220
            // get the correct format depending of the unit. e.g. first day of the month in case unit is "month"
221
            if ($unit === 'day') {
222
                $startString = 'today';
223
            } else {
224
                $startString = 'first day of this ' . $unit;
225
            }
226
            $startTS = strtotime($startString, $baseDate);
227
            $start = date("Y-m-d", $startTS);
228
229
            $return = [
230
                'value' => $startTS,
231
                'option' => 'GT',
232
                '1$filter' => $filter,
233
                '2$timestring' => $timeString,
234
                '3$target' => $baseDate,
235
                '4$target_clean' => date("Y-m-d", $baseDate),
236
                '5$startString' => $startString,
237
                '6$startDate' => $start,
238
                '7$startTS' => $startTS,
239
           ];
240
            //$this->logger->debug('parseFilter: '. json_encode($return));
241
        } else {
242
            $return = false;
243
        }
244
        return $return;
245
    }
246
247
    /**
248
     * parsing of ( ) format instructions
249
     *
250
     * @param $filter
251
     * @return string
252
     */
253
    private function parseFormat($filter) {
254
        preg_match_all("/(?<=\().*(?=\))/", $filter, $matches);
255
        if (count($matches[0]) > 0) {
256
            return $matches[0][0];
257
        } else {
258
            return 'Y-m-d H:m:s';
259
        }
260
    }
261
}