Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReassignMentees
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 5
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
2
 init
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getAllUnofficialMentors
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
 getMentorsToProcess
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
72
 execute
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace GrowthExperiments\Maintenance;
4
5use GrowthExperiments\GrowthExperimentsServices;
6use GrowthExperiments\Mentorship\Provider\MentorProvider;
7use GrowthExperiments\Mentorship\ReassignMenteesFactory;
8use GrowthExperiments\Mentorship\Store\MentorStore;
9use Maintenance;
10use MediaWiki\MediaWikiServices;
11use MediaWiki\User\UserIdentity;
12use MediaWiki\User\UserIdentityLookup;
13use RequestContext;
14use Wikimedia\Rdbms\IReadableDatabase;
15
16$IP = getenv( 'MW_INSTALL_PATH' );
17if ( $IP === false ) {
18    $IP = __DIR__ . '/../../..';
19}
20require_once "$IP/maintenance/Maintenance.php";
21
22class ReassignMentees extends Maintenance {
23
24    private IReadableDatabase $growthDbr;
25    private UserIdentityLookup $userIdentityLookup;
26    private MentorProvider $mentorProvider;
27    private ReassignMenteesFactory $reassignMenteesFactory;
28
29    public function __construct() {
30        parent::__construct();
31
32        $this->requireExtension( 'GrowthExperiments' );
33        $this->addDescription(
34            'Reassign mentees assigned to a particular user (who is not in the mentor list)'
35        );
36        $this->addOption(
37            'performer',
38            'Who should the reassignments be attributed to?',
39            true,
40            true
41        );
42        $this->addOption(
43            'all',
44            'Reassign mentees assigned to all non-mentor users'
45        );
46        $this->addOption(
47            'mentor',
48            'Username of the mentor to do the reassigning for',
49            false,
50            true
51        );
52    }
53
54    private function init() {
55        $services = MediaWikiServices::getInstance();
56        $growthServices = GrowthExperimentsServices::wrap( $services );
57        $growthLb = $growthServices->getLoadBalancer();
58
59        $this->growthDbr = $growthLb->getConnection( DB_REPLICA );
60        $this->userIdentityLookup = $services->getUserIdentityLookup();
61        $this->mentorProvider = $growthServices->getMentorProvider();
62        $this->reassignMenteesFactory = $growthServices->getReassignMenteesFactory();
63    }
64
65    /**
66     * @return UserIdentity[]
67     */
68    private function getAllUnofficialMentors(): array {
69        $officialMentors = iterator_to_array( $this->userIdentityLookup->newSelectQueryBuilder()
70            ->whereUserNames( $this->mentorProvider->getMentors() )
71            ->fetchUserIdentities() );
72        $officialMentorIds = array_map( static function ( UserIdentity $user ) {
73            return $user->getId();
74        }, $officialMentors );
75
76        $unofficialMentorIds = $this->growthDbr->newSelectQueryBuilder()
77            ->select( 'gemm_mentor_id' )
78            ->distinct()
79            ->from( 'growthexperiments_mentor_mentee' )
80            ->where( [
81                'gemm_mentor_role' => MentorStore::ROLE_PRIMARY,
82                $this->growthDbr->expr( 'gemm_mentor_id', '!=', $officialMentorIds ),
83            ] )
84            ->fetchFieldValues();
85
86        if ( $unofficialMentorIds === [] ) {
87            return [];
88        }
89
90        return iterator_to_array( $this->userIdentityLookup->newSelectQueryBuilder()
91            ->whereUserIds( $unofficialMentorIds )
92            ->fetchUserIdentities() );
93    }
94
95    /**
96     * @return UserIdentity[]
97     */
98    private function getMentorsToProcess(): array {
99        $mentors = [];
100        if ( !$this->hasOption( 'all' ) && !$this->hasOption( 'mentor' ) ) {
101            $this->fatalError( 'ERROR: Either --all or --mentor needs to be provided.' );
102        } elseif ( $this->hasOption( 'all' ) && $this->hasOption( 'mentor' ) ) {
103            $this->fatalError( 'ERROR: --all and --mentor together do not make sense.' );
104        } elseif ( $this->hasOption( 'mentor' ) ) {
105            $mentor = $this->userIdentityLookup->getUserIdentityByName( $this->getOption( 'mentor' ) );
106            if ( !$mentor ) {
107                $this->fatalError( 'ERROR: User not found.' );
108            }
109            $mentors[] = $mentor;
110        } elseif ( $this->hasOption( 'all' ) ) {
111            $mentors = $this->getAllUnofficialMentors();
112        }
113        return $mentors;
114    }
115
116    /**
117     * @inheritDoc
118     */
119    public function execute() {
120        $this->init();
121
122        $performerName = $this->getOption( 'performer' );
123        $performer = $this->userIdentityLookup->getUserIdentityByName( $performerName );
124        if ( !$performer ) {
125            $this->fatalError( 'ERROR: User "' . $performerName . '" does not exist.' );
126        }
127
128        $mentors = $this->getMentorsToProcess();
129        foreach ( $mentors as $mentor ) {
130            if ( $this->mentorProvider->isMentor( $mentor ) ) {
131                $this->error(
132                    "ERROR: User \"{$mentor->getName()}\" is currently enrolled as a mentor. " .
133                    'Remove them before the reassignment.'
134                );
135                continue;
136            }
137
138            $this->reassignMenteesFactory->newReassignMentees(
139                $performer,
140                $mentor,
141                RequestContext::getMain()
142            )->doReassignMentees(
143                'growthexperiments-quit-mentorship-reassign-mentees-log-message',
144                $mentor->getName()
145            );
146        }
147
148        $this->output( 'Done!' . PHP_EOL );
149    }
150}
151
152$maintClass = ReassignMentees::class;
153require_once RUN_MAINTENANCE_IF_MAIN;