Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.12% |
32 / 34 |
|
80.00% |
8 / 10 |
CRAP | |
0.00% |
0 / 1 |
Task | |
94.12% |
32 / 34 |
|
80.00% |
8 / 10 |
13.03 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getToken | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setToken | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTaskType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTitle | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTopics | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTopicScores | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setTopics | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
toJsonArray | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
newFromJsonArray | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
3.01 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\NewcomerTasks\Task; |
4 | |
5 | use GrowthExperiments\NewcomerTasks\TaskType\TaskType; |
6 | use GrowthExperiments\NewcomerTasks\Topic\Topic; |
7 | use GrowthExperiments\Util; |
8 | use MediaWiki\Json\JsonUnserializable; |
9 | use MediaWiki\Json\JsonUnserializableTrait; |
10 | use MediaWiki\Json\JsonUnserializer; |
11 | use MediaWiki\Linker\LinkTarget; |
12 | use MediaWiki\Title\TitleValue; |
13 | |
14 | /** |
15 | * A single task recommendation. |
16 | * A Task specifies a page and the type of the task to perform on it. |
17 | */ |
18 | class Task implements JsonUnserializable { |
19 | |
20 | use JsonUnserializableTrait; |
21 | |
22 | /** @var TaskType */ |
23 | private $taskType; |
24 | |
25 | /** @var LinkTarget The page to edit. */ |
26 | private $title; |
27 | |
28 | /** @var Topic[] */ |
29 | private $topics = []; |
30 | |
31 | /** @var float[] Match scores associated to the topics in $topics, keyed by topic ID. */ |
32 | private $topicScores = []; |
33 | |
34 | /** @var string unique task identifier for analytics purposes */ |
35 | private $token; |
36 | |
37 | /** |
38 | * @param TaskType $taskType |
39 | * @param LinkTarget $title The page this task is about. |
40 | * @param string|null $token |
41 | */ |
42 | public function __construct( TaskType $taskType, LinkTarget $title, ?string $token = null ) { |
43 | $this->taskType = $taskType; |
44 | $this->title = $title; |
45 | $this->token = $token ?? Util::generateRandomToken(); |
46 | } |
47 | |
48 | /** |
49 | * @return string |
50 | */ |
51 | public function getToken(): string { |
52 | return $this->token; |
53 | } |
54 | |
55 | /** |
56 | * @param string $token |
57 | */ |
58 | public function setToken( string $token ): void { |
59 | $this->token = $token; |
60 | } |
61 | |
62 | /** |
63 | * @return TaskType |
64 | */ |
65 | public function getTaskType(): TaskType { |
66 | return $this->taskType; |
67 | } |
68 | |
69 | /** |
70 | * @return LinkTarget |
71 | */ |
72 | public function getTitle(): LinkTarget { |
73 | return $this->title; |
74 | } |
75 | |
76 | /** |
77 | * Topics of the underlying article. Depending on the how topics are implemented, this |
78 | * might be never set, even if the TaskSuggester otherwise supports topic search. |
79 | * @return Topic[] |
80 | */ |
81 | public function getTopics(): array { |
82 | return $this->topics; |
83 | } |
84 | |
85 | /** |
86 | * Get topic matching scores for each topic this task is in. |
87 | * @return float[] Topic ID => score |
88 | */ |
89 | public function getTopicScores(): array { |
90 | // Make sure the set of returned items always matches getTopics(). |
91 | $topicScores = []; |
92 | foreach ( $this->getTopics() as $topic ) { |
93 | $topicScores[$topic->getId()] = $this->topicScores[$topic->getId()] ?? 0; |
94 | } |
95 | return $topicScores; |
96 | } |
97 | |
98 | /** |
99 | * @param Topic[] $topics |
100 | * @param float[] $topicScores Match scores associated to the topics in $topics, |
101 | * keyed by topic ID. Keys are a subset of those in $topics. |
102 | */ |
103 | public function setTopics( array $topics, array $topicScores = [] ): void { |
104 | $this->topics = $topics; |
105 | $this->topicScores = $topicScores; |
106 | } |
107 | |
108 | /** @inheritDoc */ |
109 | protected function toJsonArray(): array { |
110 | # T312589 explicitly calling jsonSerialize() will be unnecessary |
111 | # in the future. |
112 | return [ |
113 | 'taskType' => $this->getTaskType()->jsonSerialize(), |
114 | 'title' => [ $this->getTitle()->getNamespace(), $this->getTitle()->getDBkey() ], |
115 | 'topics' => array_map( static function ( Topic $topic ) { |
116 | return $topic->jsonSerialize(); |
117 | }, $this->getTopics() ), |
118 | 'topicScores' => $this->getTopicScores(), |
119 | 'token' => $this->getToken() |
120 | ]; |
121 | } |
122 | |
123 | /** @inheritDoc */ |
124 | public static function newFromJsonArray( JsonUnserializer $unserializer, array $json ) { |
125 | # T312589: In the future JsonCodec will take care of unserializing |
126 | # the values in the $json array itself. |
127 | $taskType = $json['taskType'] instanceof TaskType ? |
128 | $json['taskType'] : |
129 | $unserializer->unserialize( $json['taskType'], TaskType::class ); |
130 | $title = new TitleValue( $json['title'][0], $json['title'][1] ); |
131 | $topics = array_map( static function ( $topic ) use ( $unserializer ) { |
132 | return $topic instanceof Topic ? $topic : |
133 | $unserializer->unserialize( $topic, Topic::class ); |
134 | }, $json['topics'] ); |
135 | |
136 | $task = new static( $taskType, $title, $json['token'] ?? null ); |
137 | $task->setTopics( $topics, $json['topicScores'] ); |
138 | return $task; |
139 | } |
140 | |
141 | } |