1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Helper; |
4
|
|
|
|
5
|
|
|
use Config; |
6
|
|
|
use DataValues\DataValue; |
7
|
|
|
use DataValues\QuantityValue; |
8
|
|
|
use DataValues\TimeValue; |
9
|
|
|
use DataValues\TimeValueCalculator; |
10
|
|
|
use DataValues\UnboundedQuantityValue; |
11
|
|
|
use InvalidArgumentException; |
12
|
|
|
use ValueParsers\ValueParser; |
13
|
|
|
use Wikibase\Lib\Units\UnitConverter; |
14
|
|
|
use Wikibase\Repo\Parsers\TimeParserFactory; |
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 TimeParserFactory() )->getTimeParser(); |
55
|
|
|
$this->timeCalculator = new TimeValueCalculator(); |
56
|
|
|
$this->timeValueComparer = new TimeValueComparer(); |
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
|
|
View Code Duplication |
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 = null, DataValue $rhs = null ) { |
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
|
|
|
return $this->timeValueComparer->getComparison( $lhs, $rhs ); |
|
|
|
|
103
|
|
|
case 'quantity': |
104
|
|
|
/** @var QuantityValue|UnboundedQuantityValue $lhs */ |
105
|
|
|
/** @var QuantityValue|UnboundedQuantityValue $rhs */ |
106
|
|
|
$lhsStandard = $this->standardize( $lhs ); |
|
|
|
|
107
|
|
|
$rhsStandard = $this->standardize( $rhs ); |
108
|
|
|
return $lhsStandard->getAmount()->compare( $rhsStandard->getAmount() ); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
throw new InvalidArgumentException( 'Unsupported data value type' ); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Computes $minuend - $subtrahend, in a format depending on the data type. |
116
|
|
|
* For time values, the difference is in seconds; |
117
|
|
|
* for quantity values, the difference is the numerical difference between the quantities, |
118
|
|
|
* after attempting normalization of each side. |
119
|
|
|
* |
120
|
|
|
* @param TimeValue|QuantityValue|UnboundedQuantityValue $minuend |
121
|
|
|
* @param TimeValue|QuantityValue|UnboundedQuantityValue $subtrahend |
122
|
|
|
* |
123
|
|
|
* @throws InvalidArgumentException if the values do not both have the same, supported data value type |
124
|
|
|
* @return UnboundedQuantityValue |
125
|
|
|
*/ |
126
|
|
|
public function getDifference( DataValue $minuend, DataValue $subtrahend ) { |
127
|
|
|
if ( $minuend->getType() === 'time' && $subtrahend->getType() === 'time' ) { |
128
|
|
|
$minuendSeconds = $this->timeCalculator->getTimestamp( $minuend ); |
129
|
|
|
$subtrahendSeconds = $this->timeCalculator->getTimestamp( $subtrahend ); |
130
|
|
|
return UnboundedQuantityValue::newFromNumber( |
131
|
|
|
$minuendSeconds - $subtrahendSeconds, |
132
|
|
|
$this->config->get( 'WBQualityConstraintsSecondUnit' ) |
133
|
|
|
); |
134
|
|
|
} |
135
|
|
|
if ( $minuend->getType() === 'quantity' && $subtrahend->getType() === 'quantity' ) { |
136
|
|
|
$minuendStandard = $this->standardize( $minuend ); |
|
|
|
|
137
|
|
|
$subtrahendStandard = $this->standardize( $subtrahend ); |
|
|
|
|
138
|
|
|
$minuendValue = $minuendStandard->getAmount()->getValueFloat(); |
139
|
|
|
$subtrahendValue = $subtrahendStandard->getAmount()->getValueFloat(); |
140
|
|
|
$diff = $minuendValue - $subtrahendValue; |
141
|
|
|
// we don’t check whether both quantities have the same standard unit – |
142
|
|
|
// that’s the job of a different constraint type, Units (T164372) |
143
|
|
|
return UnboundedQuantityValue::newFromNumber( $diff, $minuendStandard->getUnit() ); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
throw new InvalidArgumentException( 'Unsupported or different data value types' ); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
public function getDifferenceInYears( TimeValue $minuend, TimeValue $subtrahend ) { |
150
|
|
|
if ( !preg_match( '/^([-+]\d{1,16})-(.*)$/', $minuend->getTime(), $minuendMatches ) || |
151
|
|
|
!preg_match( '/^([-+]\d{1,16})-(.*)$/', $subtrahend->getTime(), $subtrahendMatches ) |
152
|
|
|
) { |
153
|
|
|
throw new InvalidArgumentException( 'TimeValue::getTime() did not match expected format' ); |
154
|
|
|
} |
155
|
|
|
$minuendYear = (float)$minuendMatches[1]; |
156
|
|
|
$subtrahendYear = (float)$subtrahendMatches[1]; |
157
|
|
|
$minuendRest = $minuendMatches[2]; |
158
|
|
|
$subtrahendRest = $subtrahendMatches[2]; |
159
|
|
|
|
160
|
|
|
// calculate difference of years |
161
|
|
|
$diff = $minuendYear - $subtrahendYear; |
162
|
|
|
if ( $minuendYear > 0.0 && $subtrahendYear < 0.0 ) { |
163
|
|
|
$diff -= 1.0; // there is no year 0, remove it from difference |
164
|
|
|
} elseif ( $minuendYear < 0.0 && $subtrahendYear > 0.0 ) { |
165
|
|
|
$diff -= -1.0; // there is no year 0, remove it from negative difference |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
// adjust for date within year by parsing the month-day part within the same year |
169
|
|
|
$minuendDateValue = $this->timeParser->parse( '+0000000000001970-' . $minuendRest ); |
170
|
|
|
$subtrahendDateValue = $this->timeParser->parse( '+0000000000001970-' . $subtrahendRest ); |
171
|
|
|
$minuendDateSeconds = $this->timeCalculator->getTimestamp( $minuendDateValue ); |
172
|
|
|
$subtrahendDateSeconds = $this->timeCalculator->getTimestamp( $subtrahendDateValue ); |
173
|
|
|
if ( $minuendDateSeconds < $subtrahendDateSeconds ) { |
174
|
|
|
// difference in the last year is actually less than one full year |
175
|
|
|
// e. g. 1975-03-01 - 1974-09-01 is just six months |
176
|
|
|
// (we don’t need sub-year precision in the difference, adjusting by 0.5 is enough) |
177
|
|
|
$diff -= 0.5; |
178
|
|
|
} elseif ( $minuendDateSeconds > $subtrahendDateSeconds ) { |
179
|
|
|
// difference in the last year is actually more than one full year |
180
|
|
|
// e. g. 1975-09-01 - 1974-03-01 is 18 months |
181
|
|
|
// (we don’t need sub-year precision in the difference, adjusting by 0.5 is enough) |
182
|
|
|
$diff += 0.5; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
$unit = $this->config->get( 'WBQualityConstraintsYearUnit' ); |
186
|
|
|
return UnboundedQuantityValue::newFromNumber( $diff, $unit ); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
public function isFutureTime( TimeValue $timeValue ) { |
190
|
|
|
return $this->timeValueComparer->isFutureTime( $timeValue ); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
} |
194
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.