Module:Format TemplateData/global
Appearance
local Export = {
item = 51435481,
serial = "2020-08-01",
subpages = "TemplateData",
suffix = "tab",
suite = "TemplateDataGlobal"
}
--[=[
Retrieve TemplateData from Commons:Data (or other global source).
require()
Inspired by [[de:User:Yurik]].
]=]
local Failsafe = Export
local failsafe = function(atleast)
-- Retrieve versioning and check for compliance.
-- Precondition:
-- atleast -- string, with required version
-- or "wikidata" or "~" or "@" or false
-- Postcondition:
-- Returns string -- with queried version/item, also if problem
-- false -- if appropriate
local last = (atleast == "~")
local link = (atleast == "@")
local since = atleast
local r
if last or link or since == "wikidata" then
local item = Failsafe.item
since = false
if type(item) == "number" and item > 0 then
local suited = string.format("Q%d", item)
local entity = mw.wikibase.getEntity(suited)
if type(entity) == "table" then
local seek = Failsafe.serialProperty or "P348"
local vsn = entity:formatPropertyValues(seek)
if type(vsn) == "table" and type(vsn.value) == "string" and vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false
elseif link then
if mw.title.getCurrentTitle().prefixedText == mw.wikibase.getSitelink(suited) then
r = false
else
r = suited
end
else
r = vsn.value
end
end
end
end
end
if type(r) == "nil" then
if not since or since <= Failsafe.serial then
r = Failsafe.serial
else
r = false
end
end
return r
end -- failsafe()
local function fair(already, adapt, append)
-- Merge local definitions into global base.
-- Parameter:
-- already -- global item
-- adapt -- local override item
-- append -- append to sequence table
-- Returns merged item
local r
if already and adapt then
if type(already) == "table" and type(adapt) == "table" then
r = already
if append then
for i = 1, #adapt do
table.insert(r, adapt[i])
end -- for i
else
for k, v in pairs(adapt) do
r[k] = v
end -- for k, v
end
else
r = adapt
end
else
r = already or adapt
end
return r
end -- fair()
local function feed(apply)
-- Retrieve override from JSON code.
-- Parameter:
-- apply -- string, with JSON
-- Returns string, with error message, or table
local lucky, r = pcall(mw.text.jsonDecode, apply)
if not lucky then
r = "fatal JSON error in LOCAL override"
end
return r
end -- feed()
local function find(access)
-- Fetch data from page.
-- Parameter:
-- access -- string, with core page name
-- Returns
-- 1. string, with prefixed page name
-- 2. table with JSON data, or error message
local storage = access
local lucky, r
if Export.suffix and not storage:find(".", 2, true) then
local k = -1 - #Export.suffix
if storage:sub(k) ~= "." .. Export.suffix then
storage = string.format("%s.%s", storage, Export.suffix)
end
end
if Export.subpages and not storage:find("/", 1, true) then
storage = string.format("%s/%s", Export.subpages, storage)
end
lucky, r = pcall(mw.ext.data.get, storage, "_")
storage = "Data:" .. storage
if mw.site.siteName ~= "Wikimedia Commons" then
storage = "commons:" .. storage
end
if type(r) ~= "table" and type(r) ~= "string" then
r = "INVALID"
end
return storage, r
end -- find()
local function flat(apply)
-- Convert tabular data into TemplateData.
-- Parameter:
-- apply -- table, with tabular data
-- Returns string, with error message, or table, with TemplateData
local r, scream
local function failed(at, alert)
if scream then
scream = string.format("%s * #%d: %s", scream, at, alert)
else
scream = add
end
end -- failed()
if type(apply.schema) == "table" and type(apply.schema.fields) == "table" and type(apply.data) == "table" then
local order = {}
local entry, got, params, parOrder, s, sign, td, v
for k, v in pairs(apply.schema.fields) do
if type(v) == "table" then
table.insert(order, v.name)
end
end -- for k, v
for i = 1, #apply.data do
entry = apply.data[i]
if type(entry) == "table" then
got = {}
sign = false
for j = 1, #entry do
s = order[j]
v = entry[j]
if type(v) == "string" then
v = mw.text.trim(v)
if v == "" then
v = false
end
end
if v then
if s == "name" then
sign = v
elseif s == "aliases" then
if type(v) == "string" then
got.aliases = mw.text.split(v, "%s*|%s*")
else
failed(i, "aliases not a string")
end
else
got[s] = v
end
end
end -- for j
if sign == "|" then
if td then
failed(i, "root repeated")
else
td = {description = got.description}
if type(got.type) == "string" then
td.format = got.type:gsub("N", "\n")
end
end
elseif sign then
if params then
if params[sign] then
failed(i, "name repeated: " .. sign)
end
else
params = {}
parOrder = {}
end
params[sign] = got
table.insert(parOrder, sign)
else
failed(i, "missing name")
end
else
failed(i, "invalid component")
end
end -- for i
r = td or {}
r.params = params
r.paramOrder = parOrder
else
r = "bad tabular structure"
end
return scream or r or "EMPTY"
end -- flat()
local function flush(assembly, avoid)
-- Remove element from sequence table.
-- Parameter:
-- assembly -- sequence table
-- avoid -- element
for i = 1, #assembly do
if assembly[i] == avoid then
table.remove(assembly, i)
break -- for i
end
end -- for i
end -- flush()
local function fold(already, adapt)
-- Merge local parameter definitions into global base.
-- Parameter:
-- already -- table, with global data
-- adapt -- sequence table, with local params overrides
-- Returns string, with error message, or table, with TemplateData
local order = {}
local params = {}
local r = already
local entry, override, s
r.params = r.params or {}
r.paramOrder = r.paramOrder or {}
for i = 1, #adapt do
override = adapt[i]
if type(override) ~= "table" then
r = string.format("No object at LOCAL params[%d]", i)
break -- for i
elseif type(override.global) == "string" then
s = override.global
entry = r.params[s]
if type(entry) == "table" then
flush(r.paramOrder, s)
if type(override["local"]) == "string" then
s = override["local"]
override["local"] = nil
elseif override["local"] == false then
entry = nil
end
if entry then
override.global = nil
for k, v in pairs(override) do
entry[k] = fair(entry[k], override[k], (k == "aliases"))
end -- for k, v
table.insert(order, s)
end
params[s] = entry
else
r = string.format("No GLOBAL params %s for LOCAL [%d]", s, i)
break -- for i
end
elseif type(override["local"]) == "string" then
s = override["local"]
override["local"] = nil
params[s] = override
table.insert(order, s)
else
r = string.format("No name for LOCAL params[%d]", i)
break -- for i
end
end -- for i
if type(r) == "table" then
for i = 1, #r.paramOrder do
s = r.paramOrder[i]
params[s] = r.params[s]
table.insert(order, s)
end -- for i
r.params = params
r.paramOrder = order
end
return r
end -- fold()
local function fork(already, adapt)
-- Merge local definitions into global base.
-- Parameter:
-- already -- table, with global data
-- adapt -- table, with local overrides
-- Returns string, with error message, or table, with TemplateData
local root = {"description", "format", "maps", "sets", "style"}
local r = already
for k, v in pairs(root) do
if adapt[v] then
r[v] = fair(r[v], adapt[v])
end
end -- for k, v
if type(adapt.params) == "table" then
r = fold(r, adapt.params)
end
return r
end -- fork()
local function furnish(apply, at, adapt)
-- Convert external data into TemplateData.
-- Parameter:
-- apply -- table, with external data
-- at -- string, with page name
-- adapt -- JSON string or table or not, with local overrides
-- Returns string, with error message, or table, with TemplateData
local r
if at:sub(-4) == ".tab" then
r = flat(apply)
else
r = "Unknown page format: " .. at
end
if adapt and type(r) == "table" then
local override = adapt
if type(adapt) == "string" then
override = feed(adapt)
if type(override) == "string" then
r = override
end
end
if type(override) == "table" then
r = fork(r, override)
end
end
return r
end -- furnish()
Export.failsafe = function(frame)
-- Versioning interface.
local s = type(frame)
local since
if s == "table" then
since = frame.args[1]
elseif s == "string" then
since = frame
end
if since then
since = mw.text.trim(since)
if since == "" then
since = false
end
end
return failsafe(since) or ""
end -- Export.failsafe()
Export.fetch = function(access, adapt)
-- Fetch data from site.
-- Parameter:
-- access -- string, with page specification
-- adapt -- JSON string or table or not, with local overrides
-- Returns
-- 1. string, with error message or prefixed page name
-- 2. table with TemplateData, or not
local storage, t = find(access)
local s
if type(t) == "table" then
t = furnish(t, storage, adapt)
if type(t) ~= "table" then
s = t
end
else
s = t
end
if type(t) ~= "table" then
storage = string.format("[[%s]]", storage)
if s then
storage = string.format("%s * %s", storage, s)
end
t = false
end
return storage, t
end -- Export.fetch()
return Export