Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
93.44% |
57 / 61 |
|
33.33% |
2 / 6 |
CRAP | |
0.00% |
0 / 1 |
RangeCheckerHelper | |
93.44% |
57 / 61 |
|
33.33% |
2 / 6 |
25.18 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
standardize | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
getComparison | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
6.02 | |||
getDifference | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
5.01 | |||
getDifferenceInYears | |
95.45% |
21 / 22 |
|
0.00% |
0 / 1 |
9 | |||
isFutureTime | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Helper; |
4 | |
5 | use DataValues\DataValue; |
6 | use DataValues\QuantityValue; |
7 | use DataValues\TimeValue; |
8 | use DataValues\TimeValueCalculator; |
9 | use DataValues\UnboundedQuantityValue; |
10 | use InvalidArgumentException; |
11 | use MediaWiki\Config\Config; |
12 | use ValueParsers\IsoTimestampParser; |
13 | use ValueParsers\ValueParser; |
14 | use Wikibase\Lib\Units\UnitConverter; |
15 | |
16 | /** |
17 | * Class for helper functions for range checkers. |
18 | * |
19 | * @author BP2014N1 |
20 | * @license GPL-2.0-or-later |
21 | */ |
22 | class RangeCheckerHelper { |
23 | |
24 | /** |
25 | * @var Config |
26 | */ |
27 | private $config; |
28 | |
29 | /** |
30 | * @var ValueParser |
31 | */ |
32 | private $timeParser; |
33 | |
34 | /** |
35 | * @var TimeValueCalculator |
36 | */ |
37 | private $timeCalculator; |
38 | |
39 | /** |
40 | * @var TimeValueComparer |
41 | */ |
42 | private $timeValueComparer; |
43 | |
44 | /** |
45 | * @var UnitConverter|null |
46 | */ |
47 | private $unitConverter; |
48 | |
49 | public function __construct( |
50 | Config $config, |
51 | ?UnitConverter $unitConverter = null |
52 | ) { |
53 | $this->config = $config; |
54 | $this->timeParser = new IsoTimestampParser(); |
55 | $this->timeCalculator = new TimeValueCalculator(); |
56 | $this->timeValueComparer = new TimeValueComparer( $this->timeCalculator ); |
57 | $this->unitConverter = $unitConverter; |
58 | } |
59 | |
60 | /** |
61 | * @param UnboundedQuantityValue $value |
62 | * @return UnboundedQuantityValue $value converted to standard units if possible, otherwise unchanged $value. |
63 | */ |
64 | private function standardize( UnboundedQuantityValue $value ) { |
65 | if ( $this->unitConverter !== null ) { |
66 | $standard = $this->unitConverter->toStandardUnits( $value ); |
67 | if ( $standard !== null ) { |
68 | return $standard; |
69 | } else { |
70 | return $value; |
71 | } |
72 | } else { |
73 | return $value; |
74 | } |
75 | } |
76 | |
77 | /** |
78 | * Compare two values. |
79 | * If one of them is null, return 0 (equal). |
80 | * |
81 | * @param DataValue|null $lhs left-hand side |
82 | * @param DataValue|null $rhs right-hand side |
83 | * |
84 | * @throws InvalidArgumentException if the values do not both have the same, supported data value type |
85 | * @return integer An integer less than, equal to, or greater than zero |
86 | * when $lhs is respectively less than, equal to, or greater than $rhs. |
87 | * (In other words, just like the “spaceship” operator <=>.) |
88 | */ |
89 | public function getComparison( ?DataValue $lhs, ?DataValue $rhs ) { |
90 | if ( $lhs === null || $rhs === null ) { |
91 | return 0; |
92 | } |
93 | |
94 | if ( $lhs->getType() !== $rhs->getType() ) { |
95 | throw new InvalidArgumentException( 'Different data value types' ); |
96 | } |
97 | |
98 | switch ( $lhs->getType() ) { |
99 | case 'time': |
100 | /** @var TimeValue $lhs */ |
101 | /** @var TimeValue $rhs */ |
102 | '@phan-var TimeValue $lhs'; |
103 | '@phan-var TimeValue $rhs'; |
104 | return $this->timeValueComparer->getComparison( $lhs, $rhs ); |
105 | case 'quantity': |
106 | /** @var QuantityValue|UnboundedQuantityValue $lhs */ |
107 | /** @var QuantityValue|UnboundedQuantityValue $rhs */ |
108 | '@phan-var QuantityValue|UnboundedQuantityValue $lhs'; |
109 | '@phan-var QuantityValue|UnboundedQuantityValue $rhs'; |
110 | $lhsStandard = $this->standardize( $lhs ); |
111 | $rhsStandard = $this->standardize( $rhs ); |
112 | return $lhsStandard->getAmount()->compare( $rhsStandard->getAmount() ); |
113 | } |
114 | |
115 | throw new InvalidArgumentException( 'Unsupported data value type' ); |
116 | } |
117 | |
118 | /** |
119 | * Computes $minuend - $subtrahend, in a format depending on the data type. |
120 | * For time values, the difference is in seconds; |
121 | * for quantity values, the difference is the numerical difference between the quantities, |
122 | * after attempting normalization of each side. |
123 | * |
124 | * @param TimeValue|QuantityValue|UnboundedQuantityValue $minuend |
125 | * @param TimeValue|QuantityValue|UnboundedQuantityValue $subtrahend |
126 | * |
127 | * @throws InvalidArgumentException if the values do not both have the same, supported data value type |
128 | * @return UnboundedQuantityValue |
129 | */ |
130 | public function getDifference( DataValue $minuend, DataValue $subtrahend ) { |
131 | if ( $minuend->getType() === 'time' && $subtrahend->getType() === 'time' ) { |
132 | $minuendSeconds = $this->timeCalculator->getTimestamp( $minuend ); |
133 | $subtrahendSeconds = $this->timeCalculator->getTimestamp( $subtrahend ); |
134 | return UnboundedQuantityValue::newFromNumber( |
135 | $minuendSeconds - $subtrahendSeconds, |
136 | $this->config->get( 'WBQualityConstraintsSecondUnit' ) |
137 | ); |
138 | } |
139 | if ( $minuend->getType() === 'quantity' && $subtrahend->getType() === 'quantity' ) { |
140 | $minuendStandard = $this->standardize( $minuend ); |
141 | $subtrahendStandard = $this->standardize( $subtrahend ); |
142 | $minuendValue = $minuendStandard->getAmount()->getValueFloat(); |
143 | $subtrahendValue = $subtrahendStandard->getAmount()->getValueFloat(); |
144 | $diff = $minuendValue - $subtrahendValue; |
145 | // we don’t check whether both quantities have the same standard unit – |
146 | // that’s the job of a different constraint type, Units (T164372) |
147 | return UnboundedQuantityValue::newFromNumber( $diff, $minuendStandard->getUnit() ); |
148 | } |
149 | |
150 | throw new InvalidArgumentException( 'Unsupported or different data value types' ); |
151 | } |
152 | |
153 | public function getDifferenceInYears( TimeValue $minuend, TimeValue $subtrahend ) { |
154 | if ( !preg_match( '/^([-+]\d{1,16})-(.*)$/', $minuend->getTime(), $minuendMatches ) || |
155 | !preg_match( '/^([-+]\d{1,16})-(.*)$/', $subtrahend->getTime(), $subtrahendMatches ) |
156 | ) { |
157 | throw new InvalidArgumentException( 'TimeValue::getTime() did not match expected format' ); |
158 | } |
159 | $minuendYear = (float)$minuendMatches[1]; |
160 | $subtrahendYear = (float)$subtrahendMatches[1]; |
161 | $minuendRest = $minuendMatches[2]; |
162 | $subtrahendRest = $subtrahendMatches[2]; |
163 | |
164 | // calculate difference of years |
165 | $diff = $minuendYear - $subtrahendYear; |
166 | if ( $minuendYear > 0.0 && $subtrahendYear < 0.0 ) { |
167 | $diff -= 1.0; // there is no year 0, remove it from difference |
168 | } elseif ( $minuendYear < 0.0 && $subtrahendYear > 0.0 ) { |
169 | $diff -= -1.0; // there is no year 0, remove it from negative difference |
170 | } |
171 | |
172 | // adjust for date within year by parsing the month-day part within the same year |
173 | $minuendDateValue = $this->timeParser->parse( '+0000000000001970-' . $minuendRest ); |
174 | $subtrahendDateValue = $this->timeParser->parse( '+0000000000001970-' . $subtrahendRest ); |
175 | $minuendDateSeconds = $this->timeCalculator->getTimestamp( $minuendDateValue ); |
176 | $subtrahendDateSeconds = $this->timeCalculator->getTimestamp( $subtrahendDateValue ); |
177 | if ( $minuendDateSeconds < $subtrahendDateSeconds ) { |
178 | // difference in the last year is actually less than one full year |
179 | // e. g. 1975-03-01 - 1974-09-01 is just six months |
180 | // (we don’t need sub-year precision in the difference, adjusting by 0.5 is enough) |
181 | $diff -= 0.5; |
182 | } elseif ( $minuendDateSeconds > $subtrahendDateSeconds ) { |
183 | // difference in the last year is actually more than one full year |
184 | // e. g. 1975-09-01 - 1974-03-01 is 18 months |
185 | // (we don’t need sub-year precision in the difference, adjusting by 0.5 is enough) |
186 | $diff += 0.5; |
187 | } |
188 | |
189 | $unit = $this->config->get( 'WBQualityConstraintsYearUnit' ); |
190 | return UnboundedQuantityValue::newFromNumber( $diff, $unit ); |
191 | } |
192 | |
193 | public function isFutureTime( TimeValue $timeValue ) { |
194 | return $this->timeValueComparer->isFutureTime( $timeValue ); |
195 | } |
196 | |
197 | } |