wikidata_position_history 1.7.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +91 -6
- data/exe/position-history-for-item +1 -1
- data/lib/query_service.rb +18 -1
- data/lib/sparql/bio_query.rb +19 -9
- data/lib/sparql/item_query.rb +30 -1
- data/lib/sparql/mandates_query.rb +33 -39
- data/lib/sparql/position_query.rb +44 -27
- data/lib/wikidata_position_history.rb +1 -0
- data/lib/wikidata_position_history/checks.rb +44 -12
- data/lib/wikidata_position_history/output_row.rb +143 -0
- data/lib/wikidata_position_history/report.rb +110 -73
- data/lib/wikidata_position_history/template.rb +38 -8
- data/lib/wikidata_position_history/version.rb +1 -1
- data/wikidata_position_history.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52e5490ed1d01167632b8adc9cb5d9a37912a5accad858b5d2ada5aaa43522ff
|
4
|
+
data.tar.gz: ab39a9aed506e9c77834ed7a7fdc6a9a06006eaee84f14ee9fb0c71eb069c5a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7aae1fb45f43c0ba961d878de171e4150194514dc406e0059a8c9bc2544ce2e9549d4e9e07c5da6b7ccfe8a6aec534735c1f645fb026a5eb04512b4cee7f9cb
|
7
|
+
data.tar.gz: 797ab543a4a0ee636ed833a1f200c266ebed7b60872084b6f9b0156b11920dc619c15d4bc19b8326ca747c9f5585df4a81be7b23c5fbdd07a177e3ded1f3f702
|
data/CHANGELOG.md
CHANGED
@@ -1,20 +1,105 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
# [2.0.0] 2020-09-16
|
4
|
+
|
5
|
+
## Interface change
|
6
|
+
|
7
|
+
* `Report#wikitext_with_header` has been removed. This was undocumented,
|
8
|
+
and only used internally, so should not be a breaking change, but if
|
9
|
+
anything *was* using it, that will now break loudly (but, usefully,
|
10
|
+
should also break very early.)
|
11
|
+
|
12
|
+
## Enhancements
|
13
|
+
|
14
|
+
* A {{PositionHolderHistory}} template can now also be added to items
|
15
|
+
representing single-member constituencies, to see the history of
|
16
|
+
representatives for that seat.
|
17
|
+
|
18
|
+
# [1.11.0] 2020-09-14
|
19
|
+
|
20
|
+
## Enhancements
|
21
|
+
|
22
|
+
* Wikidata now has a new {{QB}} template to link to _both_ an Item and
|
23
|
+
its Talk page. As the PositionHolderHistory template for a position
|
24
|
+
often lives on its Talk page, any link to a position item now uses
|
25
|
+
this template, for easier of access to that. (Requested by Tagishsimon:
|
26
|
+
https://twitter.com/Tagishsimon/status/1304363879322079233)
|
27
|
+
|
28
|
+
* The change to how warnings templates work from the last release (i.e.
|
29
|
+
using on-wiki templates, so they can be improved or translated, or used
|
30
|
+
to look for problems via backlinks) definitely seems to have been a good
|
31
|
+
idea, so now the warnings for having an unexpected number of inception
|
32
|
+
or abolition dates have also been adjusted to work the same way.
|
33
|
+
|
34
|
+
# [1.10.0] 2020-09-13
|
35
|
+
|
36
|
+
## Enhancements
|
37
|
+
|
38
|
+
* If some other position has a "replaces" (P1365) or "replaced by"
|
39
|
+
(P1366) pointing to _this_ position, but this position doesn't have
|
40
|
+
the reciprocal inverse claims back to that one, include them as a
|
41
|
+
successor/predecessor, but warn that it’s only an indirect connection.
|
42
|
+
|
43
|
+
* The warnings in the above case now use an on-wiki template for their
|
44
|
+
text. This means they can be translated into other languages, and also
|
45
|
+
means that backlinks to these templates, via WhatLinksHere, can act as
|
46
|
+
a TODO list. The other warnings will be migrated to this approach Real
|
47
|
+
Soon Now™.
|
48
|
+
|
49
|
+
# [1.9.0] 2020-09-11
|
50
|
+
|
51
|
+
## Enhancements
|
52
|
+
|
53
|
+
* If a position has any successor or predecessor offices (in "replaces"
|
54
|
+
(P1365) or "replaced by" (P1366)) the report will now display those.
|
55
|
+
|
56
|
+
# [1.8.0] 2020-09-09
|
57
|
+
|
58
|
+
## Enhancements
|
59
|
+
|
60
|
+
* If the very latest person we know of having held this position also
|
61
|
+
has a 'replaced by' qualifier, that’s a sign that the successor should
|
62
|
+
really also have a suitable P39, and actually appear here too. So we
|
63
|
+
want to display a warning in such cases. Likewise if the earliest person
|
64
|
+
we know if also has a 'replaces'
|
65
|
+
|
66
|
+
* This report is meant to be used with positions that are held by only a
|
67
|
+
single person at a time. Using it to produce a report of everyone who
|
68
|
+
has been, say, a Member of the UK Parliament, is the sort of thing that
|
69
|
+
will cause all manner of havoc, as it will try to display tens of
|
70
|
+
thousands of people, all of whom have overlaps with other members etc.
|
71
|
+
So we now sanity-check first of all that the position isn't legislative,
|
72
|
+
and produce a nice "Don’t do that!" message in such cases.
|
73
|
+
|
74
|
+
* It seems that the recent ability to handle dates that are only know at
|
75
|
+
decade-level precision isn’t actually enough, as we have some that we
|
76
|
+
only know at century-level precision! (For example, that the position of
|
77
|
+
Lord Chancellor of Ireland was created some time in the 12th Century.)
|
78
|
+
Such dates will now appear in a nicer format.
|
79
|
+
|
80
|
+
* Sometimes updating the table looks like something has changed, but
|
81
|
+
really the only difference is that a few of people who have no dates
|
82
|
+
are shuffled around a bit in the list. This is because we previously
|
83
|
+
only sorted by date order, so people with no dates were effectively in a
|
84
|
+
random order. Now we sort those people by ID too, which should minimise
|
85
|
+
the number of times an update will appear in your watchlist, only to
|
86
|
+
discover nothing significant actually happened.
|
87
|
+
|
3
88
|
# [1.7.0] 2020-09-08
|
4
89
|
|
5
|
-
|
90
|
+
## Enchancements
|
6
91
|
|
7
|
-
* Yesterday’s future, when we said we
|
92
|
+
* Yesterday’s future, when we said we’d do something a little better
|
8
93
|
with positions that have multiple inception or abolition dates, has
|
9
94
|
arrived. Now we display all of them (with a warning), rather than just
|
10
95
|
picking one semi-randomly.
|
11
96
|
|
12
|
-
|
97
|
+
## Improvements
|
13
98
|
|
14
99
|
* A query like https://w.wiki/bVz is taking about 6 seconds to run.
|
15
100
|
Changing that to https://w.wiki/bW3 drops that to about half a second.
|
16
101
|
If you were to guess that the first has now been replaced by the
|
17
|
-
second, you
|
102
|
+
second, you’d be entirely correct.
|
18
103
|
|
19
104
|
# [1.6.0] 2020-09-07
|
20
105
|
|
@@ -22,7 +107,7 @@
|
|
22
107
|
|
23
108
|
* If a position has an inception date and/or abolition date, those will
|
24
109
|
now also be displayed. (If a position has more than one of either of
|
25
|
-
those — which really shouldn
|
110
|
+
those — which really shouldn’t happen, but sometimes does — then the
|
26
111
|
behaviour may not be particularly sensible. Later evolutions of this
|
27
112
|
feature will hopefully handle that better.)
|
28
113
|
|
@@ -39,7 +124,7 @@
|
|
39
124
|
|
40
125
|
* When showing the results for a position from long long ago (such as
|
41
126
|
the High Kings of Ireland), display the dates as "862 – 879" not as
|
42
|
-
"
|
127
|
+
"0862 – 0879"
|
43
128
|
* If we only know that someone took (or left) office sometime in a given
|
44
129
|
decade (i.e. at date precision 8), display that as (say) "1930s"
|
45
130
|
|
data/lib/query_service.rb
CHANGED
@@ -37,6 +37,10 @@ module QueryService
|
|
37
37
|
@url = url
|
38
38
|
end
|
39
39
|
|
40
|
+
def eql?(other)
|
41
|
+
id == other.id
|
42
|
+
end
|
43
|
+
|
40
44
|
def id
|
41
45
|
url.split('/').last unless url.to_s.empty?
|
42
46
|
end
|
@@ -45,6 +49,18 @@ module QueryService
|
|
45
49
|
"{{Q|#{id}}}" if id
|
46
50
|
end
|
47
51
|
|
52
|
+
def qblink
|
53
|
+
"{{QB|#{id}}}" if id
|
54
|
+
end
|
55
|
+
|
56
|
+
def qlink_i
|
57
|
+
"''#{qlink}''" if qlink
|
58
|
+
end
|
59
|
+
|
60
|
+
def qblink_i
|
61
|
+
"''#{qblink}''" if qblink
|
62
|
+
end
|
63
|
+
|
48
64
|
private
|
49
65
|
|
50
66
|
attr_reader :url
|
@@ -54,7 +70,7 @@ module QueryService
|
|
54
70
|
class WikidataDate
|
55
71
|
include Comparable
|
56
72
|
|
57
|
-
DATELEN = { '11' => 10, '10' => 7, '9' => 4, '8' => 4 }.freeze
|
73
|
+
DATELEN = { '11' => 10, '10' => 7, '9' => 4, '8' => 4, '7' => 2 }.freeze
|
58
74
|
|
59
75
|
def initialize(str, precision)
|
60
76
|
@str = str
|
@@ -105,6 +121,7 @@ module QueryService
|
|
105
121
|
end
|
106
122
|
|
107
123
|
def precisioned_string
|
124
|
+
return "#{truncated_string}. century" if precision == '7'
|
108
125
|
return "#{truncated_string}s" if precision == '8'
|
109
126
|
|
110
127
|
truncated_string
|
data/lib/sparql/bio_query.rb
CHANGED
@@ -21,16 +21,28 @@ module WikidataPositionHistory
|
|
21
21
|
SPARQL
|
22
22
|
end
|
23
23
|
end
|
24
|
-
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
# Biographical data for Members for a Constituency
|
26
|
+
class ConstituencyBioQuery < ItemQuery
|
27
|
+
def raw_sparql
|
28
|
+
<<~SPARQL
|
29
|
+
# constituency-biodata
|
30
|
+
|
31
|
+
SELECT DISTINCT ?item ?image
|
32
|
+
WHERE {
|
33
|
+
?item wdt:P31 wd:Q5 ; p:P39/pq:P768 wd:%s .
|
34
|
+
OPTIONAL { ?item wdt:P18 ?image }
|
35
|
+
}
|
36
|
+
ORDER BY ?item
|
37
|
+
SPARQL
|
38
|
+
end
|
30
39
|
end
|
40
|
+
end
|
31
41
|
|
42
|
+
# Represents a single row returned from the Position query
|
43
|
+
class BioRow < SPARQL::QueryRow
|
32
44
|
def person
|
33
|
-
|
45
|
+
item_from(:item)
|
34
46
|
end
|
35
47
|
|
36
48
|
def image_title
|
@@ -47,10 +59,8 @@ module WikidataPositionHistory
|
|
47
59
|
|
48
60
|
private
|
49
61
|
|
50
|
-
attr_reader :row
|
51
|
-
|
52
62
|
def image_url
|
53
|
-
|
63
|
+
raw(:image)
|
54
64
|
end
|
55
65
|
end
|
56
66
|
end
|
data/lib/sparql/item_query.rb
CHANGED
@@ -4,7 +4,7 @@ require 'erb'
|
|
4
4
|
|
5
5
|
module WikidataPositionHistory
|
6
6
|
module SPARQL
|
7
|
-
#
|
7
|
+
# Abstract class to turn raw SPARQL into result objects
|
8
8
|
class ItemQuery
|
9
9
|
def initialize(itemid)
|
10
10
|
@itemid = itemid
|
@@ -34,5 +34,34 @@ module WikidataPositionHistory
|
|
34
34
|
@json ||= QueryService::Query.new(sparql).results
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
# Abstract class to represents a single row returned from a query
|
39
|
+
class QueryRow
|
40
|
+
def initialize(row)
|
41
|
+
@row = row
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
attr_reader :row
|
47
|
+
|
48
|
+
def item_from(key)
|
49
|
+
value = raw(key)
|
50
|
+
return if value.to_s.empty?
|
51
|
+
|
52
|
+
QueryService::WikidataItem.new(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def date_from(key, precision_key)
|
56
|
+
trunc = raw(key).to_s[0..9]
|
57
|
+
return if trunc.empty?
|
58
|
+
|
59
|
+
QueryService::WikidataDate.new(trunc, raw(precision_key))
|
60
|
+
end
|
61
|
+
|
62
|
+
def raw(key)
|
63
|
+
row.dig(key, :value)
|
64
|
+
end
|
65
|
+
end
|
37
66
|
end
|
38
67
|
end
|
@@ -21,39 +21,57 @@ module WikidataPositionHistory
|
|
21
21
|
OPTIONAL { ?posn pq:P1545 ?ordinal }
|
22
22
|
OPTIONAL { ?posn pq:P5102 ?nature }
|
23
23
|
}
|
24
|
-
ORDER BY DESC(?start_date)
|
24
|
+
ORDER BY DESC(?start_date) ?item
|
25
25
|
SPARQL
|
26
26
|
end
|
27
27
|
end
|
28
|
-
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
# SPARQL for fetching all mandates of a single-member district
|
30
|
+
class ConstituencyMandatesQuery < ItemQuery
|
31
|
+
def raw_sparql
|
32
|
+
<<~SPARQL
|
33
|
+
# constituency-mandates
|
34
|
+
|
35
|
+
SELECT DISTINCT ?ordinal ?item ?start_date ?start_precision ?end_date ?end_precision ?prev ?next ?nature
|
36
|
+
WHERE {
|
37
|
+
?item wdt:P31 wd:Q5 ; p:P39 ?posn .
|
38
|
+
?posn pq:P768 wd:%s .
|
39
|
+
FILTER NOT EXISTS { ?posn wikibase:rank wikibase:DeprecatedRank }
|
40
|
+
|
41
|
+
OPTIONAL { ?posn pqv:P580 [ wikibase:timeValue ?start_date; wikibase:timePrecision ?start_precision ] }
|
42
|
+
OPTIONAL { ?posn pqv:P582 [ wikibase:timeValue ?end_date; wikibase:timePrecision ?end_precision ] }
|
43
|
+
OPTIONAL { ?posn pq:P1365|pq:P155 ?prev }
|
44
|
+
OPTIONAL { ?posn pq:P1366|pq:P156 ?next }
|
45
|
+
OPTIONAL { ?posn pq:P1545 ?ordinal }
|
46
|
+
OPTIONAL { ?posn pq:P5102 ?nature }
|
47
|
+
}
|
48
|
+
ORDER BY DESC(?start_date) ?item
|
49
|
+
SPARQL
|
50
|
+
end
|
34
51
|
end
|
52
|
+
end
|
35
53
|
|
54
|
+
# Represents a single row returned from the Mandates query
|
55
|
+
class MandateRow < SPARQL::QueryRow
|
36
56
|
def ordinal
|
37
|
-
|
57
|
+
raw(:ordinal)
|
38
58
|
end
|
39
59
|
|
40
60
|
def officeholder
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
# TODO: rename or remove. 'item' is meaningless/ambiguous
|
45
|
-
def item
|
46
|
-
officeholder.qlink
|
61
|
+
item_from(:item)
|
47
62
|
end
|
48
63
|
|
64
|
+
# TODO: switch to item_from
|
49
65
|
def prev
|
50
66
|
QueryService::WikidataItem.new(row.dig(:prev, :value)).qlink
|
51
67
|
end
|
52
68
|
|
69
|
+
# TODO: switch to item_from
|
53
70
|
def next
|
54
71
|
QueryService::WikidataItem.new(row.dig(:next, :value)).qlink
|
55
72
|
end
|
56
73
|
|
74
|
+
# TODO: switch to item_from
|
57
75
|
def nature
|
58
76
|
QueryService::WikidataItem.new(row.dig(:nature, :value)).id
|
59
77
|
end
|
@@ -63,35 +81,11 @@ module WikidataPositionHistory
|
|
63
81
|
end
|
64
82
|
|
65
83
|
def start_date
|
66
|
-
|
67
|
-
|
68
|
-
QueryService::WikidataDate.new(start_date_raw, start_date_precision)
|
84
|
+
date_from(:start_date, :start_precision)
|
69
85
|
end
|
70
86
|
|
71
87
|
def end_date
|
72
|
-
|
73
|
-
|
74
|
-
QueryService::WikidataDate.new(end_date_raw, end_date_precision)
|
75
|
-
end
|
76
|
-
|
77
|
-
def start_date_raw
|
78
|
-
row.dig(:start_date, :value).to_s[0..9]
|
88
|
+
date_from(:end_date, :end_precision)
|
79
89
|
end
|
80
|
-
|
81
|
-
def end_date_raw
|
82
|
-
row.dig(:end_date, :value).to_s[0..9]
|
83
|
-
end
|
84
|
-
|
85
|
-
def start_date_precision
|
86
|
-
row.dig(:start_precision, :value)
|
87
|
-
end
|
88
|
-
|
89
|
-
def end_date_precision
|
90
|
-
row.dig(:end_precision, :value)
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
attr_reader :row
|
96
90
|
end
|
97
91
|
end
|
@@ -8,71 +8,88 @@ module WikidataPositionHistory
|
|
8
8
|
<<~SPARQL
|
9
9
|
# position-metadata
|
10
10
|
|
11
|
-
SELECT DISTINCT ?item ?inception ?inception_precision ?abolition ?abolition_precision
|
11
|
+
SELECT DISTINCT ?item ?inception ?inception_precision ?abolition ?abolition_precision
|
12
|
+
?replaces ?replacedBy ?derivedReplaces ?derivedReplacedBy
|
13
|
+
?isPosition ?isLegislator
|
14
|
+
?isConstituency ?representative_count ?legislature
|
12
15
|
WHERE {
|
13
16
|
VALUES ?item { wd:%s }
|
14
|
-
BIND(EXISTS { wd:%s wdt:P279+ wd:Q4164871
|
17
|
+
BIND(EXISTS { wd:%s wdt:P279+ wd:Q4164871 } as ?isPosition)
|
18
|
+
BIND(EXISTS { wd:%s wdt:P279+ wd:Q4175034 } as ?isLegislator)
|
19
|
+
BIND(EXISTS { wd:%s wdt:P31/wdt:P279+ wd:Q192611 } as ?isConstituency)
|
20
|
+
|
15
21
|
OPTIONAL { ?item p:P571 [ a wikibase:BestRank ;
|
16
22
|
psv:P571 [ wikibase:timeValue ?inception; wikibase:timePrecision ?inception_precision ]
|
17
23
|
] }
|
18
24
|
OPTIONAL { ?item p:P576 [ a wikibase:BestRank ;
|
19
25
|
psv:P576 [ wikibase:timeValue ?abolition; wikibase:timePrecision ?abolition_precision ]
|
20
26
|
] }
|
21
|
-
|
27
|
+
OPTIONAL { ?item wdt:P1365 ?replaces }
|
28
|
+
OPTIONAL { ?item wdt:P1366 ?replacedBy }
|
29
|
+
OPTIONAL { ?derivedReplaces wdt:P1366 ?item }
|
30
|
+
OPTIONAL { ?derivedReplacedBy wdt:P1365 ?item }
|
31
|
+
|
32
|
+
OPTIONAL { # if constituency
|
33
|
+
?item p:P1410 [ a wikibase:BestRank ; ps:P1410 ?representative_count ; pq:P194 ?legislature ]
|
34
|
+
}
|
22
35
|
}
|
23
36
|
SPARQL
|
24
37
|
end
|
25
38
|
|
26
39
|
def sparql_args
|
27
|
-
[itemid
|
40
|
+
[itemid] * 4
|
28
41
|
end
|
29
42
|
end
|
30
43
|
end
|
31
44
|
|
32
45
|
# Represents a single row returned from the Position query
|
33
|
-
class PositionRow
|
34
|
-
def initialize(row)
|
35
|
-
@row = row
|
36
|
-
end
|
37
|
-
|
46
|
+
class PositionRow < SPARQL::QueryRow
|
38
47
|
def item
|
39
|
-
|
48
|
+
item_from(:item)
|
40
49
|
end
|
41
50
|
|
42
51
|
def inception_date
|
43
|
-
|
44
|
-
|
45
|
-
QueryService::WikidataDate.new(inception_date_raw, inception_date_precision)
|
52
|
+
date_from(:inception, :inception_precision)
|
46
53
|
end
|
47
54
|
|
48
55
|
def abolition_date
|
49
|
-
|
56
|
+
date_from(:abolition, :abolition_precision)
|
57
|
+
end
|
50
58
|
|
51
|
-
|
59
|
+
def replaces
|
60
|
+
item_from(:replaces)
|
52
61
|
end
|
53
62
|
|
54
|
-
def
|
55
|
-
|
63
|
+
def replaced_by
|
64
|
+
item_from(:replacedBy)
|
56
65
|
end
|
57
66
|
|
58
|
-
|
67
|
+
def derived_replaces
|
68
|
+
item_from(:derivedReplaces)
|
69
|
+
end
|
70
|
+
|
71
|
+
def derived_replaced_by
|
72
|
+
item_from(:derivedReplacedBy)
|
73
|
+
end
|
59
74
|
|
60
|
-
|
75
|
+
def position?
|
76
|
+
raw(:isPosition) == 'true'
|
77
|
+
end
|
61
78
|
|
62
|
-
def
|
63
|
-
|
79
|
+
def legislator?
|
80
|
+
raw(:isLegislator) == 'true'
|
64
81
|
end
|
65
82
|
|
66
|
-
def
|
67
|
-
|
83
|
+
def constituency?
|
84
|
+
raw(:isConstituency) == 'true'
|
68
85
|
end
|
69
86
|
|
70
|
-
def
|
71
|
-
|
87
|
+
def legislature
|
88
|
+
item_from(:legislature)
|
72
89
|
end
|
73
90
|
|
74
|
-
def
|
75
|
-
|
91
|
+
def representative_count
|
92
|
+
raw(:representative_count).to_i
|
76
93
|
end
|
77
94
|
end
|
78
95
|
end
|
@@ -7,6 +7,7 @@ require 'sparql/bio_query'
|
|
7
7
|
require 'sparql/mandates_query'
|
8
8
|
require 'wikidata_position_history/checks'
|
9
9
|
require 'wikidata_position_history/template'
|
10
|
+
require 'wikidata_position_history/output_row'
|
10
11
|
require 'wikidata_position_history/report'
|
11
12
|
require 'wikidata_position_history/version'
|
12
13
|
|
@@ -17,19 +17,20 @@ module WikidataPositionHistory
|
|
17
17
|
|
18
18
|
attr_reader :later, :current, :earlier
|
19
19
|
|
20
|
-
|
20
|
+
# TODO: replace these with objects instead of strings
|
21
|
+
def successor_qlink
|
21
22
|
current.next
|
22
23
|
end
|
23
24
|
|
24
|
-
def
|
25
|
+
def predecessor_qlink
|
25
26
|
current.prev
|
26
27
|
end
|
27
28
|
|
28
|
-
def
|
29
|
+
def later_holder?
|
29
30
|
!!later
|
30
31
|
end
|
31
32
|
|
32
|
-
def
|
33
|
+
def earlier_holder?
|
33
34
|
!!earlier
|
34
35
|
end
|
35
36
|
end
|
@@ -46,7 +47,7 @@ module WikidataPositionHistory
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def possible_explanation
|
49
|
-
"#{current.
|
50
|
+
"#{current.officeholder.qlink} is missing #{missing.map { |field| "{{P|#{field_map[field]}}}" }.join(', ')}"
|
50
51
|
end
|
51
52
|
|
52
53
|
def missing
|
@@ -76,14 +77,14 @@ module WikidataPositionHistory
|
|
76
77
|
|
77
78
|
def expect_prev?
|
78
79
|
return unless earlier
|
79
|
-
return if earlier.
|
80
|
+
return if earlier.officeholder.id == current.officeholder.id # sucessive terms by same person
|
80
81
|
|
81
82
|
!current.acting?
|
82
83
|
end
|
83
84
|
|
84
85
|
def expect_next?
|
85
86
|
return unless later
|
86
|
-
return if later.
|
87
|
+
return if later.officeholder.id == current.officeholder.id # sucessive terms by same person
|
87
88
|
|
88
89
|
!current.acting?
|
89
90
|
end
|
@@ -92,7 +93,7 @@ module WikidataPositionHistory
|
|
92
93
|
# Does the 'replaces' match the previous item in the list?
|
93
94
|
class WrongPredecessor < Check
|
94
95
|
def problem?
|
95
|
-
|
96
|
+
earlier_holder? && !!predecessor_qlink && (earlier.officeholder.qlink != predecessor_qlink)
|
96
97
|
end
|
97
98
|
|
98
99
|
def headline
|
@@ -100,14 +101,44 @@ module WikidataPositionHistory
|
|
100
101
|
end
|
101
102
|
|
102
103
|
def possible_explanation
|
103
|
-
"#{current.
|
104
|
+
"#{current.officeholder.qlink} has a {{P|1365}} of #{predecessor_qlink}, but follows #{earlier.officeholder.qlink} here"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is there a 'replaces' but no previous item in the list?
|
109
|
+
class MissingPredecessor < Check
|
110
|
+
def problem?
|
111
|
+
predecessor_qlink && !earlier_holder?
|
112
|
+
end
|
113
|
+
|
114
|
+
def headline
|
115
|
+
'Inconsistent predecessor'
|
116
|
+
end
|
117
|
+
|
118
|
+
def possible_explanation
|
119
|
+
"#{current.officeholder.qlink} has a {{P|1365}} of #{predecessor_qlink}, but does not follow anyone here"
|
104
120
|
end
|
105
121
|
end
|
106
122
|
|
107
123
|
# Does the 'replaced by' match the next item in the list?
|
108
124
|
class WrongSuccessor < Check
|
109
125
|
def problem?
|
110
|
-
|
126
|
+
later_holder? && !!successor_qlink && (later.officeholder.qlink != successor_qlink)
|
127
|
+
end
|
128
|
+
|
129
|
+
def headline
|
130
|
+
'Inconsistent successor'
|
131
|
+
end
|
132
|
+
|
133
|
+
def possible_explanation
|
134
|
+
"#{current.officeholder.qlink} has a {{P|1366}} of #{successor_qlink}, but is followed by #{later.officeholder.qlink} here"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Is there a 'replaced by' but no next item in the list?
|
139
|
+
class MissingSuccessor < Check
|
140
|
+
def problem?
|
141
|
+
successor_qlink && !later_holder?
|
111
142
|
end
|
112
143
|
|
113
144
|
def headline
|
@@ -115,7 +146,7 @@ module WikidataPositionHistory
|
|
115
146
|
end
|
116
147
|
|
117
148
|
def possible_explanation
|
118
|
-
"#{current.
|
149
|
+
"#{current.officeholder.qlink} has a {{P|1366}} of #{successor_qlink}, but is not followed by anyone here"
|
119
150
|
end
|
120
151
|
end
|
121
152
|
|
@@ -137,7 +168,8 @@ module WikidataPositionHistory
|
|
137
168
|
end
|
138
169
|
|
139
170
|
def possible_explanation
|
140
|
-
|
171
|
+
format('%s has a {{P|582}} of %s, which %s the {{P|580}} of %s for %s',
|
172
|
+
current.officeholder.qlink, current.end_date, overlap_explanation, later.start_date, later.officeholder.qlink)
|
141
173
|
end
|
142
174
|
|
143
175
|
protected
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WikidataPositionHistory
|
4
|
+
# simplified version of a WikidataPositionHistory::Check
|
5
|
+
Warning = Struct.new(:headline, :explanation)
|
6
|
+
|
7
|
+
class OutputRow
|
8
|
+
# Date for a single mandate row, to be passed to the report template
|
9
|
+
class Mandate
|
10
|
+
CHECKS = [Check::MissingFields, Check::Overlap,
|
11
|
+
Check::WrongPredecessor, Check::MissingPredecessor,
|
12
|
+
Check::WrongSuccessor, Check::MissingSuccessor].freeze
|
13
|
+
|
14
|
+
def initialize(later, current, earlier)
|
15
|
+
@later = later
|
16
|
+
@current = current
|
17
|
+
@earlier = earlier
|
18
|
+
end
|
19
|
+
|
20
|
+
def ordinal_string
|
21
|
+
ordinal = current.ordinal or return ''
|
22
|
+
"#{ordinal}."
|
23
|
+
end
|
24
|
+
|
25
|
+
def officeholder
|
26
|
+
current.officeholder
|
27
|
+
end
|
28
|
+
|
29
|
+
def dates
|
30
|
+
dates = [current.start_date, current.end_date]
|
31
|
+
return '' if dates.compact.empty?
|
32
|
+
|
33
|
+
dates.join(' – ')
|
34
|
+
end
|
35
|
+
|
36
|
+
def acting?
|
37
|
+
current.acting?
|
38
|
+
end
|
39
|
+
|
40
|
+
def warnings
|
41
|
+
CHECKS.map { |klass| klass.new(later, current, earlier) }.select(&:problem?)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :later, :current, :earlier
|
47
|
+
end
|
48
|
+
|
49
|
+
# Base class for the Inception/Abolition date rows
|
50
|
+
class PositionDate
|
51
|
+
def initialize(metadata)
|
52
|
+
@metadata = metadata
|
53
|
+
end
|
54
|
+
|
55
|
+
def date
|
56
|
+
return if dates.empty?
|
57
|
+
|
58
|
+
dates.join(' / ')
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
attr_reader :metadata
|
64
|
+
|
65
|
+
def position_id
|
66
|
+
metadata.position.id
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Data for the Inception date of the position
|
71
|
+
class Inception < PositionDate
|
72
|
+
def warnings
|
73
|
+
count = dates.count
|
74
|
+
return [] if count == 1
|
75
|
+
return [Warning.new('Missing field', "{{PositionHolderHistory/warning_no_inception_date|item=#{position_id}}}")] if count.zero?
|
76
|
+
|
77
|
+
[Warning.new('Multiple values', "{{PositionHolderHistory/warning_multiple_inception_dates|item=#{position_id}}}")]
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def dates
|
83
|
+
metadata.inception_dates
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Data for the Abolition date of the position
|
88
|
+
class Abolition < PositionDate
|
89
|
+
def warnings
|
90
|
+
return [] unless dates.count > 1
|
91
|
+
|
92
|
+
[Warning.new('Multiple values', "{{PositionHolderHistory/warning_multiple_abolition_dates|item=#{position_id}}}")]
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def dates
|
98
|
+
metadata.abolition_dates
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Data for related position: e.g. Successor/Predecessor
|
103
|
+
class RelatedPosition
|
104
|
+
def initialize(metadata)
|
105
|
+
@metadata = metadata
|
106
|
+
end
|
107
|
+
|
108
|
+
def position
|
109
|
+
return if implied_list.empty?
|
110
|
+
|
111
|
+
(implied_list.direct.map(&:qblink) + implied_list.indirect_only.map(&:qblink_i)).join(', ')
|
112
|
+
end
|
113
|
+
|
114
|
+
def warnings
|
115
|
+
implied_list.indirect_only.map do |from|
|
116
|
+
Warning.new('Indirect only', "{{PositionHolderHistory/#{indirect_warning_template}|from=#{from.id}|to=#{metadata.position.id}}}")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
attr_reader :metadata
|
123
|
+
|
124
|
+
def indirect_warning_template
|
125
|
+
format('warning_indirect_%s', self.class.name.split('::').last.downcase)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Data for the position that comes after this one
|
130
|
+
class Successor < RelatedPosition
|
131
|
+
def implied_list
|
132
|
+
metadata.replaced_by_combined
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Data for the position that came before this one
|
137
|
+
class Predecessor < RelatedPosition
|
138
|
+
def implied_list
|
139
|
+
metadata.replaces_combined
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -1,83 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module WikidataPositionHistory
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@
|
10
|
-
@current = current
|
11
|
-
@earlier = earlier
|
4
|
+
# A list made up of both direct and indirect claims, where we
|
5
|
+
# can tell which came from which, when required
|
6
|
+
class ImpliedList
|
7
|
+
def initialize(direct, indirect)
|
8
|
+
@direct = direct
|
9
|
+
@indirect = indirect
|
12
10
|
end
|
13
11
|
|
14
|
-
def
|
15
|
-
|
16
|
-
"#{ordinal}."
|
12
|
+
def empty?
|
13
|
+
all.empty?
|
17
14
|
end
|
18
15
|
|
19
|
-
def
|
20
|
-
|
16
|
+
def all
|
17
|
+
direct | indirect
|
21
18
|
end
|
22
19
|
|
23
|
-
def
|
24
|
-
|
25
|
-
return '' if dates.compact.empty?
|
26
|
-
|
27
|
-
dates.join(' – ')
|
20
|
+
def both
|
21
|
+
direct & indirect
|
28
22
|
end
|
29
23
|
|
30
|
-
def
|
31
|
-
|
24
|
+
def direct_only
|
25
|
+
direct - indirect
|
32
26
|
end
|
33
27
|
|
34
|
-
def
|
35
|
-
|
28
|
+
def indirect_only
|
29
|
+
indirect - direct
|
36
30
|
end
|
37
31
|
|
38
|
-
|
39
|
-
|
40
|
-
attr_reader :later, :current, :earlier
|
32
|
+
attr_reader :direct, :indirect
|
41
33
|
end
|
42
34
|
|
43
35
|
# Data about the position itself, to be passed to the report template
|
44
36
|
class Metadata
|
45
|
-
# simplified version of a WikidataPositionHistory::Check
|
46
|
-
Warning = Struct.new(:headline, :explanation)
|
47
|
-
|
48
37
|
def initialize(rows)
|
49
38
|
@rows = rows
|
50
39
|
end
|
51
40
|
|
52
|
-
def
|
41
|
+
def position
|
53
42
|
rows.map(&:item).first
|
54
43
|
end
|
55
44
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
inception_dates.join(' / ')
|
45
|
+
def predecessor
|
46
|
+
@predecessor ||= OutputRow::Predecessor.new(self)
|
60
47
|
end
|
61
48
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
return if count == 1
|
66
|
-
return Warning.new('Missing field', "#{item_qlink} is missing {{P|571}}") if count.zero?
|
67
|
-
|
68
|
-
Warning.new('Multiple values', "#{item_qlink} has more than one {{P|571}}")
|
49
|
+
def successor
|
50
|
+
@successor ||= OutputRow::Successor.new(self)
|
69
51
|
end
|
70
52
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
abolition_dates.join(' / ')
|
53
|
+
def inception
|
54
|
+
@inception ||= OutputRow::Inception.new(self)
|
75
55
|
end
|
76
56
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
Warning.new('Multiple values', "#{item_qlink} has more than one {{P|576}}")
|
57
|
+
def abolition
|
58
|
+
@abolition ||= OutputRow::Abolition.new(self)
|
81
59
|
end
|
82
60
|
|
83
61
|
def position?
|
@@ -85,9 +63,27 @@ module WikidataPositionHistory
|
|
85
63
|
rows.map(&:position?).first
|
86
64
|
end
|
87
65
|
|
88
|
-
|
66
|
+
def legislator?
|
67
|
+
# this should be the same everywhere
|
68
|
+
rows.map(&:legislator?).first
|
69
|
+
end
|
89
70
|
|
90
|
-
|
71
|
+
def constituency?
|
72
|
+
# this should be the same everywhere
|
73
|
+
rows.map(&:constituency?).first
|
74
|
+
end
|
75
|
+
|
76
|
+
def representative_count
|
77
|
+
rows.map(&:representative_count).max
|
78
|
+
end
|
79
|
+
|
80
|
+
def replaces_combined
|
81
|
+
@replaces_combined ||= ImpliedList.new(uniq_by_id(:replaces), uniq_by_id(:derived_replaces))
|
82
|
+
end
|
83
|
+
|
84
|
+
def replaced_by_combined
|
85
|
+
@replaced_by_combined ||= ImpliedList.new(uniq_by_id(:replaced_by), uniq_by_id(:derived_replaced_by))
|
86
|
+
end
|
91
87
|
|
92
88
|
def inception_dates
|
93
89
|
rows.map(&:inception_date).compact.uniq(&:to_s).sort
|
@@ -97,8 +93,54 @@ module WikidataPositionHistory
|
|
97
93
|
rows.map(&:abolition_date).compact.uniq(&:to_s).sort
|
98
94
|
end
|
99
95
|
|
100
|
-
|
101
|
-
|
96
|
+
private
|
97
|
+
|
98
|
+
attr_reader :rows
|
99
|
+
|
100
|
+
def uniq_by_id(method)
|
101
|
+
rows.map(&method).compact.uniq(&:id).sort_by(&:id)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Construct the correct ReportConfig based on the position metadata
|
106
|
+
class ReportConfigFactory
|
107
|
+
def self.config(metadata)
|
108
|
+
return ReportConfig::Constituency.new if metadata.constituency?
|
109
|
+
|
110
|
+
ReportConfig::Position.new
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
attr_reader :metadata
|
116
|
+
end
|
117
|
+
|
118
|
+
# Encapsulates the different configuration for each type of position
|
119
|
+
module ReportConfig
|
120
|
+
# Configuration for 'default' single-holder position
|
121
|
+
class Position
|
122
|
+
def mandates_query
|
123
|
+
SPARQL::MandatesQuery
|
124
|
+
end
|
125
|
+
|
126
|
+
def biodata_query
|
127
|
+
SPARQL::BioQuery
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Configuration for representatives of a single-member constituency
|
132
|
+
class Constituency
|
133
|
+
def mandates_query
|
134
|
+
SPARQL::ConstituencyMandatesQuery
|
135
|
+
end
|
136
|
+
|
137
|
+
def biodata_query
|
138
|
+
SPARQL::ConstituencyBioQuery
|
139
|
+
end
|
140
|
+
|
141
|
+
def multimember_error_template
|
142
|
+
"\n{{PositionHolderHistory/error_multimember}}\n"
|
143
|
+
end
|
102
144
|
end
|
103
145
|
end
|
104
146
|
|
@@ -112,24 +154,11 @@ module WikidataPositionHistory
|
|
112
154
|
attr_reader :position_id, :template_class
|
113
155
|
|
114
156
|
def wikitext
|
157
|
+
return legislator_template if metadata.legislator?
|
158
|
+
return config.multimember_error_template if metadata.constituency? && (metadata.representative_count != 1)
|
115
159
|
return no_items_output if mandates.empty?
|
116
160
|
|
117
|
-
output
|
118
|
-
end
|
119
|
-
|
120
|
-
def header
|
121
|
-
"== {{Q|#{position_id}}} officeholders #{position_dates} =="
|
122
|
-
end
|
123
|
-
|
124
|
-
def position_dates
|
125
|
-
dates = [metadata.inception_date, metadata.abolition_date]
|
126
|
-
return '' if dates.compact.empty?
|
127
|
-
|
128
|
-
format('(%s)', dates.join(' – '))
|
129
|
-
end
|
130
|
-
|
131
|
-
def wikitext_with_header
|
132
|
-
[header, wikitext].join("\n")
|
161
|
+
template_class.new(template_params).output
|
133
162
|
end
|
134
163
|
|
135
164
|
def template_params
|
@@ -147,7 +176,7 @@ module WikidataPositionHistory
|
|
147
176
|
end
|
148
177
|
|
149
178
|
def biodata
|
150
|
-
@biodata ||=
|
179
|
+
@biodata ||= biodata_sparql.results_as(BioRow)
|
151
180
|
end
|
152
181
|
|
153
182
|
def biodata_for(officeholder)
|
@@ -158,8 +187,16 @@ module WikidataPositionHistory
|
|
158
187
|
[nil, mandates, nil].flatten(1)
|
159
188
|
end
|
160
189
|
|
190
|
+
def config
|
191
|
+
@config ||= ReportConfigFactory.config(metadata)
|
192
|
+
end
|
193
|
+
|
161
194
|
def sparql
|
162
|
-
@sparql ||=
|
195
|
+
@sparql ||= config.mandates_query.new(position_id)
|
196
|
+
end
|
197
|
+
|
198
|
+
def biodata_sparql
|
199
|
+
config.biodata_query.new(position_id)
|
163
200
|
end
|
164
201
|
|
165
202
|
def mandates
|
@@ -170,14 +207,14 @@ module WikidataPositionHistory
|
|
170
207
|
"\n{{PositionHolderHistory/error_no_holders|id=#{position_id}}}\n"
|
171
208
|
end
|
172
209
|
|
173
|
-
def
|
174
|
-
|
210
|
+
def legislator_template
|
211
|
+
"\n{{PositionHolderHistory/error_legislator|id=#{position_id}}}\n"
|
175
212
|
end
|
176
213
|
|
177
214
|
def table_rows
|
178
215
|
padded_mandates.each_cons(3).map do |later, current, earlier|
|
179
216
|
{
|
180
|
-
mandate:
|
217
|
+
mandate: OutputRow::Mandate.new(later, current, earlier),
|
181
218
|
bio: biodata_for(current.officeholder),
|
182
219
|
}
|
183
220
|
end
|
@@ -22,29 +22,59 @@ module WikidataPositionHistory
|
|
22
22
|
def template_text
|
23
23
|
<<~ERB
|
24
24
|
{| class="wikitable" style="text-align: center; border: none;"
|
25
|
-
<% if metadata.
|
25
|
+
<% if metadata.abolition.date -%>
|
26
26
|
|-
|
27
|
-
| colspan="
|
28
|
-
| style="
|
29
|
-
|
27
|
+
| colspan="2" style="border: none; background: #fff; font-size: 1.15em; text-align: right;" | '''Position abolished''':
|
28
|
+
| style="border: none; background: #fff; text-align: left;" | <%= metadata.abolition.date %>
|
29
|
+
| style="border: none; background: #fff; text-align: left;" | \
|
30
|
+
<% metadata.abolition.warnings.each do |warning| -%>
|
30
31
|
<span style="display: block">[[File:Pictogram voting comment.svg|15px|link=]] <span style="color: #d33; font-weight: bold; vertical-align: middle;"><%= warning.headline %></span> <ref><%= warning.explanation %></ref></span>\
|
31
32
|
<% end %>
|
32
33
|
<% end -%>
|
34
|
+
<% if metadata.successor.position -%>
|
35
|
+
|-
|
36
|
+
| colspan="2" style="border: none; background: #fff; font-size: 1.15em; text-align: right;" | '''Replaced by''':
|
37
|
+
| style=" border: none; background: #fff; text-align: left;" | <%= metadata.successor.position %>
|
38
|
+
| style=" border: none; background: #fff; text-align: left;" | \
|
39
|
+
<% metadata.successor.warnings.each do |warning| -%>
|
40
|
+
<span style="display: block">[[File:Pictogram voting comment.svg|15px|link=]] <span style="color: #d33; font-weight: bold; vertical-align: middle;"><%= warning.headline %></span> <ref><%= warning.explanation %></ref></span>\
|
41
|
+
<% end %>
|
42
|
+
<% end -%>
|
43
|
+
<% if metadata.successor.position || metadata.abolition.date -%>
|
44
|
+
|-
|
45
|
+
| colspan="3" style="padding:0.5em; border: none; background: #fff"> |
|
46
|
+
| colspan="1" style="padding:0.5em; border: none; background: #fff"> |
|
47
|
+
<% end -%>
|
33
48
|
<% table_rows.map(&:values).each do |mandate, bio| -%>
|
34
49
|
|-
|
35
50
|
| style="padding:0.5em 2em" | <%= mandate.ordinal_string %>
|
36
51
|
| style="padding:0.5em 2em" | <%= bio.map(&:image_link).first %>
|
37
|
-
| style="padding:0.5em 2em" | <span style="font-size: <%= mandate.acting? ? '1.25em; font-style: italic;' : '1.5em' %>; display: block;"><%= mandate.
|
52
|
+
| style="padding:0.5em 2em" | <span style="font-size: <%= mandate.acting? ? '1.25em; font-style: italic;' : '1.5em' %>; display: block;"><%= mandate.officeholder.qlink %></span> <%= mandate.dates %>
|
38
53
|
| style="padding:0.5em 2em 0.5em 1em; border: none; background: #fff; text-align: left;" | \
|
39
54
|
<% mandate.warnings.each do |warning| -%>
|
40
55
|
<span style="display: block">[[File:Pictogram voting comment.svg|15px|link=]] <span style="color: #d33; font-weight: bold; vertical-align: middle;"><%= warning.headline %></span> <ref><%= warning.explanation %></ref></span>\
|
41
56
|
<% end %>
|
42
57
|
<% end -%>
|
43
|
-
<% if metadata.
|
58
|
+
<% if metadata.successor.position || metadata.abolition.date -%>
|
59
|
+
|-
|
60
|
+
| colspan="3" style="padding:0.5em; border: none; background: #fff"> |
|
61
|
+
| colspan="1" style="padding:0.5em; border: none; background: #fff"> |
|
62
|
+
<% end -%>
|
63
|
+
<% if metadata.inception.date -%>
|
64
|
+
|-
|
65
|
+
| colspan="2" style="border: none; background: #fff; font-size: 1.15em; text-align: right;" | '''Position created''':
|
66
|
+
| style="border: none; background: #fff; text-align: left;" | <%= metadata.inception.date %>
|
67
|
+
| style="padding:0.5em 2em 0.5em 1em; border: none; background: #fff; text-align: left;" | \
|
68
|
+
<% metadata.inception.warnings.each do |warning| -%>
|
69
|
+
<span style="display: block">[[File:Pictogram voting comment.svg|15px|link=]] <span style="color: #d33; font-weight: bold; vertical-align: middle;"><%= warning.headline %></span> <ref><%= warning.explanation %></ref></span>\
|
70
|
+
<% end %>
|
71
|
+
<% end -%>
|
72
|
+
<% if metadata.predecessor.position -%>
|
44
73
|
|-
|
45
|
-
| colspan="
|
74
|
+
| colspan="2" style=" border: none; background: #fff; font-size: 1.15em; text-align: right;" | '''Replaces''':
|
75
|
+
| style="border: none; background: #fff; text-align: left;" | <%= metadata.predecessor.position %>
|
46
76
|
| style="padding:0.5em 2em 0.5em 1em; border: none; background: #fff; text-align: left;" | \
|
47
|
-
<%
|
77
|
+
<% metadata.predecessor.warnings.each do |warning| -%>
|
48
78
|
<span style="display: block">[[File:Pictogram voting comment.svg|15px|link=]] <span style="color: #d33; font-weight: bold; vertical-align: middle;"><%= warning.headline %></span> <ref><%= warning.explanation %></ref></span>\
|
49
79
|
<% end %>
|
50
80
|
<% end -%>
|
@@ -36,5 +36,5 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_development_dependency 'rubocop-performance', '~> 1.8.0'
|
37
37
|
spec.add_development_dependency 'rubocop-rspec', '~> 1.43.2'
|
38
38
|
spec.add_development_dependency 'warning', '~> 1.1'
|
39
|
-
spec.add_development_dependency 'webmock', '~> 3.
|
39
|
+
spec.add_development_dependency 'webmock', '~> 3.9.1'
|
40
40
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wikidata_position_history
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Bowden
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-09-
|
12
|
+
date: 2020-09-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mediawiki-replaceable-content
|
@@ -171,14 +171,14 @@ dependencies:
|
|
171
171
|
requirements:
|
172
172
|
- - "~>"
|
173
173
|
- !ruby/object:Gem::Version
|
174
|
-
version: 3.
|
174
|
+
version: 3.9.1
|
175
175
|
type: :development
|
176
176
|
prerelease: false
|
177
177
|
version_requirements: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
179
|
- - "~>"
|
180
180
|
- !ruby/object:Gem::Version
|
181
|
-
version: 3.
|
181
|
+
version: 3.9.1
|
182
182
|
description:
|
183
183
|
email:
|
184
184
|
- tony@tmtm.com
|
@@ -210,6 +210,7 @@ files:
|
|
210
210
|
- lib/sparql/position_query.rb
|
211
211
|
- lib/wikidata_position_history.rb
|
212
212
|
- lib/wikidata_position_history/checks.rb
|
213
|
+
- lib/wikidata_position_history/output_row.rb
|
213
214
|
- lib/wikidata_position_history/report.rb
|
214
215
|
- lib/wikidata_position_history/template.rb
|
215
216
|
- lib/wikidata_position_history/version.rb
|