Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 73 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
MenteeOverviewDataUpdater | |
0.00% |
0 / 73 |
|
0.00% |
0 / 4 |
110 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
setBatchSize | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMentorProfilingInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
updateDataForMentor | |
0.00% |
0 / 65 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\MentorDashboard\MenteeOverview; |
4 | |
5 | use GrowthExperiments\Mentorship\Store\MentorStore; |
6 | use MediaWiki\Json\FormatJson; |
7 | use MediaWiki\User\Options\UserOptionsManager; |
8 | use MediaWiki\User\UserIdentity; |
9 | use Wikimedia\Rdbms\ILoadBalancer; |
10 | use Wikimedia\Rdbms\LBFactory; |
11 | |
12 | /** |
13 | * Updates growthexperiments_mentee_data |
14 | * |
15 | * WARNING: This class may submit heavy queries that take minutes (or hours) |
16 | * to complete. It may only be used from CLI scripts or MediaWiki jobs. |
17 | */ |
18 | class MenteeOverviewDataUpdater { |
19 | public const LAST_UPDATE_PREFERENCE = 'growthexperiments-mentor-dashboard-last-update'; |
20 | |
21 | private UncachedMenteeOverviewDataProvider $uncachedMenteeOverviewDataProvider; |
22 | private MenteeOverviewDataProvider $menteeOverviewDataProvider; |
23 | private MentorStore $mentorStore; |
24 | private UserOptionsManager $userOptionsManager; |
25 | private LBFactory $lbFactory; |
26 | private ILoadBalancer $growthLoadBalancer; |
27 | private int $batchSize = 200; |
28 | private array $mentorProfilingInfo = []; |
29 | |
30 | /** |
31 | * @param UncachedMenteeOverviewDataProvider $uncachedMenteeOverviewDataProvider |
32 | * @param MenteeOverviewDataProvider $menteeOverviewDataProvider |
33 | * @param MentorStore $mentorStore |
34 | * @param UserOptionsManager $userOptionsManager |
35 | * @param LBFactory $lbFactory |
36 | * @param ILoadBalancer $growthLoadBalancer |
37 | */ |
38 | public function __construct( |
39 | UncachedMenteeOverviewDataProvider $uncachedMenteeOverviewDataProvider, |
40 | MenteeOverviewDataProvider $menteeOverviewDataProvider, |
41 | MentorStore $mentorStore, |
42 | UserOptionsManager $userOptionsManager, |
43 | LBFactory $lbFactory, |
44 | ILoadBalancer $growthLoadBalancer |
45 | ) { |
46 | $this->uncachedMenteeOverviewDataProvider = $uncachedMenteeOverviewDataProvider; |
47 | $this->menteeOverviewDataProvider = $menteeOverviewDataProvider; |
48 | $this->mentorStore = $mentorStore; |
49 | $this->userOptionsManager = $userOptionsManager; |
50 | $this->lbFactory = $lbFactory; |
51 | $this->growthLoadBalancer = $growthLoadBalancer; |
52 | } |
53 | |
54 | /** |
55 | * @param int $batchSize |
56 | */ |
57 | public function setBatchSize( int $batchSize ) { |
58 | $this->batchSize = $batchSize; |
59 | } |
60 | |
61 | /** |
62 | * @return array |
63 | */ |
64 | public function getMentorProfilingInfo(): array { |
65 | return $this->mentorProfilingInfo; |
66 | } |
67 | |
68 | /** |
69 | * @param UserIdentity $mentor |
70 | * @return int[] List of IDs this function updated |
71 | */ |
72 | public function updateDataForMentor( UserIdentity $mentor ): array { |
73 | $this->mentorProfilingInfo = []; |
74 | |
75 | $thisBatch = 0; |
76 | |
77 | $dbw = $this->growthLoadBalancer->getConnection( DB_PRIMARY ); |
78 | $dbr = $this->growthLoadBalancer->getConnection( DB_REPLICA ); |
79 | |
80 | $data = $this->uncachedMenteeOverviewDataProvider->getFormattedDataForMentor( $mentor ); |
81 | $mentees = $this->mentorStore->getMenteesByMentor( $mentor, MentorStore::ROLE_PRIMARY ); |
82 | $updatedMenteeIds = []; |
83 | foreach ( $data as $menteeId => $menteeData ) { |
84 | $encodedData = FormatJson::encode( $menteeData ); |
85 | $storedEncodedData = $dbr->newSelectQueryBuilder() |
86 | ->select( 'mentee_data' ) |
87 | ->from( 'growthexperiments_mentee_data' ) |
88 | ->where( [ 'mentee_id' => $menteeId ] ) |
89 | ->caller( __METHOD__ )->fetchField(); |
90 | if ( $storedEncodedData === false ) { |
91 | // Row doesn't exist yet, insert it |
92 | $dbw->newInsertQueryBuilder() |
93 | ->insertInto( 'growthexperiments_mentee_data' ) |
94 | ->row( [ |
95 | 'mentee_id' => $menteeId, |
96 | 'mentee_data' => $encodedData |
97 | ] ) |
98 | ->caller( __METHOD__ ) |
99 | ->execute(); |
100 | } else { |
101 | // Row exists, if anything changed, update |
102 | if ( FormatJson::decode( $storedEncodedData, true ) !== $menteeData ) { |
103 | $dbw->newUpdateQueryBuilder() |
104 | ->update( 'growthexperiments_mentee_data' ) |
105 | ->set( [ 'mentee_data' => $encodedData ] ) |
106 | ->where( [ 'mentee_id' => $menteeId ] ) |
107 | ->caller( __METHOD__ ) |
108 | ->execute(); |
109 | } |
110 | } |
111 | |
112 | $thisBatch++; |
113 | $updatedMenteeIds[] = $menteeId; |
114 | |
115 | if ( $thisBatch >= $this->batchSize ) { |
116 | $thisBatch = 0; |
117 | $this->lbFactory->waitForReplication(); |
118 | $this->lbFactory->autoReconfigure(); |
119 | } |
120 | } |
121 | |
122 | // Delete all mentees of $mentor we did not update |
123 | $menteeIdsToDelete = array_diff( |
124 | array_map( |
125 | static function ( $mentee ) { |
126 | return $mentee->getId(); |
127 | }, |
128 | $mentees |
129 | ), |
130 | $updatedMenteeIds |
131 | ); |
132 | if ( $menteeIdsToDelete !== [] ) { |
133 | $dbw->newDeleteQueryBuilder() |
134 | ->deleteFrom( 'growthexperiments_mentee_data' ) |
135 | ->where( [ |
136 | 'mentee_id' => $menteeIdsToDelete |
137 | ] ) |
138 | ->caller( __METHOD__ ) |
139 | ->execute(); |
140 | $this->lbFactory->waitForReplication(); |
141 | } |
142 | |
143 | $this->mentorProfilingInfo = $this->uncachedMenteeOverviewDataProvider |
144 | ->getProfilingInfo(); |
145 | |
146 | // if applicable, clear cache for the mentor we just updated |
147 | if ( $this->menteeOverviewDataProvider instanceof DatabaseMenteeOverviewDataProvider ) { |
148 | $this->menteeOverviewDataProvider->invalidateCacheForMentor( $mentor ); |
149 | } |
150 | |
151 | // update the last update timestamp |
152 | $this->userOptionsManager->setOption( |
153 | $mentor, |
154 | self::LAST_UPDATE_PREFERENCE, |
155 | wfTimestamp( TS_MW ) |
156 | ); |
157 | $this->userOptionsManager->saveOptions( $mentor ); |
158 | |
159 | return $updatedMenteeIds; |
160 | } |
161 | } |