Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.72% covered (danger)
48.72%
38 / 78
41.67% covered (danger)
41.67%
10 / 24
CRAP
0.00% covered (danger)
0.00%
0 / 1
TaskType
48.72% covered (danger)
48.72%
38 / 78
41.67% covered (danger)
41.67%
10 / 24
109.29
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHandlerId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setHandlerId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDifficulty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLearnMoreLink
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExcludedTemplates
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExcludedCategories
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getShortDescription
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getLabel
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getTimeEstimate
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getViewData
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 getIconData
28.57% covered (danger)
28.57%
2 / 7
0.00% covered (danger)
0.00%
0 / 1
3.46
 toJsonArray
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 newFromJsonArray
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 getExcludedTemplatesTitleValues
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getExcludedCategoriesTitleValues
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 shouldOpenInEditMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultEditSection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSmallTaskCardImageCssClasses
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQualityGateIds
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSuggestionFilters
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GrowthExperiments\NewcomerTasks\TaskType;
4
5use GrowthExperiments\NewcomerTasks\TaskSuggester\QualityGateDecorator;
6use MediaWiki\Json\JsonUnserializable;
7use MediaWiki\Json\JsonUnserializableTrait;
8use MediaWiki\Json\JsonUnserializer;
9use MediaWiki\Linker\LinkTarget;
10use MediaWiki\Title\TitleValue;
11use Message;
12use MessageLocalizer;
13
14/**
15 * Describes a type of suggested edit.
16 */
17class TaskType implements JsonUnserializable {
18
19    use JsonUnserializableTrait;
20
21    public const DIFFICULTY_EASY = 'easy';
22    public const DIFFICULTY_MEDIUM = 'medium';
23    public const DIFFICULTY_HARD = 'hard';
24    public const DIFFICULTY_NUMERIC = [
25        1 => self::DIFFICULTY_EASY,
26        2 => self::DIFFICULTY_MEDIUM,
27        3 => self::DIFFICULTY_HARD
28    ];
29
30    public const DIFFICULTY_CLASSES = [
31        self::DIFFICULTY_EASY,
32        self::DIFFICULTY_MEDIUM,
33        self::DIFFICULTY_HARD,
34    ];
35
36    /** Whether this is a task type generated by machine */
37    protected const IS_MACHINE_SUGGESTION = false;
38
39    /** @var string Task type ID, e.g. 'copyedit'. */
40    protected $id;
41
42    /** @var string TaskTypeHandler ID. */
43    protected $handlerId;
44
45    /** @var string Task type difficulty class, one of the DIFFICULTY_* constants. */
46    protected $difficulty;
47
48    /** @var string|null Page name to point the "learn more" link to. */
49    protected $learnMoreLink;
50
51    /** @var LinkTarget[] List of templates that prevent an article from being identified with this task type. */
52    private $excludedTemplates;
53
54    /** @var LinkTarget[] List of categories that prevent an article from being identified with this task type. */
55    private $excludedCategories;
56
57    /**
58     * @param string $id Task type ID, e.g. 'copyedit'.
59     * @param string $difficulty One of the DIFFICULTY_* constants.
60     * @param array $extraData Optional pieces of information
61     *   - 'learnMoreLink' (string): Page title for the "learn more" link for this task type.
62     * @param LinkTarget[] $excludedTemplates List of templates that prevent an article from being identified with
63     *  this task type.
64     * @param LinkTarget[] $excludedCategories List of cateogires that prevent an article from being identified with
65     *  this task type.
66     */
67    public function __construct(
68        $id, $difficulty, array $extraData = [], array $excludedTemplates = [], array $excludedCategories = []
69    ) {
70        $this->id = $id;
71        $this->difficulty = $difficulty;
72        $this->learnMoreLink = $extraData['learnMoreLink'] ?? null;
73        $this->excludedTemplates = $excludedTemplates;
74        $this->excludedCategories = $excludedCategories;
75    }
76
77    /**
78     * Task type ID, e.g. 'copyedit'.
79     * @return string
80     */
81    public function getId() {
82        return $this->id;
83    }
84
85    /**
86     * @return string
87     * @internal for use by TaskTypeHandlerRegistry only
88     */
89    public function getHandlerId() {
90        return $this->handlerId;
91    }
92
93    /**
94     * @param string $handlerId
95     * @internal for use by TaskTypeHandlerRegistry only
96     */
97    public function setHandlerId( $handlerId ) {
98        $this->handlerId = $handlerId;
99    }
100
101    /**
102     * One of the DIFFICULTY_* constants.
103     * @return string
104     */
105    public function getDifficulty() {
106        return $this->difficulty;
107    }
108
109    /**
110     * Page title for the "learn more" link for this task type.
111     * @return string|null
112     */
113    public function getLearnMoreLink() {
114        return $this->learnMoreLink;
115    }
116
117    /**
118     * @return LinkTarget[]
119     */
120    public function getExcludedTemplates(): array {
121        return $this->excludedTemplates;
122    }
123
124    /**
125     * @return LinkTarget[]
126     */
127    public function getExcludedCategories(): array {
128        return $this->excludedCategories;
129    }
130
131    /**
132     * Human-readable name of the task type.
133     * @param MessageLocalizer $messageLocalizer
134     * @return Message
135     */
136    public function getName( MessageLocalizer $messageLocalizer ): Message {
137        return $messageLocalizer->msg( 'growthexperiments-homepage-suggestededits-tasktype-name-'
138            . $this->getId() );
139    }
140
141    /**
142     * Description of the task type.
143     * @param MessageLocalizer $messageLocalizer
144     * @return Message
145     */
146    public function getDescription( MessageLocalizer $messageLocalizer ): Message {
147        return $messageLocalizer->msg( 'growthexperiments-homepage-suggestededits-tasktype-description-'
148            . $this->getId() );
149    }
150
151    /**
152     * Short description of the task type.
153     * @param MessageLocalizer $messageLocalizer
154     * @return Message
155     */
156    public function getShortDescription( MessageLocalizer $messageLocalizer ): Message {
157        return $messageLocalizer->msg(
158            'growthexperiments-homepage-suggestededits-tasktype-shortdescription-' . $this->getId() );
159    }
160
161    /**
162     * Label for the task type; typically either the name, or a combination of the name and
163     * the short description.
164     * @param MessageLocalizer $messageLocalizer
165     * @return Message
166     */
167    public function getLabel( MessageLocalizer $messageLocalizer ): Message {
168        return $messageLocalizer->msg(
169            'growthexperiments-homepage-suggestededits-tasktype-label-' . $this->getId() );
170    }
171
172    /**
173     * Time estimate for the task type.
174     * @param MessageLocalizer $messageLocalizer
175     * @return Message
176     */
177    public function getTimeEstimate( MessageLocalizer $messageLocalizer ): Message {
178        return $messageLocalizer->msg( 'growthexperiments-homepage-suggestededits-tasktype-time-'
179            . $this->getId() );
180    }
181
182    /**
183     * Return an array (JSON-ish) representation of the task type.
184     * This is for the benefit of clients (contains details needed by a task UI) and cannot
185     * be used to recover the object.
186     * @param MessageLocalizer $messageLocalizer
187     * @return array
188     */
189    public function getViewData( MessageLocalizer $messageLocalizer ) {
190        $viewData = [
191            'id' => $this->getId(),
192            'difficulty' => $this->getDifficulty(),
193            'messages' => [
194                'name' => $this->getName( $messageLocalizer )->text(),
195                'description' => $this->getDescription( $messageLocalizer )->text(),
196                'shortdescription' => $this->getShortDescription( $messageLocalizer )->text(),
197                'label' => $this->getLabel( $messageLocalizer )->text(),
198                'timeestimate' => $this->getTimeEstimate( $messageLocalizer )->text(),
199            ],
200            'learnMoreLink' => $this->getLearnMoreLink(),
201            'iconData' => $this->getIconData()
202        ];
203        return $viewData;
204    }
205
206    /**
207     * Get icon data that should be shown for the task type
208     * @return array
209     */
210    public function getIconData(): array {
211        if ( static::IS_MACHINE_SUGGESTION ) {
212            return [
213                // The following classes are used here:
214                // * robot-task-type-easy
215                // * robot-task-type-medium
216                'icon' => 'robot-task-type-' . $this->getDifficulty(),
217                'filterIcon' => 'robot',
218                'descriptionMessageKey' => 'growthexperiments-homepage-suggestededits-tasktype-machine-description'
219            ];
220        }
221        return [];
222    }
223
224    /** @inheritDoc */
225    protected function toJsonArray(): array {
226        return [
227            'id' => $this->getId(),
228            'difficulty' => $this->getDifficulty(),
229            'extraData' => [ 'learnMoreLink' => $this->getLearnMoreLink() ],
230            'handlerId' => $this->getHandlerId(),
231            'iconData' => $this->getIconData(),
232            'excludedTemplates' => array_map( static function ( LinkTarget $excludedTemplate ) {
233                return [ $excludedTemplate->getNamespace(), $excludedTemplate->getDBkey() ];
234            }, $this->getExcludedTemplates() ),
235            'excludedCategories' => array_map( static function ( LinkTarget $excludedCategory ) {
236                return [ $excludedCategory->getNamespace(), $excludedCategory->getDBkey() ];
237            }, $this->getExcludedCategories() )
238        ];
239    }
240
241    /** @inheritDoc */
242    public static function newFromJsonArray( JsonUnserializer $unserializer, array $json ) {
243        $excludedTemplates = array_map( static function ( array $excludedTemplate ) {
244            return new TitleValue( $excludedTemplate[0], $excludedTemplate[1] );
245        }, $json['excludedTemplates'] ?? [] );
246        $excludedCategories = array_map( static function ( array $excludedCategory ) {
247            return new TitleValue( $excludedCategory[0], $excludedCategory[1] );
248        }, $json['excludedCategories'] ?? [] );
249        $taskType = new static(
250            $json['id'], $json['difficulty'], $json['extraData'], $excludedTemplates, $excludedCategories
251        );
252        $taskType->setHandlerId( $json['handlerId'] );
253        return $taskType;
254    }
255
256    /**
257     * @param array $config
258     * @return TitleValue[]
259     */
260    public static function getExcludedTemplatesTitleValues( array $config ): array {
261        return array_map( static function ( array $excludedTemplate ) {
262            return new TitleValue( $excludedTemplate[0], $excludedTemplate[1] );
263        }, $config['excludedTemplates'] ?? [] );
264    }
265
266    /**
267     * @param array $config
268     * @return TitleValue[]
269     */
270    public static function getExcludedCategoriesTitleValues( array $config ): array {
271        return array_map( static function ( array $excludedCategory ) {
272            return new TitleValue( $excludedCategory[0], $excludedCategory[1] );
273        }, $config['excludedCategories'] ?? [] );
274    }
275
276    /**
277     * Whether the corresponding article for the task type should be opened in edit mode
278     * @return bool
279     */
280    public function shouldOpenInEditMode(): bool {
281        return false;
282    }
283
284    /**
285     * Get the default edit section for the task type
286     * @return string
287     */
288    public function getDefaultEditSection(): string {
289        return '';
290    }
291
292    /**
293     * Get CSS classes to add to the small task card image element.
294     *
295     * @return array
296     */
297    public function getSmallTaskCardImageCssClasses(): array {
298        return [ 'mw-ge-small-task-card-image-skeleton' ];
299    }
300
301    /**
302     * The quality gate data for this task type.
303     *
304     * @see QualityGateDecorator and modules/ext.growthExperiments.Homepage.SuggestedEdits/QualityGate.js
305     * @return string[] An array of quality gate names that will be applied for the task type.
306     */
307    public function getQualityGateIds(): array {
308        return [];
309    }
310
311    /**
312     * Return the filters to apply to the recommendation
313     *
314     * @return array
315     */
316    public function getSuggestionFilters(): array {
317        return [];
318    }
319}