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