Completed
Push — master ( 81bc9f...cbc9cf )
by Dispositif
02:15
created

EditProcess::sendErrorMessage()   A

Complexity

Conditions 4
Paths 9

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 16
c 1
b 0
f 0
nc 9
nop 1
dl 0
loc 24
rs 9.7333
1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application
4
 * 2019 : Philippe M. <[email protected]>
5
 * For the full copyright and MIT license information, please view the LICENSE file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Application\Examples;
11
12
use App\Application\Bot;
13
use App\Application\Memory;
14
use App\Application\WikiPageAction;
15
use App\Domain\Utils\WikiTextUtil;
16
use App\Infrastructure\DbAdapter;
17
use App\Infrastructure\ServiceFactory;
18
use Mediawiki\DataModel\EditInfo;
19
use Throwable;
20
21
//use App\Application\CLI;
22
23
include __DIR__.'/../myBootstrap.php';
24
25
$process = new EditProcess();
26
$process->run();
27
28
/**
29
 * TODO refac
30
 * Class EditProcess
31
 *
32
 * @package App\Application\Examples
33
 */
34
class EditProcess
35
{
36
    const TASK_NAME        = 'Amélioration bibliographique';
37
    const EDIT_SIGNALEMENT = true;
38
39
    const CITATION_LIMIT         = 150;
40
    const DELAY_BOTFLAG_SECONDS  = 10;
41
    const DELAY_NOBOT_IN_SECONDS = 60;
42
    const ERROR_MSG_TEMPLATE     = __DIR__.'/../templates/message_errors.wiki';
43
44
    private $db;
45
    private $bot;
46
    private $wiki;
47
    private $wikiText;
48
    private $citationSummary;
49
    private $citationVersion = '';
50
    private $errorWarning = [];
51
    private $importantSummary = [];
52
    private $nbRows;
53
54
    // Minor flag on edit
55
    private $minorFlag = true;
56
    // Bot flag on edit
57
    private $botFlag = true;
58
59
    public function __construct()
60
    {
61
        $this->db = new DbAdapter();
62
        $this->bot = new Bot();
63
64
        $this->wiki = ServiceFactory::wikiApi();
65
    }
66
67
    public function run()
68
    {
69
        $memory = new Memory();
70
        while (true) {
71
            echo "\n-------------------------------------\n\n";
72
            echo date("Y-m-d H:i")."\n";
73
            $memory->echoMemory(true);
74
75
            $this->pageProcess();
76
        }
77
    }
78
79
    private function pageProcess()
80
    {
81
        $this->initialize();
82
83
        // get a random queue line
84
        $json = $this->db->getAllRowsToEdit(self::CITATION_LIMIT);
85
        $data = json_decode($json, true);
86
87
        if (empty($data)) {
88
            echo "SKIP : no rows to process\n";
89
            echo "Sleep 1h.";
90
            sleep(60 * 60);
91
92
            // or exit
93
            return false;
94
        }
95
96
        try {
97
            $title = $data[0]['page'];
98
            echo "$title \n";
99
            $page = new WikiPageAction($this->wiki, $title);
100
        } catch (\Exception $e) {
101
            echo "*** WikiPageAction error : $title \n";
102
            sleep(20);
103
104
            return false;
105
        }
106
107
        // TODO : HACK
108
        if (in_array($page->getLastEditor(), [getenv('BOT_NAME'), getenv('BOT_OWNER')])) {
109
            echo "SKIP : édité recemment par bot/dresseur.\n";
110
            $this->db->skipArticle($title);
111
112
            return false;
113
        }
114
        $this->wikiText = $page->getText();
115
116
        if ($this->bot->minutesSinceLastEdit($title) < 15) {
117
            echo "SKIP : édition humaine dans les dernières 15 minutes.\n";
118
119
            return false;
120
        }
121
122
        // Skip AdQ
123
        if (preg_match('#{{ ?En-tête label#i', $this->wikiText) > 0) {
124
            echo "SKIP : AdQ ou BA.\n";
125
            $this->db->skipArticle($title);
126
127
            return false;
128
        }
129
130
        // GET all article lines from db
131
        echo sprintf(">> %s rows to process\n", count($data));
132
133
        // foreach line
134
        $changed = false;
135
        foreach ($data as $dat) {
136
            // hack temporaire pour éviter articles dont CompleteProcess incomplet
137
            if (empty($dat['opti']) || empty($dat['optidate']) || $dat['optidate'] < DbAdapter::OPTI_VALID_DATE) {
138
                echo "SKIP : Complètement incomplet de l'article \n";
139
140
                return false;
141
            }
142
            $success = $this->dataProcess($dat);
143
            $changed = ($success) ? true : $changed;
144
        }
145
        if (!$changed) {
146
            echo "Rien à changer...\n\n";
147
            $this->db->skipArticle($title);
148
149
            return false;
150
        }
151
152
        // EDIT THE PAGE
153
        if (!$this->wikiText) {
154
            return false;
155
        }
156
157
        $miniSummary = $this->generateSummary();
158
        echo "Edition ?\n".$miniSummary."\n\n";
159
        echo "sleep 20...\n";
160
        sleep(20);
161
162
        $editInfo = new EditInfo($miniSummary, $this->minorFlag, $this->botFlag);
163
        $success = $page->editPage(\Normalizer::normalize($this->wikiText), $editInfo);
164
        echo ($success) ? "Ok\n" : "***** Erreur edit\n";
165
166
        if ($success) {
167
            // updata DB
168
            foreach ($data as $dat) {
169
                $this->db->sendEditedData(['id' => $dat['id']]);
170
            }
171
172
            try {
173
                if (self::EDIT_SIGNALEMENT) {
174
                    $this->sendErrorMessage($data);
175
                }
176
            } catch (Throwable $e) {
177
                dump($e);
178
                unset($e);
179
            }
180
181
            if (!$this->botFlag) {
182
                echo "sleep ".self::DELAY_NOBOT_IN_SECONDS."\n";
183
                sleep(self::DELAY_NOBOT_IN_SECONDS);
184
            }
185
            echo "sleep ".self::DELAY_BOTFLAG_SECONDS."\n";
186
            sleep(self::DELAY_BOTFLAG_SECONDS);
187
        }
188
189
        return $success;
190
    }
191
192
    private function dataProcess(array $data): bool
193
    {
194
        $origin = $data['raw'];
195
        $completed = $data['opti'];
196
197
        dump($origin, $completed, $data['modifs'], $data['version']);
198
199
        if (WikiTextUtil::isCommented($origin)) {
200
            echo "SKIP: template avec commentaire HTML\n";
201
            $this->db->skipRow(intval($data['id']));
202
203
            return false;
204
        }
205
206
        $find = mb_strpos($this->wikiText, $origin);
207
        if ($find === false) {
208
            echo "String non trouvée. \n\n";
209
            $this->db->skipRow(intval($data['id']));
210
211
            return false;
212
        }
213
214
        $this->checkErrorWarning($data);
215
216
        // Replace text
217
        $newText = WikiPageAction::replaceTemplateInText($this->wikiText, $origin, $completed);
218
219
        if (!$newText || $newText === $this->wikiText) {
220
            echo "newText error\n";
221
222
            return false;
223
        }
224
        $this->wikiText = $newText;
225
        $this->minorFlag = ('1' === $data['major']) ? false : $this->minorFlag;
226
        $this->citationVersion = $data['version'];
227
        $this->citationSummary[] = $data['modifs'];
228
        $this->nbRows++;
229
230
        return true;
231
    }
232
233
    /**
234
     * Generate wiki edition summary.
235
     *
236
     * @return string
237
     */
238
    public function generateSummary(): string
239
    {
240
        // Start summary with "Bot" when using botflag, else "*"
241
        $prefix = ($this->botFlag) ? 'bot' : '☆';
242
        // add "/!\" when errorWarning
243
        $prefix = (!empty($this->errorWarning) && !$this->botFlag) ? '⚠' : $prefix;
244
245
246
        // basic modifs
247
        $citeSummary = implode(' ', $this->citationSummary);
248
        // replace by list of modifs to verify by humans
249
        if (!empty($this->importantSummary)) {
250
            $citeSummary = implode(', ', $this->importantSummary);
251
        }
252
253
        $summary = sprintf(
254
            '%s [%s/%s] %s %s : %s',
255
            $prefix,
256
            str_replace('v', '', $this->bot::getGitVersion()),
257
            str_replace('v0.', '', $this->citationVersion),
258
            self::TASK_NAME,
259
            $this->nbRows,
260
            $citeSummary
261
        );
262
263
        if (!empty($this->importantSummary)) {
264
            $summary .= '...';
265
        }
266
267
        // shrink long summary if no important details to verify
268
        if (empty($this->importantSummary)) {
269
            $length = strlen($summary);
270
            $summary = substr($summary, 0, 80);
271
            $summary .= ($length > strlen($summary)) ? '…' : '';
272
        }
273
274
        return $summary;
275
    }
276
277
    /**
278
     * Vérifie alerte d'erreurs humaines.
279
     *
280
     * @param array $data
281
     *
282
     * @throws \Exception
283
     */
284
    private function checkErrorWarning(array $data): void
285
    {
286
        if (!isset($data['opti'])) {
287
            throw new \LogicException('Opti NULL');
288
        }
289
290
        // paramètre inconnu
291
        if (preg_match(
292
                "#\|[^|]+<!-- ?(PARAMETRE [^>]+ N'EXISTE PAS|VALEUR SANS NOM DE PARAMETRE) ?-->#",
293
                $data['opti'],
294
                $matches
295
            ) > 0
296
        ) {
297
            $this->addErrorWarning($data['page'], $matches[0]);
298
            $this->botFlag = false;
299
            $this->addSummaryTag('paramètre non corrigé');
300
        }
301
302
        // ISBN invalide
303
        if (preg_match("#isbn invalide ?=[^|}]+#i", $data['opti'], $matches) > 0) {
304
            $this->addErrorWarning($data['page'], $matches[0]);
305
            $this->botFlag = false;
306
            $this->addSummaryTag('ISBN invalide');
307
        }
308
309
        // Edits avec ajout conséquent de donnée
310
        if (preg_match('#distinction des auteurs#', $data['modifs']) > 0) {
311
            $this->botFlag = false;
312
            $this->addSummaryTag('distinction des auteurs');
313
        }
314
        // prédiction paramètre correct
315
        if (preg_match('#[^,]+(=>|⇒)[^,]+#', $data['modifs'], $matches) > 0) {
316
            $this->botFlag = false;
317
            $this->addSummaryTag(sprintf('%s', $matches[0]));
318
        }
319
        if (preg_match('#\+\+sous-titre#', $data['modifs']) > 0) {
320
            $this->botFlag = false;
321
            $this->addSummaryTag('+sous-titre');
322
        }
323
        if (preg_match('#\+lieu#', $data['modifs']) > 0) {
324
            $this->addSummaryTag('+lieu');
325
        }
326
        if (preg_match('#\+éditeur#', $data['modifs']) > 0) {
327
            $this->addSummaryTag('éditeur');
328
        }
329
        //        if (preg_match('#\+langue#', $data['modifs']) > 0) {
330
        //            $this->addSummaryTag('langue');
331
        //        }
332
333
        // mention BnF si ajout donnée + ajout identifiant bnf=
334
        if (!empty($this->importantSummary) && preg_match('#\+bnf#i', $data['modifs'], $matches) > 0) {
335
            $this->addSummaryTag('[[BnF]]');
336
        }
337
    }
338
339
    /**
340
     * Pour éviter les doublons dans signalements d'erreur.
341
     *
342
     * @param string $page
343
     * @param string $text
344
     */
345
    private function addErrorWarning(string $page, string $text): void
346
    {
347
        if (!isset($this->errorWarning[$page]) || !in_array($text, $this->errorWarning[$page])) {
348
            $this->errorWarning[$page][] = $text;
349
        }
350
    }
351
352
    /**
353
     * For substantive or ambiguous modifications done.
354
     *
355
     * @param string $tag
356
     */
357
    private function addSummaryTag(string $tag)
358
    {
359
        if (!in_array($tag, $this->importantSummary)) {
360
            $this->importantSummary[] = $tag;
361
        }
362
    }
363
364
    /**
365
     * @param array $rows Collection of citations
366
     */
367
    private function sendErrorMessage(array $rows): void
368
    {
369
        if (empty($this->errorWarning[$rows[0]['page']])) {
370
            return;
371
        }
372
        echo "** Send Error Message on talk page. Wait 10... \n";
373
        sleep(3);
374
375
        // format wiki message
376
        $errorList = '';
377
        foreach ($this->errorWarning[$rows[0]['page']] as $error) {
378
            $errorList .= sprintf("* <span style=\"background:#FCDFE8\"><nowiki>%s</nowiki></span> \n", $error);
379
        }
380
        $errorMessage = file_get_contents(self::ERROR_MSG_TEMPLATE);
381
        $errorMessage = str_replace('##ERROR LIST##', trim($errorList), $errorMessage);
382
        $errorMessage = str_replace('##ARTICLE##', $rows[0]['page'], $errorMessage);
383
384
        // Edit wiki talk page
385
        try {
386
            $talkPage = new WikiPageAction($this->wiki, 'Discussion:'.$rows[0]['page']);
387
            $editInfo = new EditInfo('Signalement erreur {ouvrage}', false, false, 5);
0 ignored issues
show
Unused Code introduced by
The call to Mediawiki\DataModel\EditInfo::__construct() has too many arguments starting with 5. ( Ignorable by Annotation )

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

387
            $editInfo = /** @scrutinizer ignore-call */ new EditInfo('Signalement erreur {ouvrage}', false, false, 5);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
388
            $talkPage->addToBottomOfThePage($errorMessage, $editInfo);
389
        } catch (\Throwable $e) {
390
            unset($e);
391
        }
392
    }
393
394
    private function initialize(): void
395
    {
396
        // initialisation vars
397
        $this->botFlag = true;
398
        $this->errorWarning = [];
399
        $this->wikiText = null;
400
        $this->citationSummary = [];
401
        $this->importantSummary = [];
402
        $this->minorFlag = true;
403
        $this->nbRows = 0;
404
405
        $this->bot->checkStopOnTalkpage();
406
    }
407
408
}
409