Module:Format TemplateData/global

local Export = { suite = "TemplateDataGlobal",

serial = "2020-08-01",

item = 51435481,

subpages = "TemplateData",

suffix = "tab" }

--[=[

Retrieve TemplateData from Commons:Data (or other global source)

require()

Inspired by 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

-- 2020-08-01

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