Content deleted Content added
tweak |
enhance per request at Template talk:Convert#Wikidata qualifiers; |input=P123|qual=Q456 gets only the first statement which "applies to part" (P518) the qualifier; if no qual, get all statements |
||
Line 1:
-- Functions to access Wikidata for Module:Convert.
local
Collection.__index = Collection
do
function Collection:add(item)
if item ~= nil then
self.n = self.n + 1
self[self.n] = item
end
end
function Collection:join(sep)
return table.concat(self, sep)
function Collection:remove(pos)
if self.n > 0 and (pos == nil or (0 < pos and pos <= self.n)) then
self.n = self.n - 1
return table.remove(self, pos)
end
end
function Collection:sort(comp)
table.sort(self, comp)
end
function Collection.new()
return setmetatable({n = 0}, Collection)
end
end
Line 75 ⟶ 87:
end
local function
-- Return:
-- false, nil : if statement does not match specification
-- true, nil : if matches, and statement has no qualifier
--
-- A match means that no qualifier was specified (qual == nil), or that
-- the statement has a qualifier matching the specification.
-- If a match occurs, the caller needs the statement's qualifier (if any)
-- so statements that duplicate the qualifier are not used, after the first.
-- Then, if convert is showing all values for a property such as the diameter
-- of a telescope's mirror (diameters of primary and secondary mirrors), it
-- will not show alternative values that could in principle be present for the
-- same item (telescope) and property (diameter) and qualifier (primary/secondary).
local target = (statement.qualifiers or {}).P518 -- P518 is "applies to part"
if type(target) == 'table' then
for _, q in ipairs(target) do
if type(q) == 'table' then
local value = (q.datavalue or {}).value
if value then
if qual == nil or qual == value.id then
return true, value.id
end
end
end
end
end
if qual == nil then
return true, nil -- only occurs if statement has no qualifier
end
return false, nil -- statement's qualifier is not relevant because statement will be skipped
end
local function get_statements(parms, pid)
-- Get specified item and return a list of tables with each statement for property pid.
-- Each table is of form {statqual=sq, stmt=statement} where sq = statement qualifier (nil if none).
-- Statements are in Wikidata's order except that those with preferred rank
-- are first, then normal rank. Any other rank is ignored.
local stored = {} -- qualifiers of statements that are first for the qualifier, and will be returned
local qid = strip_to_nil(parms.qid) -- nil for current page's item, or an item id (expensive)
local qual = strip_to_nil(parms.qual) -- nil or id of wanted P518 (applies to part) item in qualifiers
local result = Collection.new()
local entity = mw.wikibase.getEntity(qid)
if type(entity) == 'table' then
Line 88 ⟶ 135:
for _, statement in ipairs(statements) do
if type(statement) == 'table' and rank == statement.rank then
local is_match, statqual = matches_qualifier(statement, qual)
result:add({ statqual = statqual, stmt = statement })
end
end
end
Line 98 ⟶ 147:
end
local function input_from_property(tdata, parms
-- Given that pid is a Wikidata property identifier like 'P123',
-- return a collection of {amount, ucode} pairs (two strings)
-- for each matching item/property, or return nothing.
--------------------------------------------------------------------------------
-- There appear to be few restrictions on how Wikidata is organized so it is
-- very likely that any decision a module makes about how to handle data
-- will be wrong for some cases at some time. This meets current requirements.
-- For each qualifier (or if no qualitifer), if there are any preferred
-- statements, use them and ignore any normal statements.
-- For each qualifier, for the preferred statements if any, or for
-- the normal statements (but not both):
-- * Accept each statement if it has no qualifier (this will not occur
-- if qual=x is specified because other code already ensures that in that
-- case, only statements with a qualifier matching x are considered).
-- * Ignore any statements after the first if it has a qualifier.
-- The rationale is that for the diameter at [[South Pole Telescope]], want
-- convert to show the diameters for both the primary and secondary mirrors
-- if the convert does not specify which diameter is wanted.
-- However, if convert is given the wanted qualifier, only one value
-- (_the_ diameter) is wanted. For simplicity/consistency, that is also done
-- even if no qual=x is specified. Unclear what should happen.
-- For the wavelength at [[Nançay Radio Telescope]], want to show all three
-- values, and the values have no qualifiers.
--------------------------------------------------------------------------------
local result = Collection.new()
local done = {}
local skip_normal
for _, t in ipairs(get_statements(parms, pid)) do
local statement = t.stmt
if statement.mainsnak and statement.mainsnak.datatype == 'quantity' then
local value = (statement.mainsnak.datavalue or {}).value
Line 114 ⟶ 188:
local unit = value.unit
if type(unit) == 'string' then
unit = unit:match('Q%d+$') -- unit
local ucode = make_unit(tdata.wikidata_units, parms, unit)
if ucode then
if t.statqual then
if done[t.statqual] then
skip = true
else
done[t.statqual] = true
end
else
if statement.rank == 'preferred' then
skip_normal = true
elseif skip_normal then
skip = true
end
end
if not skip then
result:add({ amount, ucode })
end
end
end
Line 124 ⟶ 214:
end
end
return result
end
Line 145 ⟶ 236:
-- This is intended mainly for use in infoboxes where the input might be
-- <value><space><unit> or
-- <wikidata-property-id>
-- If successful, insert
local text = parms.input -- should be a trimmed, non-empty string
local pid = text:match('^P%d+$')
local function quit()
return false, pid and { 'cvt_no_output' } or { 'cvt_bad_input', text }
end
if pid then
parms.input_text = '' -- output an empty string if an error occurs
if result.n == 0 then
return quit()
for i, t in ipairs(result) do
-- Convert requires each input unit to be identical.
if i == 1 then
ucode = t[2]
elseif ucode ~= t[2] then
return quit()
end
end
local item = ucode
for i = result.n, 1, -1 do
table.insert(parms, index, item)
table.insert(parms, index, result[i][1])
item = parms.test == '#' and '#' or ',' -- TODO remove choice when decide which is wanted
end
return true
else
local amount, ucode = input_from_text(tdata, parms, text)
if amount and ucode then
table.insert(parms, index, ucode)
table.insert(parms, index, amount)
return true
end
end
return quit()
end
Line 245 ⟶ 356:
local speckeys = { 'base', 'alias', 'unknown', 'known' }
for _, sid in ipairs(speckeys) do
specifications[sid].units =
end
local keys
for k, v in pairs(wdunits) do
keys:add(k)
end
table.sort(keys)
Line 274 ⟶ 384:
local spec = specifications[sid]
local fields = spec.fields
local note =
for k, v in pairs(unit) do
if fields[k] then
Line 314 ⟶ 424:
spec.units:add(result)
end
local results =
if note_count > 0 then
local text = note_count .. (note_count == 1 and ' note' or ' notes')
|