Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 190
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialUploadResult
0.00% covered (danger)
0.00%
0 / 190
0.00% covered (danger)
0.00%
0 / 12
1056
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 formatErrors
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
12
 printRunSelector
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
12
 runSelectorFilter
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 runValidatorFilter
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 runFileCheck
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 processInput
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 printResultRow
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 displayFeedback
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
20
 displayFormulaFeedback
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
20
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3use MediaWiki\HTMLForm\Field\HTMLTextField;
4use MediaWiki\HTMLForm\HTMLForm;
5use MediaWiki\MediaWikiServices;
6use MediaWiki\SpecialPage\SpecialPage;
7use MediaWiki\Title\Title;
8
9/**
10 * Lets the user import a CSV file with the results
11 *
12 * @author Moritz Schubotz
13 * @author Yaron Koren (This class is based on DT_ImportCSV from the DataTransfer extension)
14 */
15class SpecialUploadResult extends SpecialPage {
16
17    /** @var ImportCsv */
18    private $importer;
19    /** @var bool|int|string */
20    protected $runId = false;
21
22    /**
23     * @param string $name
24     */
25    public function __construct( $name = 'MathUpload' ) {
26        $listed = (bool)$this->getConfig()->get( 'MathWmcServer' );
27        parent::__construct( $name, 'mathwmcsubmit', $listed );
28    }
29
30    /**
31     * @param string[] $errors
32     *
33     * @return string
34     */
35    private static function formatErrors( $errors ) {
36        return wfMessage( 'math-wmc-Warnings' )->text() . "<br />" . implode( "<br />", $errors );
37    }
38
39    /**
40     * @param null|string $query
41     *
42     * @throws PermissionsError
43     */
44    function execute( $query ) {
45        $this->setHeaders();
46        if ( !$this->getUser()->isAllowed( 'mathwmcsubmit' ) ||
47            !$this->getConfig()->get( 'MathUploadEnabled' )
48        ) {
49            throw new PermissionsError( 'mathwmcsubmit' );
50        }
51
52        $this->getOutput()->addWikiTextAsInterface( $this->msg( 'math-wmc-Introduction' )->text() );
53        $this->importer = new ImportCsv( $this->getUser() );
54        $formDescriptor = $this->printRunSelector();
55        $formDescriptor['File'] = [
56            'label-message' => 'math-wmc-FileLabel',
57            'help-message' => 'math-wmc-FileHelp',
58            'class' => HTMLTextField::class,
59            'type' => 'file',
60            'required' => true,
61            'validation-callback' => [ $this, 'runFileCheck' ],
62        ];
63        $formDescriptor['displayFormulae'] = [
64            'label-message' => 'math-wmc-display-formulae-label',
65            'help-message' => 'math-wmc-display-formulae-help',
66            'type' => 'check',
67        ];
68        $formDescriptor['attachResults'] = [
69            'label-message' => 'math-wmc-attach-results-label',
70            'help-message' => 'math-wmc-attach-results-help',
71            'type' => 'check',
72        ];
73        $htmlForm = new HTMLForm( $formDescriptor, $this->getContext() );
74        $htmlForm->setSubmitCallback( [ $this, 'processInput' ] );
75        $htmlForm->show();
76    }
77
78    /**
79     * @param string $type
80     *
81     * @return array
82     */
83    protected function printRunSelector( $type = 'selectorother' ) {
84        // If $wgMathWmcServer is unset there's no math_wmc_runs table to query
85        if ( !$this->getConfig()->get( 'MathWmcServer' ) ) {
86            return [];
87        }
88
89        $dbr = MediaWikiServices::getInstance()
90            ->getConnectionProvider()
91            ->getReplicaDatabase();
92        $formFields = [];
93        $options = [];
94        $uID = $this->getUser()->getId();
95        $res = $dbr->select( 'math_wmc_runs', [ 'runName', 'runId' ],
96            [ 'isDraft' => true, 'userID' => $uID ] );
97        foreach ( $res as $row ) {
98            $options[ $row->runName . " (" . $row->runId . ")" ] = $row->runId;
99        }
100        // Probably we want to add more fields in the future
101        $formFields['run'] = [
102            'type' => $type,
103            'label-message' => 'math-wmc-SelectRun',
104            'options' => $options,
105            'required' => true,
106            'help-message' => 'math-wmc-SelectRunHelp',
107            'filter-callback' => [ $this, 'runSelectorFilter' ],
108            'validation-callback' => [ $this, 'runValidatorFilter' ],
109            // 'section' => 'math-wmc-SectionRun'
110        ];
111        return $formFields;
112    }
113
114    /**
115     * @param string $run
116     *
117     * @return bool|int|string
118     */
119    function runSelectorFilter( $run ) {
120        if ( $run == '' ) {
121            return date( 'Y-m-d H:i:s (e)' );
122        }
123        $this->runId = $this->importer->validateRunId( $run );
124        $warnings = $this->importer->getWarnings();
125        if ( $warnings ) {
126            echo "bad wqarni";
127            foreach ( $warnings as $warning ) {
128                $this->getOutput()->addWikiTextAsInterface( $warning );
129            }
130        }
131        return $run;
132    }
133
134    /**
135     * @return bool|string
136     */
137    function runValidatorFilter() {
138        $dbr = MediaWikiServices::getInstance()
139            ->getConnectionProvider()
140            ->getReplicaDatabase();
141        $uID = $this->getUser()->getId();
142        $res = $dbr->selectField( 'math_wmc_runs', 'runName',
143            [ 'isDraft' => true, 'userID' => $uID, 'runId' => $this->runId ] );
144        if ( !$res ) {
145            return $this->msg( 'math-wmc-SelectRunHelp' )->text();
146        } else {
147            return true;
148        }
149    }
150
151    /**
152     * @return bool|null|string
153     */
154    function runFileCheck() {
155        $out = $this->getOutput();
156
157        $uploadResult = ImportStreamSource::newFromUpload( 'wpFile' );
158
159        if ( !$uploadResult->isOK() ) {
160            return $uploadResult->getWikiText();
161        }
162
163        /** @var ImportStreamSource $source */
164        $source = $uploadResult->value;
165
166        $out->addWikiMsg( 'math-wmc-importing' );
167        // FIXME: ImportStreamSource::$mHandle is private!
168        $error_msg = $this->importer->importFromFile( $source->mHandle );
169
170        if ( $error_msg !== null ) {
171            return $error_msg;
172        }
173        if ( count( $this->importer->getWarnings() ) ) {
174            $out->addWikiTextAsInterface( self::formatErrors( $this->importer->getWarnings() ) );
175        }
176
177        return true;
178    }
179
180    /**
181     * @return bool
182     */
183    function processInput() {
184        $this->getOutput()->addWikiMsg( "math-wmc-SubmissionSuccess" );
185        $this->importer->setOverwrite( !$this->getRequest()->getBool( "wpattachResults" ) );
186        $this->importer->processInput();
187        // TODO: Find adequate API call
188        $this->getOutput()->addHTML( '<table border="1" style="width:100%">
189  <tr>
190    <th>queryId</th>
191    <th>formulaId</th>
192    <th>rank</th>
193    <th>rendering</th>
194  </tr>' );
195        foreach ( $this->importer->getResults() as $result ) {
196            $this->printResultRow( $result );
197        }
198        $this->getOutput()->addHTML( '</table>' );
199        $this->displayFeedback();
200        return true;
201    }
202
203    /**
204     * @param array $row
205     */
206    private function printResultRow( $row ) {
207        $md5 = MathObject::hash2md5( $row['math_inputhash'] );
208        if ( $this->getRequest()->getBool( "wpdisplayFormulae" ) ) {
209            $this->getOutput()->addModuleStyles( [ 'ext.math.styles' ] );
210            $renderer = MediaWikiServices::getInstance()
211                ->get( 'Math.RendererFactory' )
212                ->getFromHash( $md5 );
213            if ( $renderer->render() ) {
214                $renderedMath = $renderer->getHtmlOutput();
215            } else {
216                $renderedMath = $md5;
217            }
218        } else {
219            $renderedMath = $md5;
220        }
221        $formulaId = MathSearchHooks::generateMathAnchorString( $row['oldId'], $row['fId'] );
222        $revisionRecord = MediaWikiServices::getInstance()
223            ->getRevisionLookup()
224            ->getRevisionById( $row['oldId'] );
225        $title = Title::newFromLinkTarget( $revisionRecord->getPageAsLinkTarget() );
226        $link = $title->getLinkURL() . $formulaId;
227        $this->getOutput()->addHTML( "<tr><td>{$row['qId']}</td><td><a href=\"$link\" >$formulaId</a></td>
228            <td>{$row['rank']}</td><td>$renderedMath</td></tr>" );
229    }
230
231    private function displayFeedback() {
232        $runId = $this->runId;
233        $dbr = MediaWikiServices::getInstance()
234            ->getConnectionProvider()
235            ->getReplicaDatabase();
236        $res = $dbr->select(
237            [ 'l' => 'math_wmc_rank_levels', 'r' => 'math_wmc_ref', 'math_wmc_results' ],
238            [
239                'count(DISTINCT `r`.`qId`)  AS `c`',
240                '`l`.`level`                AS `level`'
241            ],
242            [
243                "(`math_wmc_results`.`rank` <= `l`.`level`)",
244                'runId' => $runId,
245                '`math_wmc_results`.`oldId` = `r`.`oldId`',
246                '`math_wmc_results`.`qId` = `r`.`qId`'
247            ],
248            __METHOD__,
249            [
250                'GROUP BY' => '`l`.`level`',
251                'ORDER BY' => 'count(DISTINCT `r`.`qId`) DESC'
252            ]
253        );
254        if ( !$res || $res->numRows() == 0 ) {
255            $this->getOutput()->addWikiTextAsInterface( "Score is 0. Check your submission" );
256            return;
257        } else {
258            $this->getOutput()->addWikiTextAsInterface(
259                "'''Scored in " . $res->numRows() . " evaluation levels'''"
260            );
261        }
262
263        $this->getOutput()->addHTML( '<table border="1" style="width:100%">
264  <tr>
265    <th>number of correct results</th>
266    <th>rank cutoff</th>
267  </tr>' );
268        foreach ( $res as $result ) {
269            $c = $result->c;
270            $l = $result->level;
271            $this->getOutput()->addHTML( "
272  <tr>
273    <td>$c</td>
274    <td>$l</td>
275  </tr>" );
276        }
277        $this->getOutput()->addHTML( '</table>' );
278    }
279
280    private function displayFormulaFeedback() {
281        $runId = $this->runId;
282        $dbr = MediaWikiServices::getInstance()
283            ->getConnectionProvider()
284            ->getReplicaDatabase();
285        $res = $dbr->select(
286            [ 'l' => 'math_wmc_rank_levels', 'r' => 'math_wmc_ref', 'math_wmc_results' ],
287            [
288                'count(DISTINCT `r`.`qId`)  AS `c`',
289                '`l`.`level`                AS `level`'
290            ],
291            [
292                "(`math_wmc_results`.`rank` <= `l`.`level`)",
293                'runId' => $runId,
294                '`math_wmc_results`.`oldId` = `r`.`oldId`',
295                '`math_wmc_results`.`qId` = `r`.`qId`',
296                '`math_wmc_results`.`fId` = `r`.`fId`',
297            ],
298            __METHOD__,
299            [
300                'GROUP BY' => '`l`.`level`',
301                'ORDER BY' => 'count(DISTINCT `r`.`qId`) DESC'
302            ]
303        );
304        if ( !$res || $res->numRows() == 0 ) {
305            $this->getOutput()->addWikiTextAsInterface( "Score is 0. Check your submission" );
306            return;
307        } else {
308            $this->getOutput()->addWikiTextAsInterface(
309                "'''Scored in " . $res->numRows() . " evaluation levels'''"
310            );
311        }
312
313        $this->getOutput()->addHTML( '<table border="1" style="width:100%">
314  <tr>
315    <th>number of correct results</th>
316    <th>rank cutoff</th>
317  </tr>' );
318        foreach ( $res as $result ) {
319            $c = $result->c;
320            $l = $result->level;
321            $this->getOutput()->addHTML( "
322  <tr>
323    <td>$c</td>
324    <td>$l</td>
325  </tr>" );
326        }
327        $this->getOutput()->addHTML( '</table>' );
328    }
329
330    protected function getGroupName() {
331        return 'mathsearch';
332    }
333}