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