wikidata_position_history 1.11.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 +15 -0
- data/exe/position-history-for-item +1 -1
- data/lib/sparql/bio_query.rb +16 -0
- data/lib/sparql/mandates_query.rb +24 -5
- data/lib/sparql/position_query.rb +20 -1
- data/lib/wikidata_position_history/checks.rb +16 -14
- data/lib/wikidata_position_history/output_row.rb +2 -2
- data/lib/wikidata_position_history/report.rb +62 -17
- data/lib/wikidata_position_history/template.rb +1 -1
- data/lib/wikidata_position_history/version.rb +1 -1
- metadata +2 -2
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,5 +1,20 @@
|
|
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
|
+
|
3
18
|
# [1.11.0] 2020-09-14
|
4
19
|
|
5
20
|
## Enhancements
|
data/lib/sparql/bio_query.rb
CHANGED
@@ -21,6 +21,22 @@ module WikidataPositionHistory
|
|
21
21
|
SPARQL
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
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
|
39
|
+
end
|
24
40
|
end
|
25
41
|
|
26
42
|
# Represents a single row returned from the Position query
|
@@ -25,6 +25,30 @@ module WikidataPositionHistory
|
|
25
25
|
SPARQL
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
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
|
51
|
+
end
|
28
52
|
end
|
29
53
|
|
30
54
|
# Represents a single row returned from the Mandates query
|
@@ -37,11 +61,6 @@ module WikidataPositionHistory
|
|
37
61
|
item_from(:item)
|
38
62
|
end
|
39
63
|
|
40
|
-
# TODO: rename or remove. 'item' is meaningless/ambiguous
|
41
|
-
def item
|
42
|
-
officeholder.qlink
|
43
|
-
end
|
44
|
-
|
45
64
|
# TODO: switch to item_from
|
46
65
|
def prev
|
47
66
|
QueryService::WikidataItem.new(row.dig(:prev, :value)).qlink
|
@@ -11,10 +11,13 @@ module WikidataPositionHistory
|
|
11
11
|
SELECT DISTINCT ?item ?inception ?inception_precision ?abolition ?abolition_precision
|
12
12
|
?replaces ?replacedBy ?derivedReplaces ?derivedReplacedBy
|
13
13
|
?isPosition ?isLegislator
|
14
|
+
?isConstituency ?representative_count ?legislature
|
14
15
|
WHERE {
|
15
16
|
VALUES ?item { wd:%s }
|
16
17
|
BIND(EXISTS { wd:%s wdt:P279+ wd:Q4164871 } as ?isPosition)
|
17
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
|
+
|
18
21
|
OPTIONAL { ?item p:P571 [ a wikibase:BestRank ;
|
19
22
|
psv:P571 [ wikibase:timeValue ?inception; wikibase:timePrecision ?inception_precision ]
|
20
23
|
] }
|
@@ -25,12 +28,16 @@ module WikidataPositionHistory
|
|
25
28
|
OPTIONAL { ?item wdt:P1366 ?replacedBy }
|
26
29
|
OPTIONAL { ?derivedReplaces wdt:P1366 ?item }
|
27
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
|
+
}
|
28
35
|
}
|
29
36
|
SPARQL
|
30
37
|
end
|
31
38
|
|
32
39
|
def sparql_args
|
33
|
-
[itemid] *
|
40
|
+
[itemid] * 4
|
34
41
|
end
|
35
42
|
end
|
36
43
|
end
|
@@ -72,5 +79,17 @@ module WikidataPositionHistory
|
|
72
79
|
def legislator?
|
73
80
|
raw(:isLegislator) == 'true'
|
74
81
|
end
|
82
|
+
|
83
|
+
def constituency?
|
84
|
+
raw(:isConstituency) == 'true'
|
85
|
+
end
|
86
|
+
|
87
|
+
def legislature
|
88
|
+
item_from(:legislature)
|
89
|
+
end
|
90
|
+
|
91
|
+
def representative_count
|
92
|
+
raw(:representative_count).to_i
|
93
|
+
end
|
75
94
|
end
|
76
95
|
end
|
@@ -17,11 +17,12 @@ 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
|
|
@@ -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
|
-
earlier_holder? && !!
|
96
|
+
earlier_holder? && !!predecessor_qlink && (earlier.officeholder.qlink != predecessor_qlink)
|
96
97
|
end
|
97
98
|
|
98
99
|
def headline
|
@@ -100,14 +101,14 @@ 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"
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
107
108
|
# Is there a 'replaces' but no previous item in the list?
|
108
109
|
class MissingPredecessor < Check
|
109
110
|
def problem?
|
110
|
-
|
111
|
+
predecessor_qlink && !earlier_holder?
|
111
112
|
end
|
112
113
|
|
113
114
|
def headline
|
@@ -115,14 +116,14 @@ module WikidataPositionHistory
|
|
115
116
|
end
|
116
117
|
|
117
118
|
def possible_explanation
|
118
|
-
"#{current.
|
119
|
+
"#{current.officeholder.qlink} has a {{P|1365}} of #{predecessor_qlink}, but does not follow anyone here"
|
119
120
|
end
|
120
121
|
end
|
121
122
|
|
122
123
|
# Does the 'replaced by' match the next item in the list?
|
123
124
|
class WrongSuccessor < Check
|
124
125
|
def problem?
|
125
|
-
later_holder? && !!
|
126
|
+
later_holder? && !!successor_qlink && (later.officeholder.qlink != successor_qlink)
|
126
127
|
end
|
127
128
|
|
128
129
|
def headline
|
@@ -130,14 +131,14 @@ module WikidataPositionHistory
|
|
130
131
|
end
|
131
132
|
|
132
133
|
def possible_explanation
|
133
|
-
"#{current.
|
134
|
+
"#{current.officeholder.qlink} has a {{P|1366}} of #{successor_qlink}, but is followed by #{later.officeholder.qlink} here"
|
134
135
|
end
|
135
136
|
end
|
136
137
|
|
137
138
|
# Is there a 'replaced by' but no next item in the list?
|
138
139
|
class MissingSuccessor < Check
|
139
140
|
def problem?
|
140
|
-
|
141
|
+
successor_qlink && !later_holder?
|
141
142
|
end
|
142
143
|
|
143
144
|
def headline
|
@@ -145,7 +146,7 @@ module WikidataPositionHistory
|
|
145
146
|
end
|
146
147
|
|
147
148
|
def possible_explanation
|
148
|
-
"#{current.
|
149
|
+
"#{current.officeholder.qlink} has a {{P|1366}} of #{successor_qlink}, but is not followed by anyone here"
|
149
150
|
end
|
150
151
|
end
|
151
152
|
|
@@ -167,7 +168,8 @@ module WikidataPositionHistory
|
|
167
168
|
end
|
168
169
|
|
169
170
|
def possible_explanation
|
170
|
-
|
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)
|
171
173
|
end
|
172
174
|
|
173
175
|
protected
|
@@ -68,6 +68,15 @@ module WikidataPositionHistory
|
|
68
68
|
rows.map(&:legislator?).first
|
69
69
|
end
|
70
70
|
|
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
|
+
|
71
80
|
def replaces_combined
|
72
81
|
@replaces_combined ||= ImpliedList.new(uniq_by_id(:replaces), uniq_by_id(:derived_replaces))
|
73
82
|
end
|
@@ -93,6 +102,48 @@ module WikidataPositionHistory
|
|
93
102
|
end
|
94
103
|
end
|
95
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
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
96
147
|
# The entire wikitext generated for this report
|
97
148
|
class Report
|
98
149
|
def initialize(position_id, template_class = ReportTemplate)
|
@@ -104,26 +155,12 @@ module WikidataPositionHistory
|
|
104
155
|
|
105
156
|
def wikitext
|
106
157
|
return legislator_template if metadata.legislator?
|
158
|
+
return config.multimember_error_template if metadata.constituency? && (metadata.representative_count != 1)
|
107
159
|
return no_items_output if mandates.empty?
|
108
160
|
|
109
161
|
template_class.new(template_params).output
|
110
162
|
end
|
111
163
|
|
112
|
-
def header
|
113
|
-
"== {{Q|#{position_id}}} officeholders #{position_dates} =="
|
114
|
-
end
|
115
|
-
|
116
|
-
def position_dates
|
117
|
-
dates = [metadata.inception.date, metadata.abolition.date]
|
118
|
-
return '' if dates.compact.empty?
|
119
|
-
|
120
|
-
format('(%s)', dates.join(' – '))
|
121
|
-
end
|
122
|
-
|
123
|
-
def wikitext_with_header
|
124
|
-
[header, wikitext].join("\n")
|
125
|
-
end
|
126
|
-
|
127
164
|
def template_params
|
128
165
|
{
|
129
166
|
metadata: metadata,
|
@@ -139,7 +176,7 @@ module WikidataPositionHistory
|
|
139
176
|
end
|
140
177
|
|
141
178
|
def biodata
|
142
|
-
@biodata ||=
|
179
|
+
@biodata ||= biodata_sparql.results_as(BioRow)
|
143
180
|
end
|
144
181
|
|
145
182
|
def biodata_for(officeholder)
|
@@ -150,8 +187,16 @@ module WikidataPositionHistory
|
|
150
187
|
[nil, mandates, nil].flatten(1)
|
151
188
|
end
|
152
189
|
|
190
|
+
def config
|
191
|
+
@config ||= ReportConfigFactory.config(metadata)
|
192
|
+
end
|
193
|
+
|
153
194
|
def sparql
|
154
|
-
@sparql ||=
|
195
|
+
@sparql ||= config.mandates_query.new(position_id)
|
196
|
+
end
|
197
|
+
|
198
|
+
def biodata_sparql
|
199
|
+
config.biodata_query.new(position_id)
|
155
200
|
end
|
156
201
|
|
157
202
|
def mandates
|
@@ -49,7 +49,7 @@ module WikidataPositionHistory
|
|
49
49
|
|-
|
50
50
|
| style="padding:0.5em 2em" | <%= mandate.ordinal_string %>
|
51
51
|
| style="padding:0.5em 2em" | <%= bio.map(&:image_link).first %>
|
52
|
-
| 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 %>
|
53
53
|
| style="padding:0.5em 2em 0.5em 1em; border: none; background: #fff; text-align: left;" | \
|
54
54
|
<% mandate.warnings.each do |warning| -%>
|
55
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>\
|
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
|