Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
12.61% covered (danger)
12.61%
14 / 111
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
CheckRevision
12.61% covered (danger)
12.61%
14 / 111
50.00% covered (danger)
50.00%
1 / 2
91.75
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.73% covered (danger)
6.73%
7 / 104
0.00% covered (danger)
0.00%
0 / 1
91.14
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 = $autoModeratorServices->getAutoModeratorConfig();
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        $dbr = $this->getReplicaDB();
65        $tags = $changeTagsStore->getTags( $dbr, null, $revId );
66        $rev = $revisionLookup->getRevisionById( $revId );
67        // Check if revision or the revision user is not null
68        $userIdentity = $rev->getUser();
69        if ( !$rev || !$userIdentity ) {
70            return;
71        }
72        $wikiPageId = $rev->getPageId();
73        $contentHandler = $contentHandlerFactory->getContentHandler( $rev->getSlot(
74            SlotRecord::MAIN,
75            RevisionRecord::RAW
76        )->getModel() );
77        $logger = LoggerFactory::getInstance( 'AutoModerator' );
78        if ( !RevisionCheck::revertPreCheck(
79            $userIdentity,
80            $autoModeratorUser,
81            $logger,
82            $revisionStore,
83            $tags,
84            $restrictionStore,
85            $wikiPageFactory,
86            $config,
87            $rev,
88            $permissionManager
89        ) ) {
90            $this->output( "precheck skipped rev:\t$revId\n" );
91            return;
92        }
93
94        $revisionCheck = new RevisionCheck(
95            $config,
96            new AutoModeratorRollback(
97                new ServiceOptions( AutoModeratorRollback::CONSTRUCTOR_OPTIONS, $config ),
98                $services->getDBLoadBalancerFactory(),
99                $revisionStore,
100                $services->getTitleFormatter(),
101                $services->getHookContainer(),
102                $wikiPageFactory,
103                $services->getActorMigration(),
104                $services->getActorNormalization(),
105                $wikiPageFactory->newFromID( $wikiPageId ),
106                $autoModeratorUser->getUser(),
107                $rev->getUser(),
108                $config
109            )
110        );
111
112        // Get a real score or optionally set a fake score
113        $score = [];
114        switch ( $this->getOption( 'client', 'liftwing' ) ) {
115            case 'liftwing':
116                $liftWingClient = Util::initializeLiftWingClient( $config );
117                $score = $liftWingClient->get( $rev->getId() );
118                break;
119            case 'testfail':
120                $score = [
121                    'model_name' => 'revertrisk-language-agnostic',
122                    'model_version' => '3',
123                    'wiki_db' => 'enwiki',
124                    'revision_id' => $revId,
125                    'output' => [
126                        'prediction' => true,
127                        'probabilities' => [
128                            'true' => 1.000000000000000,
129                            'false' => 0.000000000000000,
130                        ],
131                    ],
132                ];
133                break;
134            case 'testpass':
135                $score = [
136                    'model_name' => 'revertrisk-language-agnostic',
137                    'model_version' => '3',
138                    'wiki_db' => 'enwiki',
139                    'revision_id' => $revId,
140                    'output' => [
141                        'prediction' => false,
142                        'probabilities' => [
143                            'true' => 0.000000000000000,
144                            'false' => 1.000000000000000,
145                        ],
146                    ],
147                ];
148                break;
149            default:
150                break;
151        }
152        $reverted = json_encode( $revisionCheck->maybeRollback( $score ),
153            JSON_FORCE_OBJECT,
154            JSON_PRETTY_PRINT );
155        $scoreStr = json_encode( $score, JSON_PRETTY_PRINT );
156        $this->output( "Revision ID:\t$revId\nWould revert?\t$reverted\nScore:\t$scoreStr\n" );
157    }
158}
159
160// @codeCoverageIgnoreStart
161$maintClass = CheckRevision::class;
162require_once RUN_MAINTENANCE_IF_MAIN;
163// @codeCoverageIgnoreEnd