Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 44 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
LinkSubmissionRecorder | |
0.00% |
0 / 44 |
|
0.00% |
0 / 4 |
56 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
record | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
6 | |||
log | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
6 | |||
titlesToPageIds | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\NewcomerTasks\AddLink; |
4 | |
5 | use ManualLogEntry; |
6 | use MediaWiki\Cache\LinkBatchFactory; |
7 | use MediaWiki\Title\TitleParser; |
8 | use MediaWiki\User\UserIdentity; |
9 | use StatusValue; |
10 | use Wikimedia\Rdbms\IDBAccessObject; |
11 | |
12 | /** |
13 | * Record information about a user accepting/rejecting parts of a link recommendation. |
14 | * This involves creating a log entry and updating an article-specific exclusion list. |
15 | */ |
16 | class LinkSubmissionRecorder { |
17 | |
18 | /** @var TitleParser */ |
19 | private $titleParser; |
20 | |
21 | /** @var LinkBatchFactory */ |
22 | private $linkBatchFactory; |
23 | |
24 | /** @var LinkRecommendationStore */ |
25 | private $linkRecommendationStore; |
26 | |
27 | /** |
28 | * @param TitleParser $titleParser |
29 | * @param LinkBatchFactory $linkBatchFactory |
30 | * @param LinkRecommendationStore $linkRecommendationStore |
31 | */ |
32 | public function __construct( |
33 | TitleParser $titleParser, |
34 | LinkBatchFactory $linkBatchFactory, |
35 | LinkRecommendationStore $linkRecommendationStore |
36 | ) { |
37 | $this->titleParser = $titleParser; |
38 | $this->linkBatchFactory = $linkBatchFactory; |
39 | $this->linkRecommendationStore = $linkRecommendationStore; |
40 | } |
41 | |
42 | /** |
43 | * Record the results of a user reviewing a link recommendation. |
44 | * @param UserIdentity $user |
45 | * @param LinkRecommendation $linkRecommendation |
46 | * @param string[] $acceptedTargets |
47 | * @param string[] $rejectedTargets |
48 | * @param string[] $skippedTargets |
49 | * @param int|null $editRevId Revision ID of the edit adding the links (might be null since |
50 | * it's not necessary that any links have been added). |
51 | * @return StatusValue |
52 | */ |
53 | public function record( |
54 | UserIdentity $user, |
55 | LinkRecommendation $linkRecommendation, |
56 | array $acceptedTargets, |
57 | array $rejectedTargets, |
58 | array $skippedTargets, |
59 | ?int $editRevId |
60 | ): StatusValue { |
61 | if ( $this->linkRecommendationStore->hasSubmission( $linkRecommendation, |
62 | IDBAccessObject::READ_LOCKING ) |
63 | ) { |
64 | // There's already a review for this revision. Possibly a race condition where two |
65 | // users reviewed the same task at the same time. |
66 | return StatusValue::newGood()->error( 'growthexperiments-addlink-duplicatesubmission', |
67 | $linkRecommendation->getRevisionId() ); |
68 | } |
69 | $this->linkRecommendationStore->recordSubmission( |
70 | $user, |
71 | $linkRecommendation, |
72 | $this->titlesToPageIds( $acceptedTargets ), |
73 | $this->titlesToPageIds( $rejectedTargets ), |
74 | $this->titlesToPageIds( $skippedTargets ), |
75 | $editRevId |
76 | ); |
77 | $logId = $this->log( |
78 | $user, |
79 | $linkRecommendation, |
80 | count( $acceptedTargets ), |
81 | count( $rejectedTargets ), |
82 | count( $skippedTargets ), |
83 | $editRevId |
84 | ); |
85 | return StatusValue::newGood( [ 'logId' => $logId ] ); |
86 | } |
87 | |
88 | /** |
89 | * Make a Special:Log entry. |
90 | * @param UserIdentity $user |
91 | * @param LinkRecommendation $linkRecommendation |
92 | * @param int $acceptedLinkCount |
93 | * @param int $rejectedLinkCount |
94 | * @param int $skippedLinkCount |
95 | * @return int Log ID. |
96 | * @param int|null $editRevId Revision ID of the edit adding the links (might be null since |
97 | * it's not necessary that any links have been added). |
98 | */ |
99 | private function log( |
100 | UserIdentity $user, |
101 | LinkRecommendation $linkRecommendation, |
102 | int $acceptedLinkCount, |
103 | int $rejectedLinkCount, |
104 | int $skippedLinkCount, |
105 | ?int $editRevId |
106 | ): int { |
107 | $totalLinkCount = $acceptedLinkCount + $rejectedLinkCount + $skippedLinkCount; |
108 | |
109 | $logEntry = new ManualLogEntry( 'growthexperiments', 'addlink' ); |
110 | $logEntry->setTarget( $linkRecommendation->getTitle() ); |
111 | $logEntry->setPerformer( $user ); |
112 | $logEntry->setParameters( [ |
113 | '4:number:count' => $totalLinkCount, |
114 | '5:number:count' => $acceptedLinkCount, |
115 | '6:number:count' => $rejectedLinkCount, |
116 | '7:number:count' => $skippedLinkCount, |
117 | ] ); |
118 | if ( $editRevId ) { |
119 | // This has the side effect of the log entry getting tagged with all the change tags |
120 | // the revision is getting tagged with. Overall, still preferable - the log entry is |
121 | // not published to recent changes so its tags don't matter much. |
122 | $logEntry->setAssociatedRevId( $editRevId ); |
123 | } |
124 | $logId = $logEntry->insert(); |
125 | // Do not publish to recent changes, it would be pointless as this action cannot |
126 | // be inspected or patrolled. |
127 | $logEntry->publish( $logId, 'udp' ); |
128 | return $logId; |
129 | } |
130 | |
131 | /** |
132 | * Converts title strings to page IDs. Non-existent pages are omitted. |
133 | * @param string[] $titles |
134 | * @return int[] |
135 | */ |
136 | private function titlesToPageIds( array $titles ): array { |
137 | $linkBatch = $this->linkBatchFactory->newLinkBatch(); |
138 | foreach ( $titles as $title ) { |
139 | // ensuring that the title is valid is left to the caller |
140 | $linkBatch->addObj( $this->titleParser->parseTitle( $title ) ); |
141 | } |
142 | $ids = $linkBatch->execute(); |
143 | // LinkBatch::execute() returns a title => ID map. Discard titles, discard |
144 | // 0 ID used for non-existent pages (we assume those won't be recommended anyway), |
145 | // squash duplicates (just in case; they shouldn't exist). |
146 | return array_unique( array_filter( array_values( $ids ) ) ); |
147 | } |
148 | |
149 | } |