EditSummary::getRecentSummariesMajor()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace App\Model;
6
7
use App\Repository\EditSummaryRepository;
8
use DateTime;
9
10
/**
11
 * An EditSummary provides statistics about a user's edit summary usage over time.
12
 */
13
class EditSummary extends Model
14
{
15
    /** @var int Number of edits from present to consider as 'recent'. */
16
    protected int $numEditsRecent;
17
18
    /**
19
     * Counts of summaries, raw edits, and per-month breakdown.
20
     * Keys are underscored because this also is served in the API.
21
     * @var array
22
     */
23
    protected array $data = [
24
        'recent_edits_minor' => 0,
25
        'recent_edits_major' => 0,
26
        'total_edits_minor' => 0,
27
        'total_edits_major' => 0,
28
        'total_edits' => 0,
29
        'recent_summaries_minor' => 0,
30
        'recent_summaries_major' => 0,
31
        'total_summaries_minor' => 0,
32
        'total_summaries_major' => 0,
33
        'total_summaries' => 0,
34
        'month_counts' => [],
35
    ];
36
37
    /**
38
     * EditSummary constructor.
39
     *
40
     * @param EditSummaryRepository $repository
41
     * @param Project $project The project we're working with.
42
     * @param User $user The user to process.
43
     * @param int|string $namespace Namespace ID or 'all' for all namespaces.
44
     * @param int|false $start Start date as Unix timestamp.
45
     * @param int|false $end End date as Unix timestamp.
46
     * @param int $numEditsRecent Number of edits from present to consider as 'recent'.
47
     */
48
    public function __construct(
49
        EditSummaryRepository $repository,
50
        Project $project,
51
        User $user,
52
        $namespace,
53
        $start = false,
54
        $end = false,
55
        int $numEditsRecent = 150
56
    ) {
57
        $this->repository = $repository;
58
        $this->project = $project;
59
        $this->user = $user;
60
        $this->namespace = $namespace;
61
        $this->start = $start;
62
        $this->end = $end;
63
        $this->numEditsRecent = $numEditsRecent;
64
    }
65
66
    /**
67
     * Get the total number of edits.
68
     * @return int
69
     */
70
    public function getTotalEdits(): int
71
    {
72
        return $this->data['total_edits'];
73
    }
74
75
    /**
76
     * Get the total number of minor edits.
77
     * @return int
78
     */
79
    public function getTotalEditsMinor(): int
80
    {
81
        return $this->data['total_edits_minor'];
82
    }
83
84
    /**
85
     * Get the total number of major (non-minor) edits.
86
     * @return int
87
     */
88
    public function getTotalEditsMajor(): int
89
    {
90
        return $this->data['total_edits_major'];
91
    }
92
93
    /**
94
     * Get the total number of recent minor edits.
95
     * @return int
96
     */
97
    public function getRecentEditsMinor(): int
98
    {
99
        return $this->data['recent_edits_minor'];
100
    }
101
102
    /**
103
     * Get the total number of recent major (non-minor) edits.
104
     * @return int
105
     */
106
    public function getRecentEditsMajor(): int
107
    {
108
        return $this->data['recent_edits_major'];
109
    }
110
111
    /**
112
     * Get the total number of edits with summaries.
113
     * @return int
114
     */
115
    public function getTotalSummaries(): int
116
    {
117
        return $this->data['total_summaries'];
118
    }
119
120
    /**
121
     * Get the total number of minor edits with summaries.
122
     * @return int
123
     */
124
    public function getTotalSummariesMinor(): int
125
    {
126
        return $this->data['total_summaries_minor'];
127
    }
128
129
    /**
130
     * Get the total number of major (non-minor) edits with summaries.
131
     * @return int
132
     */
133
    public function getTotalSummariesMajor(): int
134
    {
135
        return $this->data['total_summaries_major'];
136
    }
137
138
    /**
139
     * Get the total number of recent minor edits with with summaries.
140
     * @return int
141
     */
142
    public function getRecentSummariesMinor(): int
143
    {
144
        return $this->data['recent_summaries_minor'];
145
    }
146
147
    /**
148
     * Get the total number of recent major (non-minor) edits with with summaries.
149
     * @return int
150
     */
151
    public function getRecentSummariesMajor(): int
152
    {
153
        return $this->data['recent_summaries_major'];
154
    }
155
156
    /**
157
     * Get the month counts.
158
     * @return array Months as 'YYYY-MM' as the keys,
159
     *   with key 'total' and 'summaries' as the values.
160
     */
161
    public function getMonthCounts(): array
162
    {
163
        return $this->data['month_counts'];
164
    }
165
166
    /**
167
     * Get the whole blob of counts.
168
     * @return array Counts of summaries, raw edits, and per-month breakdown.
169
     * @codeCoverageIgnore
170
     */
171
    public function getData(): array
172
    {
173
        return $this->data;
174
    }
175
176
    /**
177
     * Fetch the data from the database, process, and put in memory.
178
     * @codeCoverageIgnore
179
     */
180
    public function prepareData(): array
181
    {
182
        // Do our database work in the Repository, passing in reference
183
        // to $this->processRow so we can do post-processing here.
184
        $ret = $this->repository->prepareData(
0 ignored issues
show
Bug introduced by
The method prepareData() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\EditSummaryRepository. ( Ignorable by Annotation )

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

184
        /** @scrutinizer ignore-call */ 
185
        $ret = $this->repository->prepareData(
Loading history...
185
            [$this, 'processRow'],
186
            $this->project,
187
            $this->user,
188
            $this->namespace,
189
            $this->start,
190
            $this->end
191
        );
192
193
        // We want to keep all the default zero values if there are no contributions.
194
        if (count($ret) > 0) {
195
            $this->data = $ret;
196
        }
197
198
        return $ret;
199
    }
200
201
    /**
202
     * Process a single row from the database, updating class properties with counts.
203
     * @param string[] $row As retrieved from the revision table.
204
     * @return string[]
205
     */
206
    public function processRow(array $row): array
207
    {
208
        // Extract the date out of the date field
209
        $timestamp = DateTime::createFromFormat('YmdHis', $row['rev_timestamp']);
210
211
        $monthKey = $timestamp->format('Y-m');
212
213
        // Grand total for number of edits
214
        $this->data['total_edits']++;
215
216
        // Update total edit count for this month.
217
        $this->updateMonthCounts($monthKey, 'total');
218
219
        // Total edit summaries
220
        if ($this->hasSummary($row)) {
221
            $this->data['total_summaries']++;
222
223
            // Update summary count for this month.
224
            $this->updateMonthCounts($monthKey, 'summaries');
225
        }
226
227
        if ($this->isMinor($row)) {
228
            $this->updateMajorMinorCounts($row, 'minor');
229
        } else {
230
            $this->updateMajorMinorCounts($row, 'major');
231
        }
232
233
        return $this->data;
234
    }
235
236
    /**
237
     * Given the row in `revision`, update minor counts.
238
     * @param string[] $row As retrieved from the revision table.
239
     * @param string $type Either 'minor' or 'major'.
240
     * @codeCoverageIgnore
241
     */
242
    private function updateMajorMinorCounts(array $row, string $type): void
243
    {
244
        $this->data['total_edits_'.$type]++;
245
246
        $hasSummary = $this->hasSummary($row);
247
        $isRecent = $this->data['recent_edits_'.$type] < $this->numEditsRecent;
248
249
        if ($hasSummary) {
250
            $this->data['total_summaries_'.$type]++;
251
        }
252
253
        // Update recent edits counts.
254
        if ($isRecent) {
255
            $this->data['recent_edits_'.$type]++;
256
257
            if ($hasSummary) {
258
                $this->data['recent_summaries_'.$type]++;
259
            }
260
        }
261
    }
262
263
    /**
264
     * Was the given row in `revision` marked as a minor edit?
265
     * @param string[] $row As retrieved from the revision table.
266
     * @return boolean
267
     */
268
    private function isMinor(array $row): bool
269
    {
270
        return 1 === (int)$row['rev_minor_edit'];
271
    }
272
273
    /**
274
     * Taking into account automated edit summaries, does the given
275
     * row in `revision` have a user-supplied edit summary?
276
     * @param string[] $row As retrieved from the revision table.
277
     * @return boolean
278
     */
279
    private function hasSummary(array $row): bool
280
    {
281
        $summary = preg_replace("/^\/\* (.*?) \*\/\s*/", '', $row['comment'] ?: '');
282
        return '' !== $summary;
283
    }
284
285
    /**
286
     * Check and see if the month is set for given $monthKey and $type.
287
     * If it is, increment it, otherwise set it to 1.
288
     * @param string $monthKey In the form 'YYYY-MM'.
289
     * @param string $type     Either 'total' or 'summaries'.
290
     * @codeCoverageIgnore
291
     */
292
    private function updateMonthCounts(string $monthKey, string $type): void
293
    {
294
        if (isset($this->data['month_counts'][$monthKey][$type])) {
295
            $this->data['month_counts'][$monthKey][$type]++;
296
        } else {
297
            $this->data['month_counts'][$monthKey][$type] = 1;
298
        }
299
    }
300
}
301