Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.08% covered (warning)
86.08%
68 / 79
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SuggestionGenerator
86.08% covered (warning)
86.08%
68 / 79
50.00% covered (danger)
50.00%
2 / 4
21.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 generateSuggestionsByItem
81.48% covered (warning)
81.48%
22 / 27
0.00% covered (danger)
0.00%
0 / 1
6.23
 generateSuggestionsByPropertyList
80.00% covered (warning)
80.00%
24 / 30
0.00% covered (danger)
0.00%
0 / 1
7.39
 filterSuggestions
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3namespace PropertySuggester;
4
5use InvalidArgumentException;
6use MediaWiki\Message\Message;
7use MediaWiki\Status\Status;
8use PropertySuggester\Suggesters\SuggesterEngine;
9use PropertySuggester\Suggesters\Suggestion;
10use Wikibase\DataModel\Entity\Item;
11use Wikibase\DataModel\Entity\ItemId;
12use Wikibase\DataModel\Entity\NumericPropertyId;
13use Wikibase\DataModel\Services\Lookup\EntityLookup;
14use Wikibase\DataModel\Services\Lookup\UnresolvedEntityRedirectException;
15use Wikibase\Repo\Api\EntitySearchException;
16use Wikibase\Repo\Api\EntitySearchHelper;
17
18/**
19 * API module helper to generate property suggestions.
20 *
21 * @author BP2013N2
22 * @license GPL-2.0-or-later
23 */
24class SuggestionGenerator {
25
26    public function __construct(
27        private readonly EntityLookup $entityLookup,
28        private readonly EntitySearchHelper $entityTermSearchHelper,
29        private readonly SuggesterEngine $suggester,
30        private readonly ?SuggesterEngine $fallbackSuggester = null,
31    ) {
32    }
33
34    /**
35     * @param string $itemIdString
36     * @param int $limit
37     * @param float $minProbability
38     * @param string $context
39     * @param string $include One of the SuggesterEngine::SUGGEST_* constants
40     * @return Status containing Suggestion[]
41     */
42    public function generateSuggestionsByItem(
43        $itemIdString,
44        $limit,
45        $minProbability,
46        $context,
47        $include
48    ): Status {
49        try {
50            $itemId = new ItemId( $itemIdString );
51        } catch ( InvalidArgumentException ) {
52            return Status::newFatal( 'wikibase-api-invalid-entity-id' );
53        }
54        /** @var Item $item */
55        try {
56            $item = $this->entityLookup->getEntity( $itemId );
57        } catch ( UnresolvedEntityRedirectException ) {
58            return Status::newFatal( 'wikibase-api-unresolved-redirect' );
59        }
60
61        if ( $item === null ) {
62            return Status::newFatal( 'wikibase-api-no-such-entity',
63                Message::plaintextParam( $itemIdString ) );
64        }
65        '@phan-var Item $item';
66
67        $suggestions = $this->suggester->suggestByItem(
68            $item,
69            $limit,
70            $minProbability,
71            $context,
72            $include
73        );
74
75        if ( $suggestions === null ) {
76            if ( $this->fallbackSuggester === null ) {
77                return Status::newGood( [] );
78            }
79            $suggestions = $this->fallbackSuggester->suggestByItem(
80                $item,
81                $limit,
82                $minProbability,
83                $context,
84                $include
85            );
86        }
87        return Status::newGood( $suggestions );
88    }
89
90    /**
91     * @param string[] $propertyIdList A list of property-id-strings
92     * @param string[] $typesIdList A list of types-id-strings
93     * @param int $limit
94     * @param float $minProbability
95     * @param string $context
96     * @param string $include One of the SuggesterEngine::SUGGEST_* constants
97     * @return Status containing Suggestion[]
98     */
99    public function generateSuggestionsByPropertyList(
100        array $propertyIdList,
101        array $typesIdList,
102        $limit,
103        $minProbability,
104        $context,
105        $include
106    ): Status {
107        $propertyIds = [];
108        foreach ( $propertyIdList as $stringId ) {
109            try {
110                $propertyIds[] = new NumericPropertyId( $stringId );
111            } catch ( InvalidArgumentException ) {
112                return Status::newFatal( 'wikibase-api-invalid-property-id' );
113            }
114        }
115
116        $typesIds = [];
117        foreach ( $typesIdList as $stringId ) {
118            try {
119                $typesIds[] = new ItemId( $stringId );
120            } catch ( InvalidArgumentException ) {
121                return Status::newFatal( 'wikibase-api-invalid-entity-id' );
122            }
123        }
124
125        $suggestions = $this->suggester->suggestByPropertyIds(
126            $propertyIds,
127            $typesIds,
128            $limit,
129            $minProbability,
130            $context,
131            $include
132        );
133
134        if ( $suggestions === null ) {
135            if ( $this->fallbackSuggester === null ) {
136                return Status::newGood( [] );
137            }
138            $suggestions = $this->fallbackSuggester->suggestByPropertyIds(
139                $propertyIds,
140                $typesIds,
141                $limit,
142                $minProbability,
143                $context,
144                $include
145            );
146        }
147
148        return Status::newGood( $suggestions );
149    }
150
151    /**
152     * @param Suggestion[] $suggestions
153     * @param string $search
154     * @param string $language
155     * @param int $resultSize
156     * @return Suggestion[]
157     * @throws EntitySearchException
158     */
159    public function filterSuggestions( array $suggestions, $search, $language, $resultSize ) {
160        if ( !$search ) {
161            return array_slice( $suggestions, 0, $resultSize );
162        }
163
164        $searchResults = $this->entityTermSearchHelper->getRankedSearchResults(
165            $search,
166            $language,
167            'property',
168            $resultSize,
169            true,
170            null
171        );
172
173        $id_set = [];
174        foreach ( $searchResults as $searchResult ) {
175            // @phan-suppress-next-next-line PhanUndeclaredMethod getEntityId() returns PropertyId
176            // as requested above and that implements getNumericId()
177            $id_set[$searchResult->getEntityId()->getNumericId()] = true;
178        }
179
180        $matching_suggestions = [];
181        $count = 0;
182        foreach ( $suggestions as $suggestion ) {
183            if ( array_key_exists( $suggestion->getPropertyId()->getNumericId(), $id_set ) ) {
184                $matching_suggestions[] = $suggestion;
185                if ( ++$count === $resultSize ) {
186                    break;
187                }
188            }
189        }
190        return $matching_suggestions;
191    }
192
193}