Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
FlowFixEditCount
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 5
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getUpdateKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doDBUpdates
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 refreshBatch
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
20
 getCountableActions
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace Flow\Maintenance;
4
5use Flow\Container;
6use Flow\FlowActions;
7use Flow\Model\UUID;
8use LoggedUpdateMaintenance;
9use MediaWiki\User\User;
10use MediaWiki\WikiMap\WikiMap;
11use Wikimedia\Rdbms\IReadableDatabase;
12
13$IP = getenv( 'MW_INSTALL_PATH' );
14if ( $IP === false ) {
15    $IP = __DIR__ . '/../../..';
16}
17
18require_once "$IP/maintenance/Maintenance.php";
19
20/**
21 * Adjusts edit counts for all existing Flow data.
22 *
23 * @ingroup Maintenance
24 */
25class FlowFixEditCount extends LoggedUpdateMaintenance {
26    /**
27     * Array of [username => increased edit count]
28     *
29     * @var array
30     */
31    protected $updates = [];
32
33    public function __construct() {
34        parent::__construct();
35
36        $this->addDescription( 'Adjusts edit counts for all existing Flow data' );
37
38        $this->addOption( 'start', 'Timestamp to start counting revisions at', false, true );
39        $this->addOption( 'stop', 'Timestamp to stop counting revisions at', false, true );
40
41        $this->setBatchSize( 300 );
42
43        $this->requireExtension( 'Flow' );
44    }
45
46    protected function getUpdateKey() {
47        return 'FlowFixEditCount';
48    }
49
50    protected function doDBUpdates() {
51        /** @var IReadableDatabase $dbr */
52        $dbr = Container::get( 'db.factory' )->getDB( DB_REPLICA );
53        $countableActions = $this->getCountableActions();
54
55        // defaults = date of first Flow commit up until now
56        $continue = UUID::getComparisonUUID( $this->getOption( 'start', '20130710230511' ) );
57        $stop = UUID::getComparisonUUID( $this->getOption( 'stop', time() ) );
58
59        while ( $continue !== false ) {
60            $continue = $this->refreshBatch( $dbr, $continue, $countableActions, $stop );
61
62            // wait for core (we're updating user table) replicas to catch up
63            $this->waitForReplication();
64        }
65
66        $this->output( "Done increasing edit counts. Increased:\n" );
67        foreach ( $this->updates as $userId => $count ) {
68            $userName = User::newFromId( $userId )->getName();
69            $this->output( "  User $userId ($userName): +$count\n" );
70        }
71
72        return true;
73    }
74
75    public function refreshBatch( IReadableDatabase $dbr, UUID $continue, array $countableActions, UUID $stop ) {
76        $rows = $dbr->newSelectQueryBuilder()
77            ->select( [ 'rev_id', 'rev_user_id' ] )
78            ->from( 'flow_revision' )
79            ->where( [
80                $dbr->expr( 'rev_id', '>', $continue->getBinary() ),
81                $dbr->expr( 'rev_id', '<=', $stop->getBinary() ),
82                $dbr->expr( 'rev_user_id', '>', 0 ),
83                'rev_user_wiki' => WikiMap::getCurrentWikiId(),
84                'rev_change_type' => $countableActions,
85            ] )
86            ->orderBy( 'rev_id' )
87            ->limit( $this->getBatchSize() )
88            ->caller( __METHOD__ )
89            ->fetchResultSet();
90
91        // end of data
92        if ( $rows->numRows() === 0 ) {
93            return false;
94        }
95
96        $userEditTracker = $this->getServiceContainer()->getUserEditTracker();
97        foreach ( $rows as $row ) {
98            // UserEditTracker::incrementUserEditCount only allows for edit count to be
99            // increased 1 at a time. It'd be better to immediately be able to increase
100            // the edit count by the exact number it should be increased with, but
101            // I'd rather re-use existing code, especially in a run-once script,
102            // where performance is not the most important thing ;)
103            $user = User::newFromId( $row->rev_user_id );
104            $userEditTracker->incrementUserEditCount( $user );
105
106            // save updates so we can print them when the script is done running
107            if ( !isset( $this->updates[$user->getId()] ) ) {
108                $this->updates[$user->getId()] = 0;
109            }
110            $this->updates[$user->getId()]++;
111
112            // set value for next batch to continue at
113            $continue = $row->rev_id;
114        }
115
116        return UUID::create( $continue );
117    }
118
119    /**
120     * Returns list of rev_change_type values that warrant an editcount increase.
121     *
122     * @return string[]
123     */
124    protected function getCountableActions() {
125        $allowedActions = [];
126
127        /** @var FlowActions $actions */
128        $actions = Container::get( 'flow_actions' );
129        foreach ( $actions->getActions() as $action ) {
130            if ( $actions->getValue( $action, 'editcount' ) ) {
131                $allowedActions[] = $action;
132            }
133        }
134
135        return $allowedActions;
136    }
137}
138
139$maintClass = FlowFixEditCount::class;
140require_once RUN_MAINTENANCE_IF_MAIN;