Passed
Branch master (35b4f7)
by Dispositif
03:50 queued 01:17
created

OuvrageEditWorker::pageProcess()   B

Complexity

Conditions 9
Paths 19

Size

Total Lines 55
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 55
rs 8.0555
c 0
b 0
f 0
cc 9
nc 19
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
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
    public function __construct(
78
        DbAdapterInterface $dbAdapter,
79
        WikiBotConfig      $bot,
80
        MemoryInterface    $memory,
81
        ?LoggerInterface   $log = null
82
    )
83
    {
84
        $this->db = $dbAdapter;
85
        $this->bot = $bot;
86
        $this->memory = $memory;
87
        $this->log = $log ?? new NullLogger();
88
    }
89
90
    /**
91
     * @throws Exception
92
     */
93
    public function run(): void
94
    {
95
        while (true) {
96
            echo "\n-------------------------------------\n\n";
97
            echo date("Y-m-d H:i:s") . " ";
98
            $this->log->info($this->memory->getMemory(true));
99
            $this->pageProcess();
100
            sleep(2); // précaution boucle infinie
101
        }
102
    }
103
104
    /**
105
     * @throws UsageException
106
     * @throws Exception
107
     */
108
    protected function pageProcess(): bool
109
    {
110
        $e = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $e is dead and can be removed.
Loading history...
111
112
        if ((new TalkStopValidator($this->bot))->validate() === false) { // move up ?
113
            return false;
114
        }
115
116
        // get a random queue line
117
        $json = $this->db->getAllRowsOfOneTitleToEdit(self::CITATION_LIMIT);
118
        $pageCitationCollection = $json ? json_decode($json, true, 512, JSON_THROW_ON_ERROR) : [];
119
120
        if ((new CitationsNotEmptyValidator($pageCitationCollection, $this->log))->validate() === false) {
121
            return false;
122
        }
123
124
        $this->pageWorkStatus = new PageWorkStatus($pageCitationCollection[0]['page']);
125
        $this->printTitle($this->pageWorkStatus->getTitle());
126
127
        // Find on wikipedia the page to edit
128
        try {
129
            $this->wikiPageAction = ServiceFactory::wikiPageAction($this->pageWorkStatus->getTitle()); // , true ?
130
        } catch (Exception $e) {
131
            $this->log->warning("*** WikiPageAction error : " . $this->pageWorkStatus->getTitle() . " \n");
132
            sleep(20);
133
134
            return false;
135
        }
136
        $pageValidator = new PageValidatorComposite(
137
            $this->bot, $pageCitationCollection, $this->db, $this->wikiPageAction
138
        );
139
        if ($pageValidator->validate() === false) {
140
            return false;
141
        }
142
143
        $this->pageWorkStatus->wikiText = $this->wikiPageAction->getText();
144
        $this->checkArticleLabels($this->pageWorkStatus->getTitle());
145
        // or do that at the end of ArticleValidForEditValidator if PageWorkStatus injected ?
146
147
        // print total number of rows completed in db
148
        $rowNumber = is_countable($pageCitationCollection) ? count($pageCitationCollection) : 0;
149
        $this->log->info(sprintf("%s rows to process\n", $rowNumber));
150
151
        // Make citations replacements
152
        if ($this->makeCitationsReplacements($pageCitationCollection) === false) {
153
            return false;
154
        }
155
156
        if ($this->editPage()) {
157
            $this->updateDb($pageCitationCollection);
158
159
            return true;
160
        }
161
162
        return false;
163
    }
164
165
    protected function printTitle(string $title): void
166
    {
167
        echo Color::BG_CYAN . $title . Color::NORMAL . " \n";
168
    }
169
170
    protected function checkArticleLabels($title): void
171
    {
172
        // Featured/Good article (AdQ/BA) todo event listener
173
        if (preg_match('#{{ ?En-tête label ?\| ?AdQ#i', $this->pageWorkStatus->wikiText)) {
174
            $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

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

277
            $this->db->/** @scrutinizer ignore-call */ 
278
                       sendEditedData(['id' => $ouvrageData['id']]);
Loading history...
278
        }
279
        try {
280
            if (self::EDIT_SIGNALEMENT && !empty($this->pageWorkStatus->errorWarning[$title])) {
281
                $this->sendOuvrageErrorsOnTalkPage($pageOuvrageCollection, $this->log);
282
            }
283
        } catch (Throwable $e) {
284
            $this->log->warning('Exception in editPage() ' . $e->getMessage());
285
            unset($e);
286
        }
287
288
        if (!$this->pageWorkStatus->botFlag) {
289
            $this->log->debug("sleep " . self::DELAY_NO_BOTFLAG_SECONDS);
290
            sleep(self::DELAY_NO_BOTFLAG_SECONDS);
291
        }
292
        if ($this->pageWorkStatus->botFlag) {
293
            $this->log->debug("sleep " . self::DELAY_BOTFLAG_SECONDS);
294
            sleep(self::DELAY_BOTFLAG_SECONDS);
295
        }
296
    }
297
298
    /**
299
     * todo extract to OuvrageEditSummaryTrait ?
300
     * Pour éviter les doublons dans signalements d'erreur.
301
     */
302
    protected function addErrorWarning(string $title, string $text): void
303
    {
304
        if (!isset($this->pageWorkStatus->errorWarning[$title]) || !in_array($text, $this->pageWorkStatus->errorWarning[$title])) {
305
            $this->pageWorkStatus->errorWarning[$title][] = $text;
306
        }
307
    }
308
}
309