Module:FamilyTree
From KB Lexicon
Documentation for this module may be created at Module:FamilyTree/doc
local p = {}
local cargo = mw.ext.cargo
local function esc(value)
if not value then
return ''
end
value = tostring(value)
value = value:gsub('\\', '\\\\')
value = value:gsub('"', '\\"')
return value
end
local function cargoQuery(tables, fields, args)
args = args or {}
local ok, result = pcall(function()
return cargo.query(tables, fields, args)
end)
if ok and result then
return result
end
return {}
end
local function trim(s)
if not s then
return nil
end
s = tostring(s)
s = mw.text.trim(s)
if s == '' then
return nil
end
return s
end
local function addToSet(set, value)
value = trim(value)
if value then
set[value] = true
end
end
local function getDisplayName(pageName)
pageName = trim(pageName)
if not pageName then
return nil
end
local rows = cargoQuery(
'Characters',
'Page,DisplayName',
{
where = 'Page="' .. esc(pageName) .. '"',
limit = 1
}
)
if rows[1] and trim(rows[1].DisplayName) then
return trim(rows[1].DisplayName)
end
return pageName
end
local function getNeighbors(person)
local neighbors = {}
person = trim(person)
if not person then
return neighbors
end
local unionRows = cargoQuery(
'Unions',
'UnionID,Partner1,Partner2',
{
where = 'Partner1="' .. esc(person) .. '" OR Partner2="' .. esc(person) .. '"',
limit = 500
}
)
for _, row in ipairs(unionRows) do
addToSet(neighbors, row.Partner1)
addToSet(neighbors, row.Partner2)
end
local parentChildRows = cargoQuery(
'ParentChild',
'Child,Parent1,Parent2,UnionID',
{
where = 'Child="' .. esc(person) .. '" OR Parent1="' .. esc(person) .. '" OR Parent2="' .. esc(person) .. '"',
limit = 1000
}
)
for _, row in ipairs(parentChildRows) do
addToSet(neighbors, row.Child)
addToSet(neighbors, row.Parent1)
addToSet(neighbors, row.Parent2)
end
neighbors[person] = nil
return neighbors
end
local function collectConnectedComponent(root)
local visited = {}
local queue = {}
local head = 1
root = trim(root)
if not root then
return visited
end
visited[root] = true
table.insert(queue, root)
while head <= #queue do
local current = queue[head]
head = head + 1
local neighbors = getNeighbors(current)
for neighbor, _ in pairs(neighbors) do
if not visited[neighbor] then
visited[neighbor] = true
table.insert(queue, neighbor)
end
end
end
return visited
end
local function sortedKeys(set)
local arr = {}
for key, _ in pairs(set) do
table.insert(arr, key)
end
table.sort(arr, function(a, b)
return a:lower() < b:lower()
end)
return arr
end
function p.connected(frame)
local args = frame.args
local parentArgs = frame:getParent() and frame:getParent().args or {}
local root = trim(args.root or parentArgs.root or args[1] or parentArgs[1])
if not root then
return 'Error: no root provided. Use root=Character Name'
end
local component = collectConnectedComponent(root)
local people = sortedKeys(component)
if #people == 0 then
return 'No connected people found for ' .. root
end
local lines = {}
table.insert(lines, "'''Connected component for " .. getDisplayName(root) .. "'''")
table.insert(lines, '* Total people found: ' .. tostring(#people))
for _, pageName in ipairs(people) do
local displayName = getDisplayName(pageName)
table.insert(lines, '* [[' .. pageName .. '|' .. displayName .. ']]')
end
return table.concat(lines, '\n')
end
return p