Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 130 |
|
0.00% |
0 / 18 |
CRAP | |
0.00% |
0 / 1 |
ImportCsv | |
0.00% |
0 / 130 |
|
0.00% |
0 / 18 |
1892 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
validateRunId | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
42 | |||
getUser | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setUser | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
importFromFile | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
30 | |||
importFromArray | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
132 | |||
isValidQId | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
getInputHash | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
addValidatedResult | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
getCsvColumnHeader | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
processInput | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
deleteRun | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getWarnings | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isOverwrite | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setOverwrite | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getResults | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRunId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | use MediaWiki\MediaWikiServices; |
4 | |
5 | class ImportCsv { |
6 | |
7 | /** |
8 | * @var array |
9 | */ |
10 | private static $columnHeaders = [ 'queryId', 'formulaId' ]; |
11 | /** |
12 | * @var bool|int|string |
13 | */ |
14 | private $runId = false; |
15 | /** |
16 | * @var string[] |
17 | */ |
18 | private $warnings = []; |
19 | /** |
20 | * @var array[] |
21 | */ |
22 | private $results = []; |
23 | /** |
24 | * @var bool[] Array mapping numeric qId values to either true (exists) or false (doesn't exist) |
25 | */ |
26 | private $validQIds = []; |
27 | /** |
28 | * @var bool |
29 | */ |
30 | private $overwrite = false; |
31 | /** |
32 | * @var User |
33 | */ |
34 | private $user; |
35 | |
36 | /** |
37 | * @param User $user |
38 | */ |
39 | function __construct( User $user ) { |
40 | $this->user = $user; |
41 | } |
42 | |
43 | /** |
44 | * @param resource $csvFile |
45 | * @param int|null $runId |
46 | * @param bool $overwrite |
47 | * |
48 | * @return string|true|null |
49 | */ |
50 | public function execute( $csvFile, $runId = null, $overwrite = false ) { |
51 | $this->overwrite = $overwrite; |
52 | $runId = $this->validateRunId( $runId ); |
53 | if ( $runId !== false ) { |
54 | $success = $this->importFromFile( $csvFile ); |
55 | if ( $success == true ) { |
56 | return $this->processInput(); |
57 | } |
58 | } else { |
59 | return "Error: Invalid runId."; |
60 | } |
61 | } |
62 | |
63 | /** |
64 | * @param string $run |
65 | * @return bool|int|string |
66 | */ |
67 | function validateRunId( $run ) { |
68 | if ( $run == '' ) { |
69 | return date( 'Y-m-d H:i:s (e)' ); |
70 | } |
71 | $dbw = MediaWikiServices::getInstance() |
72 | ->getConnectionProvider() |
73 | ->getPrimaryDatabase(); |
74 | $uID = $this->getUser()->getId(); |
75 | if ( is_int( $run ) ) { |
76 | $runId = $dbw->selectField( 'math_wmc_runs', 'runId', |
77 | [ 'isDraft' => true, 'userID' => $uID, 'runId' => $run ] ); |
78 | } else { |
79 | $runId = $dbw->selectField( 'math_wmc_runs', 'runId', |
80 | [ 'isDraft' => true, 'userID' => $uID, 'runName' => $run ] ); |
81 | } |
82 | if ( !$runId ) { |
83 | $exists = $dbw->selectField( 'math_wmc_runs', 'runId', |
84 | [ 'userID' => $uID, 'runName' => $run ] ); |
85 | if ( !$exists ) { |
86 | $success = $dbw->insert( 'math_wmc_runs', |
87 | [ 'isDraft' => true, 'userID' => $uID, 'runName' => $run ] ); |
88 | if ( $success ) { |
89 | $this->runId = $dbw->insertId(); |
90 | $this->warnings[] = wfMessage( 'math-wmc-RunAdded', $run, $this->runId )->text(); |
91 | } else { |
92 | $this->runId = false; |
93 | $this->warnings[] = wfMessage( 'math-wmc-RunAddError', $run )->text(); |
94 | } |
95 | } else { |
96 | $this->warnings[] = wfMessage( 'math-wmc-RunAddExist', $run, $exists )->text(); |
97 | $this->runId = false; |
98 | } |
99 | } else { |
100 | $this->runId = $runId; |
101 | } |
102 | return $this->runId; |
103 | } |
104 | |
105 | /** |
106 | * @return User |
107 | */ |
108 | public function getUser() { |
109 | return $this->user; |
110 | } |
111 | |
112 | /** |
113 | * @param User $user |
114 | */ |
115 | public function setUser( $user ) { |
116 | $this->user = $user; |
117 | } |
118 | |
119 | /** |
120 | * @param resource|null $csv_file |
121 | * @return null |
122 | * @throws Exception |
123 | */ |
124 | public function importFromFile( $csv_file ) { |
125 | if ( $csv_file === null ) { |
126 | return wfMessage( 'emptyfile' )->text(); |
127 | } |
128 | |
129 | $table = []; |
130 | |
131 | $line = fgetcsv( $csv_file ); |
132 | if ( $line === null ) { |
133 | throw new Exception( "Problem processing the csv file." ); |
134 | } |
135 | while ( $line !== false ) { |
136 | array_push( $table, $line ); |
137 | $line = fgetcsv( $csv_file ); |
138 | } |
139 | fclose( $csv_file ); |
140 | |
141 | // Get rid of the "byte order mark", if it's there - this is |
142 | // a three-character string sometimes put at the beginning |
143 | // of files to indicate its encoding. |
144 | // Code copied from: |
145 | // http://www.dotvoid.com/2010/04/detecting-utf-bom-byte-order-mark/ |
146 | $byteOrderMark = pack( "CCC", 0xef, 0xbb, 0xbf ); |
147 | if ( strncmp( $table[0][0], $byteOrderMark, 3 ) == 0 ) { |
148 | $table[0][0] = substr( $table[0][0], 3 ); |
149 | // If there were quotation marks around this value, |
150 | // they didn't get removed, so remove them now. |
151 | $table[0][0] = trim( $table[0][0], '"' ); |
152 | } |
153 | return $this->importFromArray( $table ); |
154 | } |
155 | |
156 | /** |
157 | * @param array $table |
158 | * @return null|string |
159 | */ |
160 | public function importFromArray( $table ) { |
161 | global $wgMathWmcMaxResults; |
162 | |
163 | // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName |
164 | define( 'ImportPattern', '/math\.(\d+)\.(\d+)/' ); |
165 | |
166 | // check header line |
167 | $uploadedHeaders = $table[0]; |
168 | if ( $uploadedHeaders != self::$columnHeaders ) { |
169 | return wfMessage( 'math-wmc-bad-header' )->text(); |
170 | } |
171 | $rank = 0; |
172 | $lastQueryID = 0; |
173 | foreach ( $table as $i => $line ) { |
174 | if ( $i == 0 ) { |
175 | continue; |
176 | } |
177 | $pId = false; |
178 | $eId = false; |
179 | $fHash = false; |
180 | $qId = trim( $line[0] ); |
181 | $fId = trim( $line[1] ); |
182 | $qValid = $this->isValidQId( $qId ); |
183 | if ( $qValid == false ) { |
184 | $this->warnings[] = wfMessage( 'math-wmc-wrong-query-reference', $i, $qId )->text(); |
185 | } |
186 | if ( preg_match( ImportPattern, $fId, $m ) ) { |
187 | $pId = (int)$m[1]; |
188 | $eId = (int)$m[2]; |
189 | $fHash = $this->getInputHash( $pId, $eId ); |
190 | if ( $fHash == false ) { |
191 | $this->warnings[] = wfMessage( 'math-wmc-wrong-formula-reference', $i, $pId, $eId )->text(); |
192 | } |
193 | } else { |
194 | $this->warnings[] = wfMessage( 'math-wmc-malformed-formula-reference', $i, $fId, |
195 | ImportPattern )->text(); |
196 | } |
197 | if ( $qValid === true && $fHash !== false ) { |
198 | // a valid result has been submitted |
199 | if ( $qId == $lastQueryID ) { |
200 | $rank++; |
201 | } else { |
202 | $lastQueryID = $qId; |
203 | $rank = 1; |
204 | } |
205 | if ( $rank <= $wgMathWmcMaxResults ) { |
206 | $this->addValidatedResult( $qId, $pId, $eId, $fHash, $rank ); |
207 | } else { |
208 | $this->warnings[] = wfMessage( 'math-wmc-too-many-results', $i, $qId, $fId, $rank, |
209 | $wgMathWmcMaxResults )->text(); |
210 | } |
211 | } |
212 | } |
213 | return true; |
214 | } |
215 | |
216 | /** |
217 | * @param int $qId |
218 | * @return bool |
219 | */ |
220 | private function isValidQId( $qId ) { |
221 | if ( array_key_exists( $qId, $this->validQIds ) ) { |
222 | return $this->validQIds[$qId]; |
223 | } |
224 | $dbr = MediaWikiServices::getInstance() |
225 | ->getConnectionProvider() |
226 | ->getReplicaDatabase(); |
227 | if ( $dbr->selectField( 'math_wmc_ref', 'qId', [ 'qId' => $qId ] ) ) { |
228 | $this->validQIds[$qId] = true; |
229 | return true; |
230 | } else { |
231 | $this->validQIds[$qId] = false; |
232 | return false; |
233 | } |
234 | } |
235 | |
236 | /** |
237 | * @param int $pId |
238 | * @param string $eId |
239 | * @return string|false |
240 | */ |
241 | private function getInputHash( $pId, $eId ) { |
242 | $dbr = MediaWikiServices::getInstance() |
243 | ->getConnectionProvider() |
244 | ->getReplicaDatabase(); |
245 | return $dbr->selectField( 'mathindex', 'mathindex_inputhash', |
246 | [ 'mathindex_revision_id' => $pId, 'mathindex_anchor' => $eId ] ); |
247 | } |
248 | |
249 | /** |
250 | * @param int $qId |
251 | * @param int $pId |
252 | * @param string $eId |
253 | * @param string $fHash |
254 | * @param int $rank |
255 | */ |
256 | private function addValidatedResult( $qId, $pId, $eId, $fHash, $rank ) { |
257 | $this->results[] = [ |
258 | 'runId' => $this->runId, |
259 | 'qId' => $qId, |
260 | 'oldId' => $pId, |
261 | 'fId' => $eId, |
262 | 'rank' => $rank, |
263 | 'math_inputhash' => $fHash |
264 | ]; |
265 | } |
266 | |
267 | /** |
268 | * @return string |
269 | */ |
270 | public static function getCsvColumnHeader() { |
271 | return implode( ',', self::$columnHeaders ); |
272 | } |
273 | |
274 | /** |
275 | * @return true |
276 | */ |
277 | function processInput() { |
278 | $this->deleteRun( $this->runId ); |
279 | $dbw = MediaWikiServices::getInstance() |
280 | ->getConnectionProvider() |
281 | ->getPrimaryDatabase(); |
282 | $dbw->begin( __METHOD__ ); |
283 | foreach ( $this->results as $result ) { |
284 | $dbw->insert( 'math_wmc_results', $result ); |
285 | } |
286 | $dbw->commit( __METHOD__ ); |
287 | return true; |
288 | } |
289 | |
290 | /** |
291 | * @param string $runID |
292 | */ |
293 | public function deleteRun( $runID ) { |
294 | if ( $this->overwrite ) { |
295 | $dbw = MediaWikiServices::getInstance() |
296 | ->getConnectionProvider() |
297 | ->getPrimaryDatabase(); |
298 | $dbw->delete( 'math_wmc_results', [ 'runId' => $runID ] ); |
299 | } |
300 | } |
301 | |
302 | /** |
303 | * @return string[] |
304 | */ |
305 | public function getWarnings() { |
306 | return $this->warnings; |
307 | } |
308 | |
309 | /** |
310 | * @return bool |
311 | */ |
312 | public function isOverwrite() { |
313 | return $this->overwrite; |
314 | } |
315 | |
316 | /** |
317 | * @param bool $overwrite |
318 | */ |
319 | public function setOverwrite( $overwrite ) { |
320 | $this->overwrite = $overwrite; |
321 | } |
322 | |
323 | /** |
324 | * @return array[] |
325 | */ |
326 | public function getResults() { |
327 | return $this->results; |
328 | } |
329 | |
330 | /** |
331 | * @return int |
332 | */ |
333 | public function getRunId() { |
334 | return $this->runId; |
335 | } |
336 | |
337 | } |