Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
36.36% |
28 / 77 |
|
25.00% |
1 / 4 |
CRAP | |
0.00% |
0 / 1 |
ReassignMentees | |
36.36% |
28 / 77 |
|
25.00% |
1 / 4 |
49.11 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
getStage | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
reassignMentees | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
doReassignMentees | |
44.23% |
23 / 52 |
|
0.00% |
0 / 1 |
12.24 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\Mentorship; |
4 | |
5 | use GrowthExperiments\Mentorship\Provider\MentorProvider; |
6 | use GrowthExperiments\Mentorship\Store\MentorStore; |
7 | use GrowthExperiments\WikiConfigException; |
8 | use MediaWiki\JobQueue\JobQueueGroupFactory; |
9 | use MediaWiki\Status\StatusFormatter; |
10 | use MediaWiki\User\UserIdentity; |
11 | use MediaWiki\WikiMap\WikiMap; |
12 | use MessageLocalizer; |
13 | use Psr\Log\LoggerAwareTrait; |
14 | use Psr\Log\NullLogger; |
15 | use Wikimedia\Rdbms\IDatabase; |
16 | |
17 | class ReassignMentees { |
18 | use LoggerAwareTrait; |
19 | |
20 | public const STAGE_LISTED_AS_MENTOR = 1; |
21 | public const STAGE_NOT_LISTED_HAS_MENTEES = 2; |
22 | public const STAGE_NOT_LISTED_NO_MENTEES = 3; |
23 | |
24 | private IDatabase $dbw; |
25 | private MentorManager $mentorManager; |
26 | private MentorProvider $mentorProvider; |
27 | private MentorStore $mentorStore; |
28 | private ChangeMentorFactory $changeMentorFactory; |
29 | private JobQueueGroupFactory $jobQueueGroupFactory; |
30 | private StatusFormatter $statusFormatter; |
31 | private UserIdentity $performer; |
32 | private UserIdentity $mentor; |
33 | private MessageLocalizer $messageLocalizer; |
34 | |
35 | /** |
36 | * @param IDatabase $dbw |
37 | * @param MentorManager $mentorManager |
38 | * @param MentorProvider $mentorProvider |
39 | * @param MentorStore $mentorStore |
40 | * @param ChangeMentorFactory $changeMentorFactory |
41 | * @param JobQueueGroupFactory $jobQueueGroupFactory |
42 | * @param StatusFormatter $statusFormatter |
43 | * @param UserIdentity $performer |
44 | * @param UserIdentity $mentor |
45 | * @param MessageLocalizer $messageLocalizer |
46 | */ |
47 | public function __construct( |
48 | IDatabase $dbw, |
49 | MentorManager $mentorManager, |
50 | MentorProvider $mentorProvider, |
51 | MentorStore $mentorStore, |
52 | ChangeMentorFactory $changeMentorFactory, |
53 | JobQueueGroupFactory $jobQueueGroupFactory, |
54 | StatusFormatter $statusFormatter, |
55 | UserIdentity $performer, |
56 | UserIdentity $mentor, |
57 | MessageLocalizer $messageLocalizer |
58 | ) { |
59 | $this->dbw = $dbw; |
60 | $this->mentorManager = $mentorManager; |
61 | $this->mentorProvider = $mentorProvider; |
62 | $this->mentorStore = $mentorStore; |
63 | $this->changeMentorFactory = $changeMentorFactory; |
64 | $this->jobQueueGroupFactory = $jobQueueGroupFactory; |
65 | $this->statusFormatter = $statusFormatter; |
66 | $this->performer = $performer; |
67 | $this->mentor = $mentor; |
68 | $this->messageLocalizer = $messageLocalizer; |
69 | $this->logger = new NullLogger(); |
70 | } |
71 | |
72 | /** |
73 | * @return int One of ReassignMentees::STAGE_* constants |
74 | */ |
75 | public function getStage(): int { |
76 | if ( $this->mentorProvider->isMentor( $this->mentor ) ) { |
77 | return self::STAGE_LISTED_AS_MENTOR; |
78 | } elseif ( $this->mentorStore->hasAnyMentees( $this->mentor, MentorStore::ROLE_PRIMARY ) ) { |
79 | return self::STAGE_NOT_LISTED_HAS_MENTEES; |
80 | } else { |
81 | return self::STAGE_NOT_LISTED_NO_MENTEES; |
82 | } |
83 | } |
84 | |
85 | /** |
86 | * Reassign mentees currently assigned to the mentor via a job |
87 | * |
88 | * If no job is needed, use doReassignMentees directly. |
89 | * |
90 | * @param string $reassignMessageKey Message key used in in ChangeMentor notification; needs |
91 | * to accept one parameter (username of the previous mentor). Additional parameters can be |
92 | * passed via $reassignMessageAdditionalParams. |
93 | * @param mixed ...$reassignMessageAdditionalParams |
94 | */ |
95 | public function reassignMentees( |
96 | string $reassignMessageKey, |
97 | ...$reassignMessageAdditionalParams |
98 | ): void { |
99 | // checking if any mentees exist is a cheap operation; do not submit a job if it is going |
100 | // to be a no-op. |
101 | if ( $this->mentorStore->hasAnyMentees( $this->mentor, MentorStore::ROLE_PRIMARY ) ) { |
102 | $this->jobQueueGroupFactory->makeJobQueueGroup()->lazyPush( |
103 | new ReassignMenteesJob( [ |
104 | 'mentorId' => $this->mentor->getId(), |
105 | 'performerId' => $this->performer->getId(), |
106 | 'reassignMessageKey' => $reassignMessageKey, |
107 | 'reassignMessageAdditionalParams' => $reassignMessageAdditionalParams, |
108 | ] ) |
109 | ); |
110 | } |
111 | } |
112 | |
113 | /** |
114 | * Actually reassign all mentees currently assigned to the mentor |
115 | * |
116 | * @param string $reassignMessageKey Message key used in in ChangeMentor notification; needs |
117 | * to accept one parameter (username of the previous mentor). Additional parameters can be |
118 | * passed via $reassignMessageAdditionalParams. |
119 | * @param mixed ...$reassignMessageAdditionalParams |
120 | * @return bool True if successful, false otherwise. |
121 | */ |
122 | public function doReassignMentees( |
123 | string $reassignMessageKey, |
124 | ...$reassignMessageAdditionalParams |
125 | ): bool { |
126 | $lockName = 'GrowthExperiments-ReassignMentees-' . $this->mentor->getId() . |
127 | WikiMap::getCurrentWikiId(); |
128 | if ( !$this->dbw->lock( $lockName, __METHOD__, 0 ) ) { |
129 | $this->logger->warning( |
130 | __METHOD__ . ' failed to acquire a lock' |
131 | ); |
132 | return false; |
133 | } |
134 | |
135 | // only process primary mentors (T309984). Backup mentors will be automatically ignored by |
136 | // MentorPageMentorManager::getMentorForUser and replaced with a valid mentor if needed |
137 | $mentees = $this->mentorStore->getMenteesByMentor( $this->mentor, MentorStore::ROLE_PRIMARY ); |
138 | foreach ( $mentees as $mentee ) { |
139 | $changeMentor = $this->changeMentorFactory->newChangeMentor( |
140 | $mentee, |
141 | $this->performer |
142 | ); |
143 | |
144 | try { |
145 | $newMentor = $this->mentorManager->getRandomAutoAssignedMentor( $mentee ); |
146 | } catch ( WikiConfigException $e ) { |
147 | $this->logger->warning( |
148 | 'ReassignMentees failed to reassign mentees for {mentor}; mentor list is invalid', |
149 | [ |
150 | 'mentor' => $this->mentor->getName() |
151 | ] |
152 | ); |
153 | return false; |
154 | } |
155 | |
156 | if ( !$newMentor ) { |
157 | $this->logger->warning( |
158 | 'ReassignMentees failed to reassign mentees for {mentor}; no mentor is available', |
159 | [ |
160 | 'mentor' => $this->mentor->getName(), |
161 | 'impact' => 'Mentor-mentee relationship dropped' |
162 | ] |
163 | ); |
164 | $this->mentorStore->dropMenteeRelationship( $mentee ); |
165 | continue; |
166 | } |
167 | |
168 | $status = $changeMentor->execute( |
169 | $newMentor, |
170 | $this->messageLocalizer->msg( |
171 | $reassignMessageKey, |
172 | $this->mentor->getName(), |
173 | ...$reassignMessageAdditionalParams |
174 | )->text(), |
175 | true |
176 | ); |
177 | if ( !$status->isOK() ) { |
178 | $this->logger->warning( |
179 | 'ReassignMentees failed to assign {mentor} as {user}\'s mentor for {reason}', |
180 | [ |
181 | 'mentor' => $newMentor->getName(), |
182 | 'user' => $mentee->getName(), |
183 | 'reason' => $this->statusFormatter->getWikiText( $status, [ 'lang' => 'en' ] ) |
184 | ] |
185 | ); |
186 | } |
187 | } |
188 | |
189 | $this->dbw->unlock( $lockName, __METHOD__ ); |
190 | return true; |
191 | } |
192 | } |