Modul:Sandkasse/jeblad/Modul:External links
Utseende
Moduldokumentasjon
[opprett]
require('Module:No globals')
local contLang = mw.language.getContentLanguage()
local conf = require 'Module:Sandkasse/jeblad/Modul:Official links/conf'(contLang:getCode())
---
-- The ''pid'' is used to identify the proper messages for the agregate.
-- @param pid string identifier for the property to aggregate
-- @param list table of entries to format as a propert aggregate
-- @return string of aggregated content
local function buildList( pid, list )
assert(pid, 'missing argument string "pid"')
assert(list, 'missing argument table "list"')
if #list == 0 then
return ''
elseif #list == 1 then
return list[1]
end
local last = table.remove(list)
local str = table.concat(list, mw.message.newRawMessage(conf:g('initial-' .. pid .. '-combiner', 'initial-default-combiner')):plain())
return mw.message.newRawMessage(conf:g('final-' .. pid .. '-combiner', 'final-default-combiner'), str, last):plain()
end
---
-- @param content string containing whatever to be rendered
-- @param marker string to insert as a part of the overall marker
-- @return string of wrapped up content
-- @todo messy solution, refactor
local function wrap( content, marker )
if content == '' or content == nil then
return nil
end
return ''
--[[
return '<span class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..marker..'"><span class="mw-collapsible-content">' .. content .. '</span>'
.. ' <span class="mw-customtoggle-'..marker..'" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}" /></span>'
--]]
end
---
-- A simple memoize solution.
-- @field id label a string → string relation
-- @todo Use a proper memoize pattern, like [http://www.lua.org/pil/17.1.html Programming in Lua: 17.1 – Memoize Functions]
-- or [https://github.com/kikito/memoize.lua/blob/master/memoize.lua kikito/memoize.lua].
-- Note also [http://lua-users.org/wiki/CurriedMemoization Curried Memoization].
local labels = {}
---
-- This is a general function, but is used for lookup of property labels.
-- @param id string identifier for the property or item to query
-- @return string label from the item
local function getEntityLabel( id )
local entity = mw.wikibase.getEntity( id )
if entity then
return entity:getLabel() -- @fixme only uses a single language, should use fallback chain
end
end
---
-- This is a general function, but is used for lookup of property labels.
-- @param id string identifier for the property or item to query
-- @return string label from the memoized pattern or the entity itself
-- @todo This is a simple solution to the memoize problem, use a proper pattern.
local function findEntityLabel( id )
if not labels[id] then
local label = getEntityLabel( id )
if label then
labels[id] = label -- labels is an outer structure
end
end
return labels[id]
end
---
-- These are the formatters for the main snaks in the statements. Usually they
-- will be the at the middle level, with formatting at the template level above
-- them and qualifiers below them.
-- @field datatype function
local mainFormatter = {}
--- Main formatter for a datavalue
-- @param pid string identifier for the property (not in use)
-- @param datavalue table representing the structure
-- @return string representing the formatted main value
mainFormatter['string'] = function( pid, datavalue )
-- Given the ''pid'' the datavalue should be of type "string".
return datavalue['type'] == 'string' and datavalue.value or nil
end
---
-- These are the weights of the main statements. Weights are used for figuring
-- out which statements are more important than other.
-- @field datatype function
local mainWeight = {}
--- Main weight for a string datavalue
-- @param pid string identifier for the property (not in use)
-- @param datavalue table representing the structure
-- @return number representing the weight of the main value
mainWeight['string'] = function( pid, datavalue )
-- Given the ''pid'' the datavalue should be of type "string".
return datavalue['type'] == 'string' and conf:fragmentWeight(datavalue.value) or 0
end
---
-- These are the formatters for the auxilary snaks in the statements, that is
-- whats usually called qualifiers. Usually they will be the at the lowest level,
-- with formatting at the template level and main snaks above them.
-- @field datatype function
local qualFormatter = {}
--- Qualifier (auxilary) formatter for a datavalue
-- @param pid string identifier for the property (not in use)
-- @param datavalue table representing the structure
-- @return string representing the formatted auxilary value
qualFormatter['wikibase-entityid'] = function( pid, datavalue )
-- Given the ''pid'' the datavalue should be of type "wikibase-entityid"
-- and datavalue['entity-type'] == 'item'.
return (datavalue['type'] == 'wikibase-entityid' and datavalue['entity-type'] == 'item')
and findEntityLabel( 'Q'..datavalue.value["numeric-id"] ) or nil
end
local qualPrefer = {}
qualPrefer['wikibase-entityid'] = function( pid, datavalue )
if datavalue['type'] ~= 'wikibase-entityid' then
return nil
end
if datavalue.value['entity-type'] ~= 'item' then
return nil
end
return conf:p( 'Q'..datavalue.value["numeric-id"] ) or false
end
local qualWeight = {}
qualWeight['wikibase-entityid'] = function( pid, datavalue )
if datavalue['type'] ~= 'wikibase-entityid' then
return 0
end
return conf:languageWeight(datavalue.value)
end
local main = {}
main.P856 = {
types = {
snaktype = 'value',
datatype = 'url',
},
formatter = mainFormatter['string'],
weight = mainWeight['string']
}
main.P1019 = {
types = {
snaktype = 'value',
datatype = 'url',
},
formatter = mainFormatter['string'],
weight = mainWeight['string']
}
main.P1581 = {
types = {
snaktype = 'value',
datatype = 'url',
},
formatter = mainFormatter['string'],
weight = mainWeight['string']
}
local qual = {}
qual.P407 = {
types = {
snaktype = 'value',
datatype = 'wikibase-item',
},
formatter = qualFormatter['wikibase-entityid'],
preferred = qualPrefer['wikibase-entityid'],
weight = qualWeight['wikibase-entityid']
}
local qorder = {'P407'}
local p = {}
---
-- @param pid string identifying which property-set to get
-- @param qid {string|nil} identifying which item to get, or to use the default one
-- @return table of head entries, those that somehow are deemed more important
-- @return table of tail entries, those that somehow are deemed less important
function p.findMainLinks(pid, qid)
local head = {}
local tail = {}
local maxWeight = -100
local entity = mw.wikibase.getEntityObject( qid )
if not entity or not entity.claims then
return head, tail
end
local statements = pid and entity.claims[pid]
if not statements then
return head, tail
end
for _, claim in ipairs( statements ) do
-- to avoid deep tests
if not claim then
claim = {}
end
local valid = true
if claim['type'] ~= 'statement' then
valid = valid and false
end
if claim['rank'] == 'deprecated' then
valid = valid and false
end
local mainsnak = claim.mainsnak or {}
if not mainsnak or not main[pid] then
valid = valid and false
end
if ((main[pid] and mainsnak.snaktype ~= main[pid].types.snaktype)
or (main[pid] and mainsnak.datatype ~= main[pid].types.datatype))
then
valid = valid and false
end
local weight = (claim['rank'] == 'preferred' and 100)
or (claim['rank'] == 'normal' and 50)
or 0
if valid then
-- local preferred -- = claim['rank'] == 'preferred'
weight = weight + main[pid].weight(pid, mainsnak.datavalue)
local mainStr = main[pid].formatter(pid, mainsnak.datavalue)
local optionals = {}
local qualifiers = claim.qualifiers or {}
for _, qualid in ipairs( qorder ) do
local text = nil
if qualifiers[qualid] then
local items = {}
for _, qualsnak in ipairs( qualifiers[qualid] ) do
if qualsnak and qual[qualid] then
if not (qualsnak.snaktype ~= qual[qualid].types.snaktype
or qualsnak.datatype ~= qual[qualid].types.datatype)
then
items[1+#items] = qual[qualid].formatter(qualsnak.property, qualsnak.datavalue)
weight = weight + qual[qualid].weight(qualsnak.property, qualsnak.datavalue)
-- preferred = preferred or qual[qualid].preferred(qualsnak.property, qualsnak.datavalue)
end
end
end
text = buildList(qualid, items)
if qualid == 'P407' and (not text or text == '') then
-- this should never happen, unless it will be possible to add empty qualifiers
text = mw.message.newRawMessage(conf:g('qualifier-'.. qualid..'-empty', 'qualifier-default-empty')):plain()
end
else
if qualid == 'P407' then
text = conf:guess(mainStr)
end
if not text then
text = mw.message.newRawMessage(
conf:g('qualifier-'.. pid..'-missing-'..qualid,
'qualifier-default-missing-'..qualid,
'qualifier-default-missing')):plain()
end
end
if text then
text = mw.message.newRawMessage(conf:g('ext-link-' .. claim['rank']), mainStr, text):plain()
if weight > maxWeight then
maxWeight = weight
for _,v in ipairs(head) do
table.insert(tail, v)
end
head = { text }
elseif weight == maxWeight then
table.insert(head, text)
else
table.insert(tail, text)
end
end
end
-- if #optionals > 0 then
-- mainStr = buildList(pid, optionals)
-- end
if #head == 0 then
head = tail
tail = {}
end
end
end
return head, tail
end
function p.links( frame )
local items = {}
local counts = {}
for _,v in ipairs(frame.args) do
local _, _, ch, num= v:find("^%s*([pP])(%d+)%s*$")
if ch then
local pid = ch:upper()..num
local label = findEntityLabel( pid )
if not counts[pid] then
counts[pid] = 0
end
if label then
local head, tail = p.findMainLinks(pid)
if (head and #head > 0) or (tail and #tail > 0) then
counts[pid] = counts[pid] + 1
end
if head and #head > 0 then
items[1+#items] = { contLang:ucfirst( label ), buildList('', head), wrap(buildList('', tail), pid) }
elseif tail and #tail > 0 then
items[1+#items] = { contLang:ucfirst( label ), buildList('', tail) }
end
end
end
end
for i,v in ipairs(items) do
items[i] = mw.message.newRawMessage(
((#v >=3 and #v[3])
and conf:g(i==1 and 'first-list-item-with-additional' or 'rest-list-item-with-additional')
or conf:g(i==1 and 'first-list-item' or 'rest-list-item')),
unpack(v))
:plain()
end
local cats = {}
for k,v in pairs(counts) do
cats[1+#cats] = '[[Category:' .. mw.message.newRawMessage(conf:g(counts[k]==0 and 'cat-exclusion' or 'cat-inclusion'), findEntityLabel( k )):plain() .. ']]'
end
if #items > 0 then
return table.concat(items, "\n") .. table.concat(cats, "")
end
return "''" .. mw.message.newRawMessage(conf:g('no-links-available')):plain() .. "''" .. table.concat(cats, "")
end
return p