Passed
Push — master ( 9b6f1b...510c7f )
by MusikAnimal
04:53
created

EditSummary::getSqlStatement()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 8
nop 0
dl 0
loc 25
ccs 0
cts 0
cp 0
crap 20
rs 8.5806
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 DateTime;
9
10
/**
11
 * An EditSummary provides statistics about a user's edit summary
12
 * usage over time.
13
 */
14
class EditSummary extends Model
15
{
16
    /** @var Project The project. */
17
    protected $project;
18
19
    /** @var User The user. */
20
    protected $user;
21
22
    /** @var string|int The namespace to target. */
23
    protected $namespace;
24
25
    /** @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...
26
    protected $conn;
27
28
    /** @var int Number of edits from present to consider as 'recent'. */
29
    protected $numEditsRecent;
30
31
    /**
32
     * Counts of summaries, raw edits, and per-month breakdown.
33
     * Keys are underscored because this also is served in the API.
34
     * @var array
35
     */
36
    protected $data = [
37
        'recent_edits_minor' => 0,
38
        'recent_edits_major' => 0,
39
        'total_edits_minor' => 0,
40
        'total_edits_major' => 0,
41
        'total_edits' => 0,
42
        'recent_summaries_minor' => 0,
43
        'recent_summaries_major' => 0,
44
        'total_summaries_minor' => 0,
45
        'total_summaries_major' => 0,
46
        'total_summaries' => 0,
47
        'month_counts' => [],
48
    ];
49
50
    /**
51
     * EditSummary constructor.
52
     *
53
     * @param Project $project The project we're working with.
54
     * @param User $user The user to process.
55
     * @param string $namespace Namespace ID or 'all' for all namespaces.
56
     * @param int $numEditsRecent Number of edits from present to consider as 'recent'.
57
     */
58 2
    public function __construct(Project $project, User $user, $namespace, $numEditsRecent = 150)
59
    {
60 2
        $this->project = $project;
61 2
        $this->user = $user;
62 2
        $this->namespace = $namespace;
63 2
        $this->numEditsRecent = $numEditsRecent;
64 2
    }
65
66
    /**
67
     * Get the total number of edits.
68
     * @return int
69
     */
70 1
    public function getTotalEdits()
71
    {
72 1
        return $this->data['total_edits'];
73
    }
74
75
    /**
76
     * Get the total number of minor edits.
77
     * @return int
78
     */
79 1
    public function getTotalEditsMinor()
80
    {
81 1
        return $this->data['total_edits_minor'];
82
    }
83
84
    /**
85
     * Get the total number of major (non-minor) edits.
86
     * @return int
87
     */
88 1
    public function getTotalEditsMajor()
89
    {
90 1
        return $this->data['total_edits_major'];
91
    }
92
93
    /**
94
     * Get the total number of recent minor edits.
95
     * @return int
96
     */
97 1
    public function getRecentEditsMinor()
98
    {
99 1
        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 1
    public function getRecentEditsMajor()
107
    {
108 1
        return $this->data['recent_edits_major'];
109
    }
110
111
    /**
112
     * Get the total number of edits with summaries.
113
     * @return int
114
     */
115 1
    public function getTotalSummaries()
116
    {
117 1
        return $this->data['total_summaries'];
118
    }
119
120
    /**
121
     * Get the total number of minor edits with summaries.
122
     * @return int
123
     */
124 1
    public function getTotalSummariesMinor()
125
    {
126 1
        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 1
    public function getTotalSummariesMajor()
134
    {
135 1
        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 1
    public function getRecentSummariesMinor()
143
    {
144 1
        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 1
    public function getRecentSummariesMajor()
152
    {
153 1
        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 1
    public function getMonthCounts()
162
    {
163 1
        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()
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()
181
    {
182
        // Do our database work in the Repository, passing in reference
183
        // to $this->processRow so we can do post-processing here.
184
        $this->data = $this->getRepository()->prepareData(
185
            $this->project,
186
            $this->user,
187
            $this->namespace,
188
            [$this, 'processRow']
189
        );
190
    }
191
192
    /**
193
     * Process a single row from the database, updating class properties with counts.
194
     * @param string[] $row As retrieved from the revision table.
195
     * @todo Somehow allow this to be private and still be accessible in the Repository.
196
     */
197 1
    public function processRow($row)
198
    {
199
        // Extract the date out of the date field
200 1
        $timestamp = DateTime::createFromFormat('YmdHis', $row['rev_timestamp']);
201
202 1
        $monthKey = date_format($timestamp, 'Y-m');
203
204
        // Grand total for number of edits
205 1
        $this->data['total_edits']++;
206
207
        // Update total edit count for this month.
208 1
        $this->updateMonthCounts($monthKey, 'total');
209
210
        // Total edit summaries
211 1
        if ($this->hasSummary($row)) {
212 1
            $this->data['total_summaries']++;
213
214
            // Update summary count for this month.
215 1
            $this->updateMonthCounts($monthKey, 'summaries');
216
        }
217
218 1
        if ($this->isMinor($row)) {
219 1
            $this->updateMajorMinorCounts($row, 'minor');
220
        } else {
221 1
            $this->updateMajorMinorCounts($row, 'major');
222
        }
223
224 1
        return $this->data;
225
    }
226
227
    /**
228
     * Given the row in `revision`, update minor counts.
229
     * @param string[] $row As retrieved from the revision table.
230
     * @param string $type Either 'minor' or 'major'.
231
     * @codeCoverageIgnore
232
     */
233
    private function updateMajorMinorCounts($row, $type)
234
    {
235
        $this->data['total_edits_'.$type]++;
236
237
        $hasSummary = $this->hasSummary($row);
238
        $isRecent = $this->data['recent_edits_'.$type] < $this->numEditsRecent;
239
240
        if ($hasSummary) {
241
            $this->data['total_summaries_'.$type]++;
242
        }
243
244
        // Update recent edits counts.
245
        if ($isRecent) {
246
            $this->data['recent_edits_'.$type]++;
247
248
            if ($hasSummary) {
249
                $this->data['recent_summaries_'.$type]++;
250
            }
251
        }
252
    }
253
254
    /**
255
     * Was the given row in `revision` marked as a minor edit?
256
     * @param  string[] $row As retrieved from the revision table.
257
     * @return boolean
258
     */
259 1
    private function isMinor($row)
260
    {
261 1
        return (int)$row['rev_minor_edit'] === 1;
262
    }
263
264
    /**
265
     * Taking into account automated edit summaries, does the given
266
     * row in `revision` have a user-supplied edit summary?
267
     * @param  string[] $row As retrieved from the revision table.
268
     * @return boolean
269
     */
270 2
    private function hasSummary($row)
271
    {
272 2
        $summary = preg_replace("/^\/\* (.*?) \*\/\s*/", '', $row['rev_comment']);
273 2
        return $summary !== '';
274
    }
275
276
    /**
277
     * Check and see if the month is set for given $monthKey and $type.
278
     * If it is, increment it, otherwise set it to 1.
279
     * @param  string $monthKey In the form 'YYYY-MM'.
280
     * @param  string $type     Either 'total' or 'summaries'.
281
     * @codeCoverageIgnore
282
     */
283
    private function updateMonthCounts($monthKey, $type)
284
    {
285
        if (isset($this->data['month_counts'][$monthKey][$type])) {
286
            $this->data['month_counts'][$monthKey][$type]++;
287
        } else {
288
            $this->data['month_counts'][$monthKey][$type] = 1;
289
        }
290
    }
291
}
292