Passed
Push — master ( fd6b1a...a766ea )
by Dispositif
02:28
created

OuvrageEditWorker::addErrorWarning()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 2
1
<?php
2
3
/*
4
 * This file is part of dispositif/wikibot application (@github)
5
 * 2019-2023 © Philippe M./Irønie  <[email protected]>
6
 * For the full copyright and MIT license information, view the license file.
7
 */
8
9
declare(strict_types=1);
10
11
namespace App\Application\OuvrageEdit;
12
13
use App\Application\InfrastructurePorts\DbAdapterInterface;
14
use App\Application\InfrastructurePorts\MemoryInterface;
15
use App\Application\OuvrageEdit\Validators\CitationsNotEmptyValidator;
16
use App\Application\OuvrageEdit\Validators\CitationValidator;
17
use App\Application\OuvrageEdit\Validators\PageValidatorComposite;
18
use App\Application\OuvrageEdit\Validators\TalkStopValidator;
19
use App\Application\OuvrageEdit\Validators\WikiTextValidator;
20
use App\Application\WikiBotConfig;
21
use App\Application\WikiPageAction;
22
use App\Infrastructure\ServiceFactory;
23
use Codedungeon\PHPCliColors\Color;
24
use Exception;
25
use Mediawiki\Api\UsageException;
26
use Normalizer;
27
use Psr\Log\LoggerInterface;
28
use Psr\Log\NullLogger;
29
use Throwable;
30
31
/**
32
 * Legacy class, to be refactored. Too big, too many responsibilities.
33
 * todo use PageOuvrageCollectionDTO.
34
 * todo chain of responsibility pattern + log decorator (+ events ?)
35
 */
36
class OuvrageEditWorker
37
{
38
    use OuvrageEditSummaryTrait, TalkPageEditTrait;
0 ignored issues
show
Bug introduced by
The trait App\Application\OuvrageEdit\TalkPageEditTrait requires the property $errorWarning which is not provided by App\Application\OuvrageEdit\OuvrageEditWorker.
Loading history...
introduced by
The trait App\Application\OuvrageE...OuvrageEditSummaryTrait requires some properties which are not provided by App\Application\OuvrageEdit\OuvrageEditWorker: $importantSummary, $nbRows, $errorWarning
Loading history...
39
40
    public const TASK_NAME = '📗 Amélioration bibliographique'; // 📖📔📘📗
41
    public const LUCKY_MESSAGE = ' 🇺🇦'; // ☘️
42
    /**
43
     * poster ou pas le message en PD signalant les erreurs à résoudre
44
     */
45
    public const EDIT_SIGNALEMENT = true;
46
    public const CITATION_LIMIT = 150;
47
    public const DELAY_BOTFLAG_SECONDS = 60;
48
    public const DELAY_NO_BOTFLAG_SECONDS = 60;
49
    public const DELAY_MINUTES_AFTER_HUMAN_EDIT = 10;
50
    public const ERROR_MSG_TEMPLATE = __DIR__ . '/templates/message_errors.wiki';
51
52
    /**
53
     * @var PageWorkStatus
54
     */
55
    protected $pageWorkStatus;
56
57
    /**
58
     * @var WikiPageAction
59
     */
60
    protected $wikiPageAction = null;
61
62
    protected $db;
63
    /**
64
     * @var WikiBotConfig
65
     */
66
    protected $bot;
67
    /**
68
     * @var MemoryInterface
69
     */
70
    protected $memory;
71
72
    /**
73
     * @var LoggerInterface
74
     */
75
    protected $log;
76
    /**
77
     * @var ImportantSummaryCreator
78
     */
79
    protected $summaryCreator;
80
81
    public function __construct(
82
        DbAdapterInterface $dbAdapter,
83
        WikiBotConfig      $bot,
84
        MemoryInterface    $memory,
85
        ?LoggerInterface   $log = null
86
    )
87
    {
88
        $this->db = $dbAdapter;
89
        $this->bot = $bot;
90
        $this->memory = $memory;
91
        $this->log = $log ?? new NullLogger();
92
    }
93
94
    /**
95
     * @throws Exception
96
     */
97
    public function run(): void
98
    {
99
        while (true) {
100
            echo "\n-------------------------------------\n\n";
101
            echo date("Y-m-d H:i:s") . " ";
102
            $this->log->info($this->memory->getMemory(true));
103
            $this->pageProcess();
104
            sleep(2); // précaution boucle infinie
105
        }
106
    }
107
108
    /**
109
     * @throws UsageException
110
     * @throws Exception
111
     */
112
    protected function pageProcess(): bool
113
    {
114
        if ((new TalkStopValidator($this->bot))->validate() === false) { // move up ?
115
            return false;
116
        }
117
118
        // get a random queue line
119
        $json = $this->db->getAllRowsOfOneTitleToEdit(self::CITATION_LIMIT);
120
        $pageCitationCollection = $json ? json_decode($json, true, 512, JSON_THROW_ON_ERROR) : [];
121
122
        if ((new CitationsNotEmptyValidator($pageCitationCollection, $this->log))->validate() === false) {
123
            return false;
124
        }
125
126
        $this->pageWorkStatus = new PageWorkStatus($pageCitationCollection[0]['page']);
127
        $this->printTitle($this->pageWorkStatus->getTitle());
128
129
        // Find on wikipedia the page to edit
130
        try {
131
            $this->wikiPageAction = ServiceFactory::wikiPageAction($this->pageWorkStatus->getTitle()); // , true ?
132
        } catch (Exception $e) {
133
            $this->log->warning("*** WikiPageAction error : " . $this->pageWorkStatus->getTitle() . " \n");
134
            sleep(20);
135
136
            return false;
137
        }
138
        $pageValidator = new PageValidatorComposite(
139
            $this->bot, $pageCitationCollection, $this->db, $this->wikiPageAction
140
        );
141
        if ($pageValidator->validate() === false) {
142
            return false;
143
        }
144
145
        $this->pageWorkStatus->wikiText = $this->wikiPageAction->getText();
146
        $this->checkArticleLabels($this->pageWorkStatus->getTitle());
147
        // or do that at the end of ArticleValidForEditValidator if PageWorkStatus injected ?
148
149
        // print total number of rows completed in db
150
        $rowNumber = is_countable($pageCitationCollection) ? count($pageCitationCollection) : 0;
151
        $this->log->info(sprintf("%s rows to process\n", $rowNumber));
152
153
        // Make citations replacements
154
        if ($this->makeCitationsReplacements($pageCitationCollection) === false) {
155
            return false;
156
        }
157
158
        if ($this->editPage()) {
159
            $this->updateDb($pageCitationCollection);
160
161
            return true;
162
        }
163
164
        return false;
165
    }
166
167
    protected function printTitle(string $title): void
168
    {
169
        echo Color::BG_CYAN . $title . Color::NORMAL . " \n";
170
    }
171
172
    protected function checkArticleLabels($title): void
173
    {
174
        // Featured/Good article (AdQ/BA) todo event listener
175
        if (preg_match('#{{ ?En-tête label ?\| ?AdQ#i', $this->pageWorkStatus->wikiText)) {
176
            $this->db->setLabel($title, 2);
0 ignored issues
show
Bug introduced by
The method setLabel() does not exist on App\Application\Infrastr...orts\DbAdapterInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to App\Application\Infrastr...orts\DbAdapterInterface. ( Ignorable by Annotation )

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

176
            $this->db->/** @scrutinizer ignore-call */ 
177
                       setLabel($title, 2);
Loading history...
177
            $this->log->warning("Article de Qualité !\n");
178
            $this->pageWorkStatus->botFlag = false;
179
            $this->pageWorkStatus->featured_article = true; // to add star in edit summary
180
        }
181
        if (preg_match('#{{ ?En-tête label ?\| ?BA#i', $this->pageWorkStatus->wikiText)) {
182
            $this->db->setLabel($title, 1);
183
            $this->pageWorkStatus->botFlag = false;
184
            $this->pageWorkStatus->featured_article = true; // to add star in edit summary
185
            $this->log->warning("Bon article !!\n");
186
        }
187
    }
188
189
    protected function makeCitationsReplacements(array $pageCitationCollection): bool
190
    {
191
        $oldText = $this->pageWorkStatus->wikiText;
192
        $this->summaryCreator = new ImportantSummaryCreator($this->pageWorkStatus);
193
        foreach ($pageCitationCollection as $dat) {
194
            $this->processOneCitation($dat); // that modify PageWorkStatus->wikiText
195
        }
196
        $newWikiTextValidator = new WikiTextValidator(
197
            $this->pageWorkStatus->wikiText, $oldText, $this->log, $this->pageWorkStatus->getTitle(), $this->db
198
        );
199
200
        return $newWikiTextValidator->validate();
201
    }
202
203
    /**
204
     * @throws Exception
205
     */
206
    protected function processOneCitation(array $ouvrageData): bool
207
    {
208
        $origin = $ouvrageData['raw'];
209
        $completed = $ouvrageData['opti'];
210
        $this->printDebug($ouvrageData);
211
212
        $citationValidator = new CitationValidator(
213
            $ouvrageData,
214
            $this->pageWorkStatus->wikiText,
215
            $this->log,
216
            $this->db
217
        );
218
        if ($citationValidator->validate() === false) {
219
            return false;
220
        }
221
222
        // Replace text
223
        $newText = WikiPageAction::replaceTemplateInText($this->pageWorkStatus->wikiText, $origin, $completed);
224
225
        if (empty($newText) || $newText === $this->pageWorkStatus->wikiText) {
226
            $this->log->warning("newText error");
227
228
            return false;
229
        }
230
        $this->summaryCreator->processPageOuvrage($ouvrageData);
231
        $this->pageWorkStatus->wikiText = $newText;
232
        $this->pageWorkStatus->minorFlag = ('1' === $ouvrageData['major']) ? false : $this->pageWorkStatus->minorFlag;
233
        $this->pageWorkStatus->citationVersion = $ouvrageData['version']; // todo gérer versions différentes
234
        $this->pageWorkStatus->citationSummary[] = $ouvrageData['modifs'];
235
        $this->pageWorkStatus->nbRows++;
236
237
        return true;
238
    }
239
240
    protected function printDebug(array $data)
241
    {
242
        $this->log->debug('origin: ' . $data['raw']);
243
        $this->log->debug('completed: ' . $data['opti']);
244
        $this->log->debug('modifs: ' . $data['modifs']);
245
        $this->log->debug('version: ' . $data['version']);
246
    }
247
248
    protected function editPage(): bool
249
    {
250
        $miniSummary = $this->generateFinalSummary();
251
252
        $this->log->debug("sleep 2...");
253
        sleep(2); // todo ???
254
255
        try {
256
            $editInfo = ServiceFactory::editInfo($miniSummary, $this->pageWorkStatus->minorFlag, $this->pageWorkStatus->botFlag);
257
            $success = $this->wikiPageAction->editPage(Normalizer::normalize($this->pageWorkStatus->wikiText), $editInfo);
258
        } catch (Throwable $e) {
259
            // Invalid CSRF token.
260
            if (strpos($e->getMessage(), 'Invalid CSRF token') !== false) {
261
                $this->log->alert("*** Invalid CSRF token \n");
262
                throw new Exception('Invalid CSRF token', $e->getCode(), $e);
263
            } else {
264
                $this->log->warning('Exception in editPage() ' . $e->getMessage());
265
                sleep(10);
266
267
                return false;
268
            }
269
        }
270
        $this->log->info($success ? "Edition Ok\n" : "***** Edition KO !\n");
271
272
        return $success;
273
    }
274
275
    protected function updateDb(array $pageOuvrageCollection)
276
    {
277
        $title = $pageOuvrageCollection[0]['page'];
278
        foreach ($pageOuvrageCollection as $ouvrageData) {
279
            $this->db->sendEditedData(['id' => $ouvrageData['id']]);
0 ignored issues
show
Bug introduced by
The method sendEditedData() does not exist on App\Application\Infrastr...orts\DbAdapterInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to App\Application\Infrastr...orts\DbAdapterInterface. ( Ignorable by Annotation )

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

279
            $this->db->/** @scrutinizer ignore-call */ 
280
                       sendEditedData(['id' => $ouvrageData['id']]);
Loading history...
280
        }
281
        try {
282
            if (self::EDIT_SIGNALEMENT && !empty($this->pageWorkStatus->errorWarning[$title])) {
283
                $this->sendOuvrageErrorsOnTalkPage($pageOuvrageCollection, $this->log);
284
            }
285
        } catch (Throwable $e) {
286
            $this->log->warning('Exception in editPage() ' . $e->getMessage());
287
            unset($e);
288
        }
289
290
        if (!$this->pageWorkStatus->botFlag) {
291
            $this->log->debug("sleep " . self::DELAY_NO_BOTFLAG_SECONDS);
292
            sleep(self::DELAY_NO_BOTFLAG_SECONDS);
293
        }
294
        if ($this->pageWorkStatus->botFlag) {
295
            $this->log->debug("sleep " . self::DELAY_BOTFLAG_SECONDS);
296
            sleep(self::DELAY_BOTFLAG_SECONDS);
297
        }
298
    }
299
}
300