Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.55% |
68 / 69 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
DiffWithinRangeChecker | |
98.55% |
68 / 69 |
|
80.00% |
4 / 5 |
28 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getSupportedContextTypes | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
getDefaultContextTypes | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
getSupportedEntityTypes | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
parseConstraintParameters | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
rangeInYears | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
5.12 | |||
checkConstraint | |
100.00% |
35 / 35 |
|
100.00% |
1 / 1 |
15 | |||
checkConstraintParameters | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | |
3 | namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker; |
4 | |
5 | use DataValues\QuantityValue; |
6 | use MediaWiki\Config\Config; |
7 | use Wikibase\DataModel\Entity\NumericPropertyId; |
8 | use Wikibase\DataModel\Snak\PropertyValueSnak; |
9 | use Wikibase\DataModel\Statement\Statement; |
10 | use WikibaseQuality\ConstraintReport\Constraint; |
11 | use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker; |
12 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context; |
13 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException; |
14 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser; |
15 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\RangeCheckerHelper; |
16 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage; |
17 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult; |
18 | use WikibaseQuality\ConstraintReport\Role; |
19 | |
20 | /** |
21 | * @author BP2014N1 |
22 | * @license GPL-2.0-or-later |
23 | */ |
24 | class DiffWithinRangeChecker implements ConstraintChecker { |
25 | |
26 | /** |
27 | * @var ConstraintParameterParser |
28 | */ |
29 | private $constraintParameterParser; |
30 | |
31 | /** |
32 | * @var RangeCheckerHelper |
33 | */ |
34 | private $rangeCheckerHelper; |
35 | |
36 | /** |
37 | * @var Config |
38 | */ |
39 | private $config; |
40 | |
41 | public function __construct( |
42 | ConstraintParameterParser $constraintParameterParser, |
43 | RangeCheckerHelper $rangeCheckerHelper, |
44 | Config $config |
45 | ) { |
46 | $this->constraintParameterParser = $constraintParameterParser; |
47 | $this->rangeCheckerHelper = $rangeCheckerHelper; |
48 | $this->config = $config; |
49 | } |
50 | |
51 | /** |
52 | * @codeCoverageIgnore This method is purely declarative. |
53 | */ |
54 | public function getSupportedContextTypes() { |
55 | return self::ALL_CONTEXT_TYPES_SUPPORTED; |
56 | } |
57 | |
58 | /** |
59 | * @codeCoverageIgnore This method is purely declarative. |
60 | */ |
61 | public function getDefaultContextTypes() { |
62 | return Context::ALL_CONTEXT_TYPES; |
63 | } |
64 | |
65 | /** @codeCoverageIgnore This method is purely declarative. */ |
66 | public function getSupportedEntityTypes() { |
67 | return self::ALL_ENTITY_TYPES_SUPPORTED; |
68 | } |
69 | |
70 | /** |
71 | * @param Constraint $constraint |
72 | * |
73 | * @throws ConstraintParameterException |
74 | * @return array [ DataValue|null $min, DataValue|null $max, NumericPropertyId $property ] |
75 | */ |
76 | private function parseConstraintParameters( Constraint $constraint ) { |
77 | [ $min, $max ] = $this->constraintParameterParser->parseQuantityRangeParameter( |
78 | $constraint->getConstraintParameters(), |
79 | $constraint->getConstraintTypeItemId() |
80 | ); |
81 | $property = $this->constraintParameterParser->parsePropertyParameter( |
82 | $constraint->getConstraintParameters(), |
83 | $constraint->getConstraintTypeItemId() |
84 | ); |
85 | |
86 | return [ $min, $max, $property ]; |
87 | } |
88 | |
89 | /** |
90 | * Check whether the endpoints of a range are in years or not. |
91 | * @param QuantityValue|null $min |
92 | * @param QuantityValue|null $max |
93 | * |
94 | * @return bool |
95 | */ |
96 | private function rangeInYears( $min, $max ) { |
97 | $yearUnit = $this->config->get( 'WBQualityConstraintsYearUnit' ); |
98 | |
99 | if ( $min !== null && $min->getUnit() === $yearUnit ) { |
100 | return true; |
101 | } |
102 | if ( $max !== null && $max->getUnit() === $yearUnit ) { |
103 | return true; |
104 | } |
105 | |
106 | return false; |
107 | } |
108 | |
109 | /** |
110 | * Checks 'Diff within range' constraint. |
111 | * |
112 | * @param Context $context |
113 | * @param Constraint $constraint |
114 | * |
115 | * @throws ConstraintParameterException |
116 | * @return CheckResult |
117 | */ |
118 | public function checkConstraint( Context $context, Constraint $constraint ) { |
119 | if ( $context->getSnakRank() === Statement::RANK_DEPRECATED ) { |
120 | return new CheckResult( $context, $constraint, CheckResult::STATUS_DEPRECATED ); |
121 | } |
122 | |
123 | $snak = $context->getSnak(); |
124 | |
125 | if ( !$snak instanceof PropertyValueSnak ) { |
126 | // nothing to check |
127 | return new CheckResult( $context, $constraint, CheckResult::STATUS_COMPLIANCE ); |
128 | } |
129 | |
130 | $minuend = $snak->getDataValue(); |
131 | '@phan-var \DataValues\TimeValue|\DataValues\QuantityValue|\DataValues\UnboundedQuantityValue $minuend'; |
132 | |
133 | /** @var NumericPropertyId $property */ |
134 | [ $min, $max, $property ] = $this->parseConstraintParameters( $constraint ); |
135 | |
136 | // checks only the first occurrence of the referenced property |
137 | foreach ( $context->getSnakGroup( Context::GROUP_NON_DEPRECATED ) as $otherSnak ) { |
138 | if ( |
139 | !$property->equals( $otherSnak->getPropertyId() ) || |
140 | !$otherSnak instanceof PropertyValueSnak |
141 | ) { |
142 | continue; |
143 | } |
144 | |
145 | $subtrahend = $otherSnak->getDataValue(); |
146 | '@phan-var \DataValues\TimeValue|\DataValues\QuantityValue|\DataValues\UnboundedQuantityValue $subtrahend'; |
147 | if ( $subtrahend->getType() === $minuend->getType() ) { |
148 | $diff = $this->rangeInYears( $min, $max ) && $minuend->getType() === 'time' ? |
149 | $this->rangeCheckerHelper->getDifferenceInYears( $minuend, $subtrahend ) : |
150 | $this->rangeCheckerHelper->getDifference( $minuend, $subtrahend ); |
151 | |
152 | if ( $this->rangeCheckerHelper->getComparison( $min, $diff ) > 0 || |
153 | $this->rangeCheckerHelper->getComparison( $diff, $max ) > 0 |
154 | ) { |
155 | // at least one of $min, $max is set at this point, otherwise there could be no violation |
156 | $openness = $min !== null ? ( $max !== null ? '' : '-rightopen' ) : '-leftopen'; |
157 | // possible message keys: |
158 | // wbqc-violation-message-diff-within-range |
159 | // wbqc-violation-message-diff-within-range-leftopen |
160 | // wbqc-violation-message-diff-within-range-rightopen |
161 | $message = ( new ViolationMessage( "wbqc-violation-message-diff-within-range$openness" ) ) |
162 | ->withEntityId( $context->getSnak()->getPropertyId(), Role::PREDICATE ) |
163 | ->withDataValue( $minuend, Role::OBJECT ) |
164 | ->withEntityId( $otherSnak->getPropertyId(), Role::PREDICATE ) |
165 | ->withDataValue( $subtrahend, Role::OBJECT ); |
166 | if ( $min !== null ) { |
167 | $message = $message->withDataValue( $min, Role::OBJECT ); |
168 | } |
169 | if ( $max !== null ) { |
170 | $message = $message->withDataValue( $max, Role::OBJECT ); |
171 | } |
172 | $status = CheckResult::STATUS_VIOLATION; |
173 | } else { |
174 | $message = null; |
175 | $status = CheckResult::STATUS_COMPLIANCE; |
176 | } |
177 | } else { |
178 | $message = new ViolationMessage( 'wbqc-violation-message-diff-within-range-must-have-equal-types' ); |
179 | $status = CheckResult::STATUS_VIOLATION; |
180 | } |
181 | |
182 | return new CheckResult( $context, $constraint, $status, $message ); |
183 | } |
184 | |
185 | return new CheckResult( $context, $constraint, CheckResult::STATUS_COMPLIANCE ); |
186 | } |
187 | |
188 | public function checkConstraintParameters( Constraint $constraint ) { |
189 | $constraintParameters = $constraint->getConstraintParameters(); |
190 | $constraintTypeItemId = $constraint->getConstraintTypeItemId(); |
191 | $exceptions = []; |
192 | try { |
193 | $this->constraintParameterParser->parseQuantityRangeParameter( |
194 | $constraintParameters, |
195 | $constraintTypeItemId |
196 | ); |
197 | } catch ( ConstraintParameterException $e ) { |
198 | $exceptions[] = $e; |
199 | } |
200 | try { |
201 | $this->constraintParameterParser->parsePropertyParameter( |
202 | $constraintParameters, |
203 | $constraintTypeItemId |
204 | ); |
205 | } catch ( ConstraintParameterException $e ) { |
206 | $exceptions[] = $e; |
207 | } |
208 | return $exceptions; |
209 | } |
210 | |
211 | } |