Jump to content

Module:Convert/wikidata/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Johnuniq (talk | contribs) at 11:53, 16 May 2016 (handle "not known to convert" units; fix listunits to work with new data format, and check syntax). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
-- Functions to access Wikidata for Module:Convert.

local function collection()
	-- Return a table to hold items.
	return {
		n = 0,
		add = function (self, item)
			self.n = self.n + 1
			self[self.n] = item
		end,
		join = function (self, sep)
			return table.concat(self, sep)
		end,
	}
end

local function strip_to_nil(text)
	-- If text is a non-empty string, return its trimmed content,
	-- otherwise return nothing (empty string or not a string).
	if type(text) == 'string' then
		return text:match('(%S.-)%s*$')
	end
end

local function make_unit(units, parms, unit_url)
	-- Return a unit code for convert or nil if unit unknown.
	-- If necessary, add a dummy unit to parms so convert will use it
	-- for the input without attempting a conversion since nothing
	-- useful is available (for example, with unit volt).
	if type(unit_url) ~= 'string' then
		return nil
	end
	local unit = units[unit_url:match('Q%d+$')]
	if type(unit) ~= 'table' then
		return nil
	end
	if unit.ucode then
		return unit.ucode  -- a unit known to convert
	end
	parms.opt_ignore_error = true
	local ucode = unit._ucode       -- must be a non-empty string
	local ukey, utable
	if unit.si then
		local base = units[unit.si]
		ukey = base.symbol          -- must be a non-empty string
		local n1 = base.name1
		local n2 = base.name2
		if not n1 then
			n1 = ukey
			if not n2 then
				n2 = n1             -- do not append 's'
			end
		end
		utable = {
			_symbol = ukey,
			_name1 = n1,
			_name2 = n2,
			link = unit.link or base.link,
			utype = n1,
			prefixes = 1,
		}
	else
		ukey = ucode
		utable = {
			symbol = ucode,         -- must be a non-empty string
			name1 = unit.name1,     -- if nil, uses symbol
			name2 = unit.name2,     -- if nil, uses name1..'s'
			link = unit.link,       -- if nil, uses name1
			utype = unit.name1 or unit.symbol,
		}
	end
	local common = {
		scale = 1,
		default = '',
		defkey = '',
		linkey = '',
		bad_mcode = '',
	}
	for k, v in pairs(common) do
		utable[k] = v
	end
	parms.unittable = { [ukey] = utable }
	return ucode
end

local function adjustparameters(tdata, parms, pid, index)
	-- For Module:Convert, adjust parms (a table of {{convert}} parameters).
	-- Return true if successful or return false, t where t is an error message table.
	-- Given that pid is a Wikidata property identifier like 'P123',
	-- try to find a value and unit for the pid.
	-- If successful, replace parms[index] with the value and
	-- insert the unit after index.
	local qid = strip_to_nil(parms.qid)  -- nil (item is current page) or requested id (expensive)
	local entity = mw.wikibase.getEntity(qid)
	local claims = ((entity or {}).claims or {})[pid]
	if claims and claims[1] then
		local first = claims[1]  -- TODO taking the first claim is not adequate
		if first.mainsnak and first.mainsnak.datatype == 'quantity' then
			local value = (first.mainsnak.datavalue or {}).value
			if value then
				local amount = tostring(value.amount)
				local ucode = make_unit(tdata.wikidata_units, parms, value.unit)
				if amount and ucode then
					if amount:sub(1, 1) == '+' then
						amount = amount:sub(2)
					end
					parms[index] = amount
					table.insert(parms, index + 1, ucode)
					return true
				end
			end
		end
	end
	return false, { 'cvt_wd_property', tostring(pid),
		qid and (' for item ' .. tostring(qid)) or '' }
end

--------------------------------------------------------------------------------
--- List units and check syntax of definitions ---------------------------------
--------------------------------------------------------------------------------
local specifications = {
	-- seq = sequence in which fields are displayed
	base = {
		title = 'SI base units',
		fields = {
			symbol = { seq = 2, mandatory = true },
			name1  = { seq = 3, mandatory = true },
			name2  = { seq = 4 },
			link   = { seq = 5 },
		},
		noteseq = 6,
		header = '{| class="wikitable"\n!si !!symbol !!name1 !!name2 !!link !!note',
		item = '|-\n|%s ||%s ||%s ||%s ||%s ||%s',
		footer = '|}',
	},
	known = {
		title = 'Units known to convert',
		fields = {
			ucode  = { seq = 2, mandatory = true },
			label  = { seq = 3, mandatory = true },
		},
		noteseq = 4,
		header = '{| class="wikitable"\n!qid !!ucode !!label !!note',
		item = '|-\n|%s ||%s ||%s ||%s',
		footer = '|}',
	},
	unknown = {
		title = 'Units not known to convert',
		fields = {
			_ucode = { seq = 2, mandatory = true },
			si     = { seq = 3, si = true },
			name1  = { seq = 4 },
			name2  = { seq = 5 },
			link   = { seq = 6 },
			label  = { seq = 7, mandatory = true },
		},
		noteseq = 8,
		header = '{| class="wikitable"\n!qid !!_ucode !!base !!name1 !!name2 !!link !!label !!note',
		item = '|-\n|%s ||%s ||%s ||%s ||%s ||%s ||%s ||%s',
		footer = '|}',
	},
}

local function listunits(tdata, ulookup)
	-- For Module:Convert, make wikitext to list the built-in Wikidata units.
	-- Return true, wikitext if successful or return false, t where t is an
	-- error message table. Currently, an error return never occurs.
	-- The syntax of each unit definition is checked and a note is added if
	-- a problem is detected.
	local wdunits = tdata.wikidata_units
	for _, s in ipairs({ 'base', 'unknown', 'known' }) do
		specifications[s].units = collection()
	end
	local keys, n = {}, 0
	for k, v in pairs(wdunits) do
		n = n + 1
		keys[n] = k
	end
	table.sort(keys)
	for _, key in ipairs(keys) do
		local unit = wdunits[key]
		local s
		if key:match('^Q%d+$') then
			if unit.ucode then
				s = 'known'
			else
				s = 'unknown'
			end
		else
			s = 'base'
		end
		local result = { key }
		local spec = specifications[s]
		local fields = spec.fields
		local note = collection()
		for k, v in pairs(unit) do
			if fields[k] then
				local seq = fields[k].seq
				if result[seq] then
					note:add('duplicate ' .. k)  -- cannot happen since keys are unique
				else
					result[seq] = v
				end
			else
				note:add('invalid ' .. k)
			end
		end
		for k, v in pairs(fields) do
			local seq = v.seq
			if result[seq] then
				if v.si and not wdunits[result[seq]] then
					note:add('need si ' .. result[seq])
				end
			else
				result[seq] = ''
				if v.mandatory then
					note:add('missing ' .. k)
				end
			end
		end
		result[spec.noteseq] = note:join('<br />')
		spec.units:add(result)
	end
	local results = collection()
	for _, s in ipairs({ 'base', 'unknown', 'known' }) do
		local spec = specifications[s]
		results:add("'''" .. spec.title .. "'''")
		results:add(spec.header)
		local fmt = spec.item
		for _, unit in ipairs(spec.units) do
			results:add(string.format(fmt, unpack(unit)))
		end
		results:add(spec.footer)
	end
	return true, results:join('\n')
end

return { _adjustparameters = adjustparameters, _listunits = listunits }