Jump to content

Module:Wikidata table: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
unwind debug
add fallback from 'inception' to 'date of official opening'
Line 47: Line 47:
local df = "y"
local df = "y"


-- fallbacks for common properties
-- fallbacks for common properties:
-- property-to-fallback-from = "property-to-fallback-to"
local fallback = {
local fallback = {
P276 = "P131",
P276 = "P131",
P571 = "P1619",
}
}



Revision as of 00:16, 14 February 2021

--[[ Module:Wikidata table --]]

local p = {}

local debugging = true

-- Internationalisation
local i18n = {
	filespace = "File",
	editonwikidata = "Edit this on Wikidata",
}

local months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }
local mnths = {}
for idx, val in ipairs(months) do
	mnths[idx] = val:sub(1,3)
end

local unitsymbol = {
	inch = "in",
	foot = "ft",
	yard = "yd",
	mile = "mi",
	["nautical mile"] = "nmi",
	metre = "m",
	centimetre = "cm",
	millimetre = "mm",
	kilometre = "km",
}

-- prefixes for particular qualifiers
local prefix = {
	P580 = "from ",
	P582 = "until ",
}

-- error messages
local function errmsg(txt)
	if debugging then
		return "Error: " .. txt
	else
		return nil
	end
end

-- date format default to year
local df = "y"

-- fallbacks for common properties:
-- property-to-fallback-from = "property-to-fallback-to"
local fallback = {
	P276 = "P131",
	P571 = "P1619",
}

-- return a number rounded to a precision
local function decimalprecision(x, prec)
	local s = 1
	if x < 0 then
		x = -x
		s = -1
	end
	-- if prec is not suplied, pick an arbitrary precision
	if not tonumber(prec) then prec = 1e-4
	elseif prec > 1 then prec = 1
	elseif prec < 1e-6 then prec = 1e-6
	else prec = 10 ^ math.floor(math.log10(prec))
	end
	x = math.floor(x / prec + 0.5) * prec * s
	-- if it's integral, cast to an integer:
	if  x == math.floor(x) then x = math.floor(x) end
	-- if it's less than 1e-4, it will be in exponent form, so return a string with 6dp
	-- 9e-5 becomes 0.000090
	if math.abs(x) < 1e-4 then x = string.format("%f", x) end
	return x
end

-- creates an icon that links to the relevant Wikidata entity
local function createicon(entityID, propertyID, langcode)
	langcode = langcode or ""
	if not entityID or entityID == "" then entityID= mw.wikibase.getEntityIdForCurrentPage() end
	propertyID = propertyID or ""
	local icon = "&nbsp;<span class='penicon autoconfirmed-show'>[["
	-- "&nbsp;<span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
	.. i18n.filespace
	.. ":OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
	.. i18n.editonwikidata
	.. "|link=https://www.wikidata.org/wiki/" .. entityID
	if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
	if propertyID ~= "" then icon = icon .. "#" .. propertyID end
	icon = icon .. "|" .. i18n.editonwikidata .. "]]</span>"
	return icon
end

-- takes a qid and attempts to return a linked name for it
-- otherwise an unlinked name; otherwise the qid
function p._getLink(qid)
	local lbl = ""
	local slink = mw.wikibase.sitelink(qid)
	local label = mw.wikibase.getLabel(qid)
	if slink and label then
		if slink:lower() == label:lower() then
			lbl = "[[" .. slink .. "]]"
		else
			lbl = "[[" .. slink .. "|" .. label .. "]]"
		end
	elseif slink then
		lbl = "[[" .. slink .. "]]"
	elseif label then
		lbl = label
	else
		lbl = qid
	end
	return lbl
end
-- entrypoint for #invoke getLink
function p.getLink(frame)
	local qid = (frame.args.qid or ""):upper()
	if qid == "" then return nil end
	return p._getLink()
end

-- takes a qid and a property id and returns the corresponding values or empty string
-- maxvals greater than zero sets the maximum number of values to return
-- the string quals contains property ids of qualifiers to be returned ('-' is the separator)
function p._getWD(qid, pid, maxvals, quals)
	maxvals = maxvals or 0
	local ret = {}
	for idx, prop in ipairs(mw.wikibase.getBestStatements(qid, pid)) do
		if prop.mainsnak.snaktype ~= "value" then return nil end
		local dtype = prop.mainsnak.datatype
		local dval = prop.mainsnak.datavalue.value
		local retval
		if dtype == "wikibase-item" then
			retval = p._getLink(dval.id)
		elseif dtype == "monlingualtext" then
			retval = dval.text
		elseif dtype == "commonsMedia" or dtype == "external-id" or dtype == "string" or dtype == "url" then
			retval = dval
		elseif dtype == "time" then
			dval.time = dval.time:gsub("%-00%-00T", "-01-01T")
			local era = ""
			if dval.time:sub(1,1) == "-" then
				era = "&nbsp;BCE"
			end
			local year, month, day = dval.time:match("[+-](%d*)-(%d*)-(%d*)T")
			year, day = tonumber(year), tonumber(day)
			month = mnths[tonumber(month)]
			if df == "y" then
				retval = year .. era
			elseif df == "mdy" then
				retval = month .. " " .. day .. ", " .. year .. era
			else
				retval = day .. " " .. month .. " " .. year .. era
			end
		elseif dtype == "quantity" then
			local amount = tonumber(dval.amount)
			local unitqid = string.match( dval.unit, "(Q%d+)" )
			local unit = mw.wikibase.getLabel(unitqid)
			local symbol = unitsymbol[unit]
			if symbol then
				retval = mw.getCurrentFrame():expandTemplate{ title = "cvt", args = {amount, symbol} }
			else
				retval = amount  .. " " .. unit
			end
		elseif dtype == "globe-coordinate" then
			local lat = decimalprecision(dval.latitude, dval.precision)
			local long = decimalprecision(dval.longitude, dval.precision)
			retval = mw.getCurrentFrame():expandTemplate{ title="Coord", args={lat, long, format="dms"} }
		else
			retval = dval
		end
		if quals and prop.qualifiers and retval then
			local qtbl = {}
			for qpid in quals:gmatch("P%d+") do
				local qval = prop.qualifiers[qpid] and mw.wikibase.formatValue(prop.qualifiers[qpid][1])
				if qval then
					qtbl[#qtbl+1] = (prefix[qpid] or "") .. qval
				end
			end
			if #qtbl > 0 then
				retval = retval .. " (" .. table.concat(qtbl, " ") .. ")"
			end
		end
		ret[#ret+1] = retval
		if maxvals > 0 and #ret >= maxvals then break end
	end
	return table.concat(ret, "<br>")
end
-- entrypoint for #invoke getWD
function p.getWD(frame)
	local qid = (frame.args.qid or ""):upper()
	if qid == "" then return nil end
	local pid = (frame.args.pid or ""):upper()
	if pid == "" then return nil end
	local maxvals = tonumber(frame.args.maxvals) or 0
	local quals = (frame.args.quals or ""):upper()
	if quals == "" then quals = nil end
	return p._getWD(qid, pid, maxvals, quals)
end

-- make multiple table rows, one for each qid in args.qids,
-- with one table cell for each pid in args.pids
function p._makerows(args)
	args.qids = (args.qids or ""):upper()
	if args.qids == "" then return errmsg("missing qids") end
	args.pids = (args.pids or ""):upper()
	if args.pids == "" then return errmsg("missing pids") end

	local qids, pids = {}, {}
	for qid in args.qids:gmatch("Q%d+") do
		qids[#qids+1] = qid
	end
	for pid in args.pids:gmatch("P%d+") do
		pids[#pids+1] = pid
	end

	args.df = args.df or ""
	if args.df ~= "" then df = args.df end

	local out = ""
	for r, qid in ipairs(qids) do
		out = out .. "<tr>"
		out = out .. "<th scope='row'>" .. p._getLink(qid) .. "</th>"
		for c, pid in ipairs(pids) do
			if pid == "P18" then -- image
				local img = p._getWD(qid, pid, 1)
				if img then
					out = out .. "<td>[[File:" .. img .. "|120px]]</td>"
				end
			else
				out = out .. "<td>" .. p._getWD(qid, pid, 0) .. "</td>"
			end
		end
		out = out .. "</tr>"
	end

	return out
end
-- entry point for #invoke makerows
function p.makerows(frame)
	local args = {}
	for key, value in pairs(frame:getParent().args) do
		args[key] = value
	end
	for key, value in pairs(frame.args) do
		args[key] = value
	end
	return p._makerows(args)
end

-- make a single table row, one cell per value passed in args.pids
-- each value may be a combination of properties and qualifiers
function p._makerow(args)
	local qid = (args.qid or ""):upper():match("Q%d+")
	if not qid then return errmsg("missing qid") end
	-- remove whitespace, uppercase, trap nil
	args.pids = (args.pids or ""):upper():gsub("%s", "")
	if args.pids == "" then return errmsg("missing pids") end
	-- collect any parameters cr1, cr2, etc. as cell replacements; ca1, ca2, etc. as addenda
	local cellrep, celladd = {}, {}
	for key, value in pairs(args) do
		local col = (type(key) == "string") and tonumber(key:match("^[Cc]r(%d+)$"))
		if col then
			cellrep[col] = value
		end
	end
	for key, value in pairs(args) do
		local col = (type(key) == "string") and tonumber(key:match("^[Cc]a(%d+)$"))
		if col then
			celladd[col] = value
		end
	end
	-- set date format if passed
	args.df = args.df or ""
	if args.df ~= "" then df = args.df end
	-- create the html to return
	local out = "<tr>"
	if cellrep[1] then
		out = out .. "<th scope='row'>" .. cellrep[1] .. createicon(qid) .. "</th>"
	else
		out = out .. "<th scope='row'>" .. p._getLink(qid) .. (celladd[1] or "") .. createicon(qid) .. "</th>"
	end
	-- split args.pids at comma separators into table of cellpids (may be like P12+P34/P456-P789)
	local cellpids = mw.text.split(args.pids, ",+")
	for c, val in ipairs(cellpids) do
		if cellrep[c+1] then
			out = out .. "<td>" .. cellrep[c+1] .. "</td>"
		else
			-- separate properties from qualifiers with /
			local props = mw.text.split(val, "/")[1]
			local quals = mw.text.split(val, "/")[2]
			local ptbl = {} -- table of property values
			for pid in props:gmatch("P%d+", quals) do
				if pid == "P18" then -- image
					local img = p._getWD(qid, pid, 1)
					if img then
						ptbl[#ptbl+1] = "[[File:" .. img .. "|120px]]"
					end
				else
					local wdval = p._getWD(qid, pid, 0, quals)
					if wdval == "" and fallback[pid] then
						wdval = p._getWD(qid, fallback[pid], 0, quals)
					end
					ptbl[#ptbl+1] = wdval
				end
			end
			out = out .. "<td>" .. table.concat(ptbl, "<br>") .. (celladd[c+1] or "") .. "</td>"
		end
	end
	out = out .. "</tr>"
	return out
end
-- entry point for #invoke makerow
function p.makerow(frame)
	local args = {}
	for key, value in pairs(frame:getParent().args) do
		args[key] = value
	end
	for key, value in pairs(frame.args) do
		args[key] = value
	end
	return p._makerow(args)
end

return p