Moduł:Wikidane/Tree
Zobacz podstrony tego modułu.
|
--------------------------------------------------------------------------------
-- NODE of data
--------------------------------------------------------------------------------
local nodeMetatable = {}
local nodeMethodtable = {}
nodeMetatable.__index = nodeMethodtable
nodeMethodtable.getLevel = function(me)
local result = 0
local parent = me.parent
while parent do
result = result + 1
parent = parent.parent
end
return result
end
nodeMethodtable.normalize = function(me)
local nodes = me.nodes
local i = 1
if #nodes <= 0 then
-- there are no children
return 0
end
local node = nodes[i]
i = i + 1
local deep = node:normalize()
while i <= #nodes do
local node2 = nodes[i]
i = i + 1
local deep2 = node2:normalize()
if deep < deep2 then
-- take longer path
node = node2
deep = deep2
end
end
-- use single parent
me.nodes = { node }
return 1 + deep
end
nodeMethodtable.getPath = function(me, accept, useroot)
local result = {}
if me.nodes[1] then
local node = useroot and me or me.nodes[1]
while node do
local name = node.name
if not accept(name) then
-- terminate sequence
break
end
table.insert(result, name)
node = node.nodes[1]
end
-- reverse result
local i = 1
local j = #result
while i < j do
local l = result[i]
local r = result[j]
result[i] = r
result[j] = l
i = i + 1
j = j - 1
end
end
return result
end
nodeMethodtable.add = function(me, node)
node.parent = me
table.insert(me.nodes, node)
end
nodeMethodtable.remove = function(me, node)
local nodes = {}
for i = 1, #me.nodes do
local item = me.nodes[i]
if item.name ~= node.name then
table.insert(nodes, item)
else
node.parent = nil
end
end
me.nodes = nodes
end
local function newNode(name)
local me = {}
me.name = name
me.nodes = {}
me.parent = nil
setmetatable(me, nodeMetatable)
return me
end
--------------------------------------------------------------------------------
-- TOOLS
--------------------------------------------------------------------------------
local getRequiredEntityId = function(frame, Q)
if Q then
return Q
end
local id = frame.args[1]
if id and (#id > 0) then
return id
end
local entity = mw.wikibase.getEntity()
if entity then
return entity.id
end
end
local wikidataLinkItem = function(q)
return "<span class=\"plainlinks wdlink\" title=\"edytuj dane z infoboxu w Wikidanych\">[https://www.wikidata.org/wiki/"..q.." •]</span>"
end
local defaultFormatItem = function(q)
local label = mw.wikibase.label(q)
local site = mw.wikibase.sitelink(q)
if site and label then
return site == label and ("[["..site.."]]") or ("[["..site.."|"..label.."]]")
elseif site then
return "[["..site.."]]"
elseif label then
return label
else
return q
end
end
local getBestStatements = function(entity, property)
if entity and entity.claims and entity.claims[property] then
local preferred = {}
local normal = {}
for _, v in ipairs(entity.claims[property]) do
if v.rank == "normal" then
table.insert(normal, v)
elseif v.rank == "preferred" then
table.insert(preferred, v)
end
end
if #preferred > 0 then
return preferred
elseif #normal > 0 then
return normal
end
end
end
local function isValueType(entity, type)
mw.logObject({ entity.id, type }, "isValueType")
local passed = {}
local statements = {}
local function loadBestStatements(e)
local best = getBestStatements(e, "P279") or {}
for _, v in ipairs(best) do
table.insert(statements, v)
end
end
passed[entity.id] = true
loadBestStatements(entity)
local i = 1
while i <= #statements do
local v = statements[i]
if v.mainsnak.datavalue then
local id = "Q"..v.mainsnak.datavalue.value["numeric-id"]
mw.logObject(id, "id")
if id == type then
mw.logObject(true, "return isValueType")
return true
end
if not passed[id] then
passed[id] = true
loadBestStatements(mw.wikibase.getEntity(id))
end
end
i = i + 1
end
mw.logObject(false, "return isValueType")
return false
end
--------------------------------------------------------------------------------
-- LIST FORMATTER
--------------------------------------------------------------------------------
local listFormatterMetatable = {}
local listFormatterMethodtable = {}
listFormatterMetatable.__index = listFormatterMethodtable
listFormatterMetatable.__tostring = function(me)
return me.builder and tostring(me.builder) or nil
end
listFormatterMethodtable.append = function(me, item)
me.builder = me.builder or mw.html.create("ul")
me.builder:tag("li"):wikitext(me.format(item))
return me
end
local listFormatter = function(formatItem)
me = {
format = formatItem or defaultFormatItem,
}
setmetatable(me, listFormatterMetatable)
return me
end
--------------------------------------------------------------------------------
-- TAXONOMY FORMATTER
--------------------------------------------------------------------------------
local taxonomyFormatterMetatable = {}
local taxonomyFormatterMethodtable = {}
taxonomyFormatterMetatable.__index = taxonomyFormatterMethodtable
taxonomyFormatterMetatable.__tostring = function(me)
return me.builder and table.concat(me.builder, "") or nil
end
taxonomyFormatterMethodtable.append = function(me, item)
if #me.builder == 0 then
table.insert(me.builder, "|-\n")
end
local formatDescription = function(site, label, latin)
if site and label and latin then
return "[["..site.."|"..label.."]] (''"..latin.."'')"
elseif site and latin then
return "[["..site.."|''"..latin.."'']]"
elseif site and label then
return "[["..site.."|"..label.."]]"
elseif site then
return "[["..site.."]]"
elseif label and latin then
return label.." (''"..latin.."'')"
elseif latin then
return "''"..latin.."''"
elseif label then
return label
end
end
local entity = mw.wikibase.getEntity(item)
local label = entity:getLabel()
local site = entity:getSitelink()
local ranks = getBestStatements(entity, "P105")
local names = getBestStatements(entity, "P225")
local taxonomicName = false
table.insert(me.builder, "!")
if ranks then
for _, rank in ipairs(ranks) do
if rank.mainsnak.snaktype == "value" then
if (rank.mainsnak.datatype == "wikibase-item")and (rank.mainsnak.datavalue.type == "wikibase-entityid") and (rank.mainsnak.datavalue.value["entity-type"] == "item") then
local rankid = "Q"..rank.mainsnak.datavalue.value["numeric-id"]
table.insert(me.builder, wikidataLinkItem(rankid))
local rankEntity = mw.wikibase.getEntity(rankid)
local rankSite = rankEntity:getSitelink()
local rankLabel = rankEntity:getLabel('pl')
local rankLatin = rankEntity:getLabel('la')
local ranktext = formatDescription(rankSite, rankLabel, rankLatin) or ""
table.insert(me.builder, ranktext)
break
end
end
end
end
table.insert(me.builder, "\n|")
if names then
for _, name in ipairs(names) do
if name.mainsnak.snaktype == "value" then
if (name.mainsnak.datatype == "string") and (name.mainsnak.datavalue.type == "string") then
taxonomicName = name.mainsnak.datavalue.value
break
end
end
end
end
if label == taxonomicName then
label = false
end
table.insert(me.builder, wikidataLinkItem(entity.id))
table.insert(me.builder, formatDescription(site, label, taxonomicName) or item)
table.insert(me.builder, "\n|-\n")
return me
end
local taxonomyFormatter = function()
me = {
builder = {}
}
setmetatable(me, taxonomyFormatterMetatable)
return me
end
--------------------------------------------------------------------------------
-- ADMINISTRATIVE DIVISION FORMATTER
--------------------------------------------------------------------------------
local administrativeDivisionFormatterMetatable = {}
local administrativeDivisionFormatterMethodtable = {}
administrativeDivisionFormatterMetatable.__index = administrativeDivisionFormatterMethodtable
administrativeDivisionFormatterMetatable.__tostring = function(me)
if me.usetop then
local suffix = #me.builder == 0 and "\n|-\n" or "\n"
table.insert(me.builder, 1, "|-\n!Państwo\n|"..defaultFormatItem(me.top[1])..suffix)
end
if me.bottom then
me:append(me.bottom)
end
return table.concat(me.builder, "")
end
local function acceptAdministrativeDivisionEntity(entity)
return isValueType(entity, "Q56061") -- administrative territorial entity
or isValueType(entity, "Q15617994") -- designation for an administrative territorial entity
or (entity.id == "Q1078001") -- territorial dispute
end
administrativeDivisionFormatterMethodtable.append = function(me, item)
if #me.builder == 0 then
table.insert(me.builder, "|-\n")
end
if item == me.bottom then
me.bottom = false;
end
if item == "Q1078001" then
table.insert(me.builder, "!")
table.insert(me.builder, wikidataLinkItem(item))
table.insert(me.builder, defaultFormatItem(item))
for i, v in ipairs(me.top) do
table.insert(me.builder, i > 1 and ", " or "\n|")
table.insert(me.builder, wikidataLinkItem(v))
table.insert(me.builder, defaultFormatItem(v))
end
table.insert(me.builder, "\n|-\n")
return me
end
if (#me.top == 1) and (item == me.top[1]) then
table.insert(me.builder, "!Państwo")
me.usetop = false
else
local entity = mw.wikibase.getEntity(item)
local itis = getBestStatements(entity, "P31")
table.insert(me.builder, "!")
if itis then
local more = false
for _, v in ipairs(itis) do
if v.mainsnak.snaktype == "value" then
if (v.mainsnak.datatype == "wikibase-item")and (v.mainsnak.datavalue.type == "wikibase-entityid") and (v.mainsnak.datavalue.value["entity-type"] == "item") then
local id = "Q"..v.mainsnak.datavalue.value["numeric-id"]
local e = mw.wikibase.getEntity(id)
if acceptAdministrativeDivisionEntity(e) then
if more then
table.insert(me.builder, ", ")
end
table.insert(me.builder, wikidataLinkItem(id))
table.insert(me.builder, defaultFormatItem(id))
more = true
end
end
end
end
end
end
table.insert(me.builder, "\n|")
table.insert(me.builder, wikidataLinkItem(item))
table.insert(me.builder, defaultFormatItem(item))
table.insert(me.builder, "\n|-\n")
return me
end
local administrativeDivisionFormatter = function(top, bottom)
me = {
usetop = #top == 1,
top = top,
bottom = bottom,
builder = {}
}
setmetatable(me, administrativeDivisionFormatterMetatable)
return me
end
--------------------------------------------------------------------------------
-- GENERATOR
--------------------------------------------------------------------------------
local generatorMetatable = {}
local generatorMethodtable = {}
generatorMetatable.__index = generatorMethodtable
generatorMethodtable.run = function(me, wgWikibaseItemId, builder)
me.types = {}
me.subclasses = {}
mw.log("LOADING DATA "..wgWikibaseItemId)
me:loadData(wgWikibaseItemId)
mw.log("BUILDING TREE "..wgWikibaseItemId)
local dictionary = {}
local root = newNode(wgWikibaseItemId)
dictionary[wgWikibaseItemId] = root
me:buildingTree(dictionary, root)
mw.log("LOOKING FOR PATH "..wgWikibaseItemId)
local deep = root:normalize()
local accept = function(item)
return not me.T.terminators[item];
end
local path = root:getPath(accept, me.T.showLeaf);
if #path then
mw.log("PREPARING LINKS AND LABELS "..wgWikibaseItemId)
for i = 1, #path do
if (i ~= 1) or not me.T.hideTerminators or not me.T.terminators[path[i]] then
builder:append(path[i])
end
end
local result = tostring(builder)
--mw.logObject(result, "showTree")
return result
end
end
generatorMethodtable.loadData = function(me, q)
if me.T[q] then
mw.log("loadData: cut searching "..q)
-- cut searching
return
end
local data = mw.wikibase.getEntity(q)
if not me.types[q] then
local ids = me:parseItemClaims(data.claims.P31, me.T.accept)
if ids then
me.types[q] = ids
for i = 1, #me.T.parentSources do
local p = me.T.parentSources[i]
me:LoadParentItems(q, mw.wikibase.getEntity(q))
end
end
end
end
generatorMethodtable.buildingTree = function(me, nodes, node)
local subclasses = me.subclasses[node.name]
if subclasses then
for i = 1, #subclasses do
local subclass = subclasses[i]
-- find parent
local n = node
local parent = false
while n do
if n.name == subclass then
parent = n
break
end
n = n.parent
end
if parent then
mw.log("there is cycle in the graph")
else
local available = nodes[subclass]
if available then
local alevel = available:getLevel()
local nlevel = node:getLevel()
if alevel <= nlevel then
-- the available node level path is shorter
-- move it to this location
available.parent:remove(available)
node:add(available)
else
-- the available node level path is same size or longer
-- as this is further class, it is ignored
end
else
local newnode = newNode(subclass)
nodes[subclass] = newnode
node:add(newnode)
me:buildingTree(nodes, newnode)
end
end
end
end
end
generatorMethodtable.LoadParentItems = function(me, q, data)
local accept = function() return true end
for i = 1, #me.T.parentSources do
local p = me.T.parentSources[i]
local ids = me:parseItemClaims(data.claims[p], accept)
if ids then
me.subclasses[q] = ids
for j = 1, #ids do
me:loadData(ids[j])
end
end
end
end
generatorMethodtable.parseItemClaims = function(me, items, check)
if not items or (#items <= 0) then
mw.log("parseItemClaims: no items")
return {}
end
mw.logObject(items, "parseItemClaims")
if #items > 1 then
local recent = false
local date = false
for _, v in ipairs(items) do
if v.qualifiers and v.qualifiers.P580 then
for _, d in ipairs(v.qualifiers.P580) do
if d.snaktype == "value" then
if not date or (date < d.datavalue.value.time) then
date = d.datavalue.value.time
recent = v
end
end
end
end
end
if date and recent then
mw.log(recemt, "recent")
items = { recent, }
end
end
local ids = {}
local j = 1
for i = 1, #items do
if items[i].mainsnak.datavalue then
local id = "Q"..items[i].mainsnak.datavalue.value["numeric-id"]
if check(id) then
ids[j] = id
j = j + 1
end
end
end
return #ids > 0 and ids or nil
end
local generator = function(T)
local me = {}
me.T = T;
setmetatable(me, generatorMetatable)
return me
end
return {
taxonomy = function(frame, Q)
local entityId = getRequiredEntityId(frame, Q)
if not entityId then
return
end
local acceptableTypes = {
Q16521 = true,
Q310890 = true,
Q713623 = true,
Q23038290 = true,
}
local params = {
parentSources = {
"P171",
},
accept = function(id)
return acceptableTypes[id]
end,
terminators = {
Q2382443 = true,
},
showLeaf = true,
}
return generator(params):run(entityId, taxonomyFormatter())
end,
administrativeDivision = function(frame, Q)
local entityId = getRequiredEntityId(frame, Q)
if not entityId then
return
end
local entity = mw.wikibase.getEntity(entityId)
local tops = getBestStatements(entity, "P17");
if tops then
local params = {
parentSources = {
"P131",
},
accept = function(id)
local entity = mw.wikibase.getEntity(id)
return entity and acceptAdministrativeDivisionEntity(entity)
end,
terminators = {},
showLeaf = true,
}
for _, v in ipairs(tops) do
if (v.mainsnak.snaktype == "value") and (v.mainsnak.datatype == "wikibase-item") and (v.mainsnak.datavalue.type == "wikibase-entityid") and (v.mainsnak.datavalue.value["entity-type"] == "item") then
local top = "Q"..v.mainsnak.datavalue.value["numeric-id"]
table.insert(params.terminators, top)
mw.logObject(params, top)
end
end
return generator(params):run(entityId, administrativeDivisionFormatter(params.terminators, entityId))
end
end,
classTree = function(frame, Q)
local entityId = getRequiredEntityId(frame, Q)
if not entityId then
return
end
local params = {
parentSources = mw.text.split( frame.args.parentSources, '%s' ),
terminators = {},
showLeaf = frame.args.showLeaf and true or false
}
local acceptableTypes = frame.args.acceptableTypes
if acceptableTypes and (#acceptableTypes > 0) and (acceptableTypes ~= "-") then
local accept = {}
for _, v in ipairs(mw.text.split( frame.args.acceptableTypes, '%s' )) do
accept[v] = true
end
params.accept = function(id) mw.logObject(accept[id], "classTree.accept["..id.."]") return accept[id] end
else
params.accept = function(id) return true end
end
local terminators = frame.args.terminators
if terminators and (#terminators > 0) then
for _, v in ipairs(mw.text.split( frame.args.terminators, '%s' )) do
params.terminators[v] = true
end
end
return generator(params):run(entityId, listFormatter())
end,
}