Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
10.61% covered (danger)
10.61%
14 / 132
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
CheckRevision
11.11% covered (danger)
11.11%
14 / 126
50.00% covered (danger)
50.00%
1 / 2
131.69
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 execute
5.88% covered (danger)
5.88%
7 / 119
0.00% covered (danger)
0.00%
0 / 1
132.05
1<?php
2
3namespace AutoModerator\Maintenance;
4
5use AutoModerator\AutoModeratorServices;
6use AutoModerator\RevisionCheck;
7use AutoModerator\Services\AutoModeratorRollback;
8use AutoModerator\Util;
9use MediaWiki\Config\ServiceOptions;
10use MediaWiki\Logger\LoggerFactory;
11use MediaWiki\MainConfigNames;
12use MediaWiki\Maintenance\Maintenance;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\Revision\RevisionRecord;
15use MediaWiki\Revision\SlotRecord;
16
17$IP = getenv( 'MW_INSTALL_PATH' );
18if ( $IP === false ) {
19    $IP = __DIR__ . '/../../..';
20}
21require_once "$IP/maintenance/Maintenance.php";
22
23/**
24 * Check a revision to see if it would be reverted
25 */
26class CheckRevision extends Maintenance {
27
28    public function __construct() {
29        parent::__construct();
30        $this->requireExtension( 'AutoModerator' );
31        $this->addDescription(
32            'Check a revision and report if it would be reverted based on scoring from a machine learning model.'
33        );
34        $this->addOption( 'revid', 'Revision ID', true, true );
35        $this->addOption( 'client', 'Client for score fetching', false, true );
36    }
37
38    public function execute() {
39        if ( !ctype_digit( $this->getoption( 'revid' ) ) ) {
40            $this->output( "'revid' must be an integer\n" );
41            return;
42        }
43        $revId = (int)$this->getOption( 'revid' );
44        if ( $revId === 0 ) {
45            $this->output( "'revid' must be greater than zero\n" );
46            return;
47        }
48
49        // setup dependencies that we get for free when running in a hook.
50        $services = MediaWikiServices::getInstance();
51        $autoModeratorServices = AutoModeratorServices::wrap( $services );
52
53        $changeTagsStore = $services->getChangeTagsStore();
54        $config = $services->getMainConfig();
55        $wikiConfig = $autoModeratorServices->getAutoModeratorWikiConfig();
56        $contentHandlerFactory = $services->getContentHandlerFactory();
57        $revisionLookup = $services->getRevisionLookup();
58        $revisionStore = $services->getRevisionStoreFactory()->getRevisionStore();
59        $userGroupManager = $services->getUserGroupManager();
60        $wikiPageFactory = $services->getWikiPageFactory();
61        $restrictionStore = $services->getRestrictionStore();
62        $permissionManager = $services->getPermissionManager();
63        $autoModeratorUser = Util::getAutoModeratorUser( $config, $userGroupManager );
64        $wikiId = Util::getWikiID( $config );
65        $dbr = $this->getReplicaDB();
66        $tags = $changeTagsStore->getTags( $dbr, null, $revId );
67        $rev = $revisionLookup->getRevisionById( $revId );
68        // Check if revision or the revision user is not null
69        $userIdentity = $rev->getUser();
70        if ( !$rev || !$userIdentity ) {
71            return;
72        }
73        $wikiPageId = $rev->getPageId();
74        $undoSummaryMessageKey = ( !$userIdentity->isRegistered() && $config->get( MainConfigNames::DisableAnonTalk ) )
75            ? 'automoderator-wiki-undo-summary-anon' : 'automoderator-wiki-undo-summary';
76        $contentHandler = $contentHandlerFactory->getContentHandler( $rev->getSlot(
77            SlotRecord::MAIN,
78            RevisionRecord::RAW
79        )->getModel() );
80        $logger = LoggerFactory::getInstance( 'AutoModerator' );
81        if ( !RevisionCheck::revertPreCheck(
82            $userIdentity,
83            $autoModeratorUser,
84            $logger,
85            $revisionStore,
86            $tags,
87            $restrictionStore,
88            $wikiPageFactory,
89            $wikiConfig,
90            $revId,
91            $wikiPageId,
92            $permissionManager
93        ) ) {
94            $this->output( "precheck skipped rev:\t$revId\n" );
95            return;
96        }
97
98        $revisionCheck = new RevisionCheck(
99            $wikiPageId,
100            $wikiPageFactory,
101            $rev->getId(),
102            $autoModeratorUser,
103            $revisionStore,
104            $wikiConfig,
105            $config,
106            $contentHandler,
107            wfMessage( $undoSummaryMessageKey )->rawParams( $revId, $userIdentity->getName() )->plain(),
108            new AutoModeratorRollback(
109                new ServiceOptions( AutoModeratorRollback::CONSTRUCTOR_OPTIONS, $config ),
110                $services->getDBLoadBalancerFactory(),
111                $revisionStore,
112                $services->getTitleFormatter(),
113                $services->getHookContainer(),
114                $wikiPageFactory,
115                $services->getActorMigration(),
116                $services->getActorNormalization(),
117                $wikiPageFactory->newFromID( $wikiPageId ),
118                $autoModeratorUser->getUser(),
119                $rev->getUser(),
120                $config,
121                $wikiConfig
122            )
123        );
124
125        // Get a real score or optionally set a fake score
126        $score = [];
127        switch ( $this->getOption( 'client', 'liftwing' ) ) {
128            case 'liftwing':
129                $liftWingClient = Util::initializeLiftWingClient( $config );
130                $score = $liftWingClient->get( $rev->getId() );
131                break;
132            case 'testfail':
133                $score = [
134                    'model_name' => 'revertrisk-language-agnostic',
135                    'model_version' => '3',
136                    'wiki_db' => 'enwiki',
137                    'revision_id' => $revId,
138                    'output' => [
139                        'prediction' => true,
140                        'probabilities' => [
141                            'true' => 1.000000000000000,
142                            'false' => 0.000000000000000,
143                        ],
144                    ],
145                ];
146                break;
147            case 'testpass':
148                $score = [
149                    'model_name' => 'revertrisk-language-agnostic',
150                    'model_version' => '3',
151                    'wiki_db' => 'enwiki',
152                    'revision_id' => $revId,
153                    'output' => [
154                        'prediction' => false,
155                        'probabilities' => [
156                            'true' => 0.000000000000000,
157                            'false' => 1.000000000000000,
158                        ],
159                    ],
160                ];
161                break;
162            default:
163                break;
164        }
165        $revertRiskModelName = Util::getRevertRiskModel( $config );
166        $reverted = json_encode( $revisionCheck->maybeRollback( $score, $revertRiskModelName ),
167            JSON_FORCE_OBJECT,
168            JSON_PRETTY_PRINT );
169        $scoreStr = json_encode( $score, JSON_PRETTY_PRINT );
170        $this->output( "Revision ID:\t$revId\nWould revert?\t$reverted\nScore:\t$scoreStr\n" );
171    }
172}
173
174$maintClass = CheckRevision::class;
175require_once RUN_MAINTENANCE_IF_MAIN;