Passed
Push — master ( 782005...8964b9 )
by MusikAnimal
06:51
created

EditSummary::getTotalSummaries()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the EditSummary class.
4
 */
5
6
namespace Xtools;
7
8
use AppBundle\Helper\I18nHelper;
9
use DateTime;
10
11
/**
12
 * An EditSummary provides statistics about a user's edit summary
13
 * usage over time.
14
 */
15
class EditSummary extends Model
16
{
17
    /** @var Project The project. */
18
    protected $project;
19
20
    /** @var User The user. */
21
    protected $user;
22
23
    /** @var I18nHelper For i18n and l10n. */
24
    protected $i18n;
25
26
    /** @var string|int The namespace to target. */
27
    protected $namespace;
28
29
    /** @var Connection $conn Connection to the replica database. */
0 ignored issues
show
Bug introduced by
The type Xtools\Connection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
    protected $conn;
31
32
    /** @var int Number of edits from present to consider as 'recent'. */
33
    protected $numEditsRecent;
34
35
    /**
36
     * Counts of summaries, raw edits, and per-month breakdown.
37
     * Keys are underscored because this also is served in the API.
38
     * @var array
39
     */
40
    protected $data = [
41
        'recent_edits_minor' => 0,
42
        'recent_edits_major' => 0,
43
        'total_edits_minor' => 0,
44
        'total_edits_major' => 0,
45
        'total_edits' => 0,
46
        'recent_summaries_minor' => 0,
47
        'recent_summaries_major' => 0,
48
        'total_summaries_minor' => 0,
49
        'total_summaries_major' => 0,
50
        'total_summaries' => 0,
51
        'month_counts' => [],
52
    ];
53
54
    /**
55
     * EditSummary constructor.
56
     *
57
     * @param Project $project The project we're working with.
58
     * @param User $user The user to process.
59
     * @param string $namespace Namespace ID or 'all' for all namespaces.
60
     * @param int $numEditsRecent Number of edits from present to consider as 'recent'.
61
     */
62 2
    public function __construct(Project $project, User $user, $namespace, $numEditsRecent = 150)
63
    {
64 2
        $this->project = $project;
65 2
        $this->user = $user;
66 2
        $this->namespace = $namespace;
67 2
        $this->numEditsRecent = $numEditsRecent;
68 2
    }
69
70
    /**
71
     * Make the I18nHelper accessible to EditSummary.
72
     * @param I18nHelper $i18n
73
     * @codeCoverageIgnore
74
     */
75
    public function setI18nHelper(I18nHelper $i18n)
76
    {
77
        $this->i18n = $i18n;
78
    }
79
80
    /**
81
     * Get the total number of edits.
82
     * @return int
83
     */
84 1
    public function getTotalEdits()
85
    {
86 1
        return $this->data['total_edits'];
87
    }
88
89
    /**
90
     * Get the total number of minor edits.
91
     * @return int
92
     */
93 1
    public function getTotalEditsMinor()
94
    {
95 1
        return $this->data['total_edits_minor'];
96
    }
97
98
    /**
99
     * Get the total number of major (non-minor) edits.
100
     * @return int
101
     */
102 1
    public function getTotalEditsMajor()
103
    {
104 1
        return $this->data['total_edits_major'];
105
    }
106
107
    /**
108
     * Get the total number of recent minor edits.
109
     * @return int
110
     */
111 1
    public function getRecentEditsMinor()
112
    {
113 1
        return $this->data['recent_edits_minor'];
114
    }
115
116
    /**
117
     * Get the total number of recent major (non-minor) edits.
118
     * @return int
119
     */
120 1
    public function getRecentEditsMajor()
121
    {
122 1
        return $this->data['recent_edits_major'];
123
    }
124
125
    /**
126
     * Get the total number of edits with summaries.
127
     * @return int
128
     */
129 1
    public function getTotalSummaries()
130
    {
131 1
        return $this->data['total_summaries'];
132
    }
133
134
    /**
135
     * Get the total number of minor edits with summaries.
136
     * @return int
137
     */
138 1
    public function getTotalSummariesMinor()
139
    {
140 1
        return $this->data['total_summaries_minor'];
141
    }
142
143
    /**
144
     * Get the total number of major (non-minor) edits with summaries.
145
     * @return int
146
     */
147 1
    public function getTotalSummariesMajor()
148
    {
149 1
        return $this->data['total_summaries_major'];
150
    }
151
152
    /**
153
     * Get the total number of recent minor edits with with summaries.
154
     * @return int
155
     */
156 1
    public function getRecentSummariesMinor()
157
    {
158 1
        return $this->data['recent_summaries_minor'];
159
    }
160
161
    /**
162
     * Get the total number of recent major (non-minor) edits with with summaries.
163
     * @return int
164
     */
165 1
    public function getRecentSummariesMajor()
166
    {
167 1
        return $this->data['recent_summaries_major'];
168
    }
169
170
    /**
171
     * Get the month counts.
172
     * @return array Months as 'YYYY-MM' as the keys,
173
     *   with key 'total' and 'summaries' as the values.
174
     */
175 1
    public function getMonthCounts()
176
    {
177 1
        return $this->data['month_counts'];
178
    }
179
180
    /**
181
     * Get the whole blob of counts.
182
     * @return array Counts of summaries, raw edits, and per-month breakdown.
183
     * @codeCoverageIgnore
184
     */
185
    public function getData()
186
    {
187
        return $this->data;
188
    }
189
190
    /**
191
     * Fetch the data from the database, process, and put in memory.
192
     * @codeCoverageIgnore
193
     */
194
    public function prepareData()
195
    {
196
        // Do our database work in the Repository, passing in reference
197
        // to $this->processRow so we can do post-processing here.
198
        $ret = $this->getRepository()->prepareData(
199
            $this->project,
200
            $this->user,
201
            $this->namespace,
202
            [$this, 'processRow']
203
        );
204
205
        // We want to keep all the default zero values if there are no contributions.
206
        if (count($ret) > 0) {
207
            $this->data = $ret;
208
        }
209
210
        return $ret;
211
    }
212
213
    /**
214
     * Process a single row from the database, updating class properties with counts.
215
     * @param string[] $row As retrieved from the revision table.
216
     * @todo Somehow allow this to be private and still be accessible in the Repository.
217
     */
218 1
    public function processRow($row)
219
    {
220
        // Extract the date out of the date field
221 1
        $timestamp = DateTime::createFromFormat('YmdHis', $row['rev_timestamp']);
222
223 1
        $monthKey = $this->i18n->dateFormat($timestamp, 'yyyy-MM');
0 ignored issues
show
Bug introduced by
It seems like $timestamp can also be of type false; however, parameter $datetime of AppBundle\Helper\I18nHelper::dateFormat() does only seem to accept integer|string|DateTime, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

223
        $monthKey = $this->i18n->dateFormat(/** @scrutinizer ignore-type */ $timestamp, 'yyyy-MM');
Loading history...
224
225
        // Grand total for number of edits
226 1
        $this->data['total_edits']++;
227
228
        // Update total edit count for this month.
229 1
        $this->updateMonthCounts($monthKey, 'total');
230
231
        // Total edit summaries
232 1
        if ($this->hasSummary($row)) {
233 1
            $this->data['total_summaries']++;
234
235
            // Update summary count for this month.
236 1
            $this->updateMonthCounts($monthKey, 'summaries');
237
        }
238
239 1
        if ($this->isMinor($row)) {
240 1
            $this->updateMajorMinorCounts($row, 'minor');
241
        } else {
242 1
            $this->updateMajorMinorCounts($row, 'major');
243
        }
244
245 1
        return $this->data;
246
    }
247
248
    /**
249
     * Given the row in `revision`, update minor counts.
250
     * @param string[] $row As retrieved from the revision table.
251
     * @param string $type Either 'minor' or 'major'.
252
     * @codeCoverageIgnore
253
     */
254
    private function updateMajorMinorCounts($row, $type)
255
    {
256
        $this->data['total_edits_'.$type]++;
257
258
        $hasSummary = $this->hasSummary($row);
259
        $isRecent = $this->data['recent_edits_'.$type] < $this->numEditsRecent;
260
261
        if ($hasSummary) {
262
            $this->data['total_summaries_'.$type]++;
263
        }
264
265
        // Update recent edits counts.
266
        if ($isRecent) {
267
            $this->data['recent_edits_'.$type]++;
268
269
            if ($hasSummary) {
270
                $this->data['recent_summaries_'.$type]++;
271
            }
272
        }
273
    }
274
275
    /**
276
     * Was the given row in `revision` marked as a minor edit?
277
     * @param  string[] $row As retrieved from the revision table.
278
     * @return boolean
279
     */
280 1
    private function isMinor($row)
281
    {
282 1
        return (int)$row['rev_minor_edit'] === 1;
283
    }
284
285
    /**
286
     * Taking into account automated edit summaries, does the given
287
     * row in `revision` have a user-supplied edit summary?
288
     * @param  string[] $row As retrieved from the revision table.
289
     * @return boolean
290
     */
291 2
    private function hasSummary($row)
292
    {
293 2
        $summary = preg_replace("/^\/\* (.*?) \*\/\s*/", '', $row['rev_comment']);
294 2
        return $summary !== '';
295
    }
296
297
    /**
298
     * Check and see if the month is set for given $monthKey and $type.
299
     * If it is, increment it, otherwise set it to 1.
300
     * @param  string $monthKey In the form 'YYYY-MM'.
301
     * @param  string $type     Either 'total' or 'summaries'.
302
     * @codeCoverageIgnore
303
     */
304
    private function updateMonthCounts($monthKey, $type)
305
    {
306
        if (isset($this->data['month_counts'][$monthKey][$type])) {
307
            $this->data['month_counts'][$monthKey][$type]++;
308
        } else {
309
            $this->data['month_counts'][$monthKey][$type] = 1;
310
        }
311
    }
312
}
313