Module:FamilyTree: Difference between revisions
From KB Lexicon
No edit summary |
No edit summary |
||
| Line 36: | Line 36: | ||
end | end | ||
return s | return s | ||
end | |||
local function formatYear(dateValue) | |||
dateValue = trim(dateValue) | |||
if not dateValue then | |||
return nil | |||
end | |||
return tostring(dateValue):match('^(%d%d%d%d)') | |||
end | end | ||
| Line 43: | Line 51: | ||
seen[value] = true | seen[value] = true | ||
table.insert(list, value) | table.insert(list, value) | ||
end | end | ||
end | end | ||
| Line 58: | Line 59: | ||
end) | end) | ||
return list | return list | ||
end | end | ||
| Line 108: | Line 79: | ||
end | end | ||
local function | local function getDisplayName(pageName) | ||
local | local c = getCharacter(pageName) | ||
return | if c and trim(c.DisplayName) then | ||
return trim(c.DisplayName) | |||
end | |||
return pageName | |||
end | end | ||
local function | local function makeLinkedName(pageName) | ||
return '[[' .. pageName .. '|' .. getDisplayName(pageName) .. ']]' | |||
end | end | ||
| Line 233: | Line 203: | ||
end | end | ||
local function | local function getSiblingRows(person) | ||
person = trim(person) | person = trim(person) | ||
if not person then | if not person then | ||
| Line 241: | Line 211: | ||
local targetRows = cargoQuery( | local targetRows = cargoQuery( | ||
'ParentChild', | 'ParentChild', | ||
'Child,Parent1,Parent2,UnionID', | 'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder', | ||
{ | { | ||
where = 'Child="' .. esc(person) .. '"', | where = 'Child="' .. esc(person) .. '"', | ||
| Line 249: | Line 219: | ||
if not targetRows[1] then | if not targetRows[1] then | ||
return {} | return { person } | ||
end | end | ||
| Line 257: | Line 227: | ||
local whereParts = {} | local whereParts = {} | ||
if targetUnion then | if targetUnion then | ||
table.insert(whereParts, 'UnionID="' .. esc(targetUnion) .. '"') | table.insert(whereParts, 'UnionID="' .. esc(targetUnion) .. '"') | ||
end | end | ||
if targetP1 and targetP2 then | if targetP1 and targetP2 then | ||
table.insert(whereParts, '(Parent1="' .. esc(targetP1) .. '" AND Parent2="' .. esc(targetP2) .. '")') | table.insert(whereParts, '(Parent1="' .. esc(targetP1) .. '" AND Parent2="' .. esc(targetP2) .. '")') | ||
| Line 267: | Line 235: | ||
if #whereParts == 0 then | if #whereParts == 0 then | ||
return {} | return { person } | ||
end | end | ||
| Line 288: | Line 256: | ||
end) | end) | ||
local | local people = {} | ||
local seen = {} | local seen = {} | ||
for _, row in ipairs(rows) do | for _, row in ipairs(rows) do | ||
addUnique(people, seen, row.Child) | |||
end | end | ||
if #people == 0 then | |||
table.insert(people, person) | |||
end | end | ||
return people | |||
return | |||
end | end | ||
| Line 409: | Line 272: | ||
local groups = {} | local groups = {} | ||
local used = {} | local used = {} | ||
local | local working = {} | ||
for _, person in ipairs(people or {}) do | for _, person in ipairs(people or {}) do | ||
table.insert( | table.insert(working, person) | ||
end | end | ||
sorted( | sorted(working) | ||
for _, person in ipairs( | for _, person in ipairs(working) do | ||
if not used[person] then | if not used[person] then | ||
local partners = getPartners(person) | local partners = getPartners(person) | ||
| Line 422: | Line 285: | ||
for _, partner in ipairs(partners) do | for _, partner in ipairs(partners) do | ||
for _, candidate in ipairs( | for _, candidate in ipairs(working) do | ||
if candidate == partner and not used[candidate] then | if candidate == partner and not used[candidate] then | ||
matchedPartner = partner | matchedPartner = partner | ||
| Line 439: | Line 302: | ||
local union = getUnionBetween(person, matchedPartner) | local union = getUnionBetween(person, matchedPartner) | ||
table.insert(groups, { | table.insert(groups, { | ||
type = 'couple', | |||
left = person, | |||
right = matchedPartner, | |||
marriageYear = union and formatYear(union.MarriageDate) or nil | marriageYear = union and formatYear(union.MarriageDate) or nil | ||
}) | }) | ||
| Line 445: | Line 310: | ||
used[person] = true | used[person] = true | ||
table.insert(groups, { | table.insert(groups, { | ||
type = 'single', | |||
person = person | |||
}) | }) | ||
end | end | ||
| Line 455: | Line 320: | ||
end | end | ||
local function | local function buildFamilyUnitsForGeneration(people) | ||
local | local units = {} | ||
for _, person in ipairs(people or {}) do | |||
local partners = getPartners(person) | |||
local children = getChildren(person) | |||
if partners[1] then | |||
local union = getUnionBetween(person, partners[1]) | |||
table.insert(units, { | |||
person = person, | |||
partner = partners[1], | |||
marriageYear = union and formatYear(union.MarriageDate) or nil, | |||
children = children | |||
}) | |||
table.insert( | else | ||
table.insert(units, { | |||
marriageYear = | person = person, | ||
partner = nil, | |||
marriageYear = nil, | |||
children = children | |||
}) | }) | ||
end | end | ||
end | end | ||
return | return units | ||
end | |||
local function makeCard(pageName) | |||
pageName = trim(pageName) | |||
if not pageName then | |||
return '' | |||
end | |||
local c = getCharacter(pageName) | |||
local displayName = getDisplayName(pageName) | |||
local birthYear = c and formatYear(c.BirthDate) or nil | |||
local deathYear = c and formatYear(c.DeathDate) or nil | |||
local years = '' | |||
if birthYear or deathYear then | |||
years = '<div class="ft-years">' .. (birthYear or '?') .. '–' .. (deathYear or '') .. '</div>' | |||
end | |||
return '<div class="ft-card">[[' .. pageName .. '|' .. displayName .. ']]' .. years .. '</div>' | |||
end | end | ||
local function | local function makeCoupleGroupRow(groups) | ||
if not groups or #groups == 0 then | if not groups or #groups == 0 then | ||
return '' | return '' | ||
| Line 485: | Line 376: | ||
for _, group in ipairs(groups) do | for _, group in ipairs(groups) do | ||
if group.type == 'single' then | |||
table.insert(html, '<div class="ft-unit ft-unit-single">') | |||
table.insert(html, makeCard(group.person)) | |||
table.insert(html, '<div class="ft-single">') | |||
table.insert(html, makeCard( | |||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
else | else | ||
table.insert(html, '<div class="ft-couple">') | table.insert(html, '<div class="ft-unit ft-unit-couple">') | ||
table.insert(html, makeCard( | table.insert(html, makeCard(group.left)) | ||
table.insert(html, '<div class="ft-marriage">') | table.insert(html, '<div class="ft-marriage">') | ||
table.insert(html, | if group.marriageYear then | ||
table.insert(html, '<div class="ft-marriage-year">' .. group.marriageYear .. '</div>') | |||
end | |||
table.insert(html, '<div class="ft-marriage-line"></div>') | table.insert(html, '<div class="ft-marriage-line"></div>') | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
table.insert(html, makeCard( | table.insert(html, makeCard(group.right)) | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
end | end | ||
| Line 508: | Line 398: | ||
end | end | ||
local function | local function makeFamilyUnitsRow(units) | ||
return '<div class="ft- | if not units or #units == 0 then | ||
return '' | |||
end | |||
local html = {} | |||
table.insert(html, '<div class="ft-family-row">') | |||
for _, unit in ipairs(units) do | |||
table.insert(html, '<div class="ft-family-unit">') | |||
table.insert(html, '<div class="ft-family-main">') | |||
if unit.partner then | |||
table.insert(html, '<div class="ft-unit ft-unit-couple">') | |||
table.insert(html, makeCard(unit.person)) | |||
table.insert(html, '<div class="ft-marriage">') | |||
if unit.marriageYear then | |||
table.insert(html, '<div class="ft-marriage-year">' .. unit.marriageYear .. '</div>') | |||
end | |||
table.insert(html, '<div class="ft-marriage-line"></div>') | |||
table.insert(html, '</div>') | |||
table.insert(html, makeCard(unit.partner)) | |||
table.insert(html, '</div>') | |||
else | |||
table.insert(html, '<div class="ft-unit ft-unit-single">') | |||
table.insert(html, makeCard(unit.person)) | |||
table.insert(html, '</div>') | |||
end | |||
table.insert(html, '</div>') | |||
if unit.children and #unit.children > 0 then | |||
table.insert(html, '<div class="ft-family-desc-line"></div>') | |||
table.insert(html, '<div class="ft-family-children">') | |||
for _, child in ipairs(unit.children) do | |||
table.insert(html, makeCard(child)) | |||
end | |||
table.insert(html, '</div>') | |||
end | |||
table.insert(html, '</div>') | |||
end | |||
table.insert(html, '</div>') | |||
return table.concat(html) | |||
end | end | ||
| Line 521: | Line 454: | ||
end | end | ||
local | local visited = {} | ||
local queue = {} | |||
local head = 1 | |||
visited[root] = true | |||
table.insert(queue, root) | |||
while head <= #queue do | |||
local current = queue[head] | |||
head = head + 1 | |||
local neighbors = {} | |||
local parents = getParents(current) | |||
local children = getChildren(current) | |||
local partners = getPartners(current) | |||
for _, person in ipairs(parents) do | |||
addSet(neighbors, person) | |||
end | |||
for _, person in ipairs(children) do | |||
addSet(neighbors, person) | |||
end | |||
for _, person in ipairs(partners) do | |||
addSet(neighbors, person) | |||
end | |||
for neighbor, _ in pairs(neighbors) do | |||
if neighbor and not visited[neighbor] then | |||
visited[neighbor] = true | |||
table.insert(queue, neighbor) | |||
end | |||
end | |||
end | |||
local people = {} | local people = {} | ||
for name, _ in pairs(visited) do | |||
for name, _ in pairs( | |||
table.insert(people, name) | table.insert(people, name) | ||
end | end | ||
sorted(people) | sorted(people) | ||
local lines = {} | local lines = {} | ||
table.insert(lines, "'''Connected component for " .. getDisplayName(root) .. "'''") | table.insert(lines, "'''Connected component for " .. getDisplayName(root) .. "'''") | ||
table.insert(lines, '* Total people found: ' .. tostring(#people)) | table.insert(lines, '* Total people found: ' .. tostring(#people)) | ||
for _, person in ipairs(people) do | for _, person in ipairs(people) do | ||
table.insert(lines, '* ' .. makeLinkedName(person)) | table.insert(lines, '* ' .. makeLinkedName(person)) | ||
| Line 554: | Line 515: | ||
local parents = getParents(root) | local parents = getParents(root) | ||
local siblings = | local siblings = getSiblingRows(root) | ||
local partners = getPartners(root) | local partners = getPartners(root) | ||
local children = getChildren(root) | local children = getChildren(root) | ||
local siblingList = {} | |||
for _, person in ipairs(siblings) do | |||
if person ~= root then | |||
table.insert(siblingList, person) | |||
end | |||
end | |||
local lines = {} | local lines = {} | ||
| Line 572: | Line 540: | ||
table.insert(lines, '|-') | table.insert(lines, '|-') | ||
table.insert(lines, '! Siblings') | table.insert(lines, '! Siblings') | ||
table.insert(lines, '| ' .. (# | table.insert(lines, '| ' .. (#siblingList > 0 and linkList(siblingList) or '—')) | ||
table.insert(lines, '|-') | table.insert(lines, '|-') | ||
| Line 597: | Line 565: | ||
local parents = getParents(root) | local parents = getParents(root) | ||
local | local siblingGeneration = getSiblingRows(root) | ||
local grandparents = {} | local grandparents = {} | ||
local gpSeen = {} | local gpSeen = {} | ||
for _, parentName in ipairs(parents) do | for _, parentName in ipairs(parents) do | ||
local parentParents = getParents(parentName) | local parentParents = getParents(parentName) | ||
| Line 614: | Line 579: | ||
local grandparentGroups = buildCoupleGroups(grandparents) | local grandparentGroups = buildCoupleGroups(grandparents) | ||
local parentGroups = buildCoupleGroups(parents) | local parentGroups = buildCoupleGroups(parents) | ||
local | local familyUnits = buildFamilyUnitsForGeneration(siblingGeneration) | ||
local html = {} | local html = {} | ||
| Line 622: | Line 587: | ||
if #grandparents > 0 then | if #grandparents > 0 then | ||
table.insert(html, '<div class="ft-generation">') | table.insert(html, '<div class="ft-generation">') | ||
table.insert(html, | table.insert(html, makeCoupleGroupRow(grandparentGroups)) | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
end | end | ||
| Line 632: | Line 597: | ||
if #parents > 0 then | if #parents > 0 then | ||
table.insert(html, '<div class="ft-generation">') | table.insert(html, '<div class="ft-generation">') | ||
table.insert(html, | table.insert(html, makeCoupleGroupRow(parentGroups)) | ||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
end | end | ||
if #parents > 0 then | if #parents > 0 and #familyUnits > 0 then | ||
table.insert(html, '<div class="ft-connector"></div>') | table.insert(html, '<div class="ft-connector"></div>') | ||
end | end | ||
if #familyUnits > 0 then | |||
table.insert(html, '<div class="ft-generation">') | |||
table.insert(html, makeFamilyUnitsRow(familyUnits)) | |||
if # | |||
table.insert(html, '<div class="ft | |||
table.insert(html, | |||
table.insert(html, '</div>') | table.insert(html, '</div>') | ||
end | end | ||
Revision as of 22:14, 27 March 2026
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 s == nil then
return nil
end
s = tostring(s)
s = mw.text.trim(s)
if s == '' then
return nil
end
return s
end
local function formatYear(dateValue)
dateValue = trim(dateValue)
if not dateValue then
return nil
end
return tostring(dateValue):match('^(%d%d%d%d)')
end
local function addUnique(list, seen, value)
value = trim(value)
if value and not seen[value] then
seen[value] = true
table.insert(list, value)
end
end
local function sorted(list)
table.sort(list, function(a, b)
return tostring(a):lower() < tostring(b):lower()
end)
return list
end
local function getCharacter(pageName)
pageName = trim(pageName)
if not pageName then
return nil
end
local rows = cargoQuery(
'Characters',
'Page,DisplayName,BirthDate,DeathDate,Status,Gender',
{
where = 'Page="' .. esc(pageName) .. '"',
limit = 1
}
)
return rows[1]
end
local function getDisplayName(pageName)
local c = getCharacter(pageName)
if c and trim(c.DisplayName) then
return trim(c.DisplayName)
end
return pageName
end
local function makeLinkedName(pageName)
return '[[' .. pageName .. '|' .. getDisplayName(pageName) .. ']]'
end
local function getParents(person)
person = trim(person)
if not person then
return {}, nil
end
local rows = cargoQuery(
'ParentChild',
'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
{
where = 'Child="' .. esc(person) .. '"',
limit = 20
}
)
local parents = {}
local seen = {}
for _, row in ipairs(rows) do
addUnique(parents, seen, row.Parent1)
addUnique(parents, seen, row.Parent2)
end
return sorted(parents), rows[1]
end
local function getChildren(person)
person = trim(person)
if not person then
return {}
end
local rows = cargoQuery(
'ParentChild',
'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
{
where = 'Parent1="' .. esc(person) .. '" OR Parent2="' .. esc(person) .. '"',
limit = 200
}
)
table.sort(rows, function(a, b)
local aOrder = tonumber(a.BirthOrder) or 9999
local bOrder = tonumber(b.BirthOrder) or 9999
if aOrder == bOrder then
return tostring(a.Child):lower() < tostring(b.Child):lower()
end
return aOrder < bOrder
end)
local children = {}
local seen = {}
for _, row in ipairs(rows) do
addUnique(children, seen, row.Child)
end
return children
end
local function getPartners(person)
person = trim(person)
if not person then
return {}, {}
end
local rows = cargoQuery(
'Unions',
'UnionID,Partner1,Partner2,UnionType,Status,MarriageDate,DivorceDate,EngagementDate',
{
where = 'Partner1="' .. esc(person) .. '" OR Partner2="' .. esc(person) .. '"',
limit = 50
}
)
local partners = {}
local seen = {}
for _, row in ipairs(rows) do
local p1 = trim(row.Partner1)
local p2 = trim(row.Partner2)
if p1 == person then
addUnique(partners, seen, p2)
elseif p2 == person then
addUnique(partners, seen, p1)
end
end
return sorted(partners), rows
end
local function getUnionBetween(personA, personB)
personA = trim(personA)
personB = trim(personB)
if not personA or not personB then
return nil
end
local rows = cargoQuery(
'Unions',
'UnionID,Partner1,Partner2,UnionType,Status,MarriageDate,DivorceDate,EngagementDate',
{
where = '(Partner1="' .. esc(personA) .. '" AND Partner2="' .. esc(personB) .. '") OR (Partner1="' .. esc(personB) .. '" AND Partner2="' .. esc(personA) .. '")',
limit = 1
}
)
return rows[1]
end
local function getSiblingRows(person)
person = trim(person)
if not person then
return {}
end
local targetRows = cargoQuery(
'ParentChild',
'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
{
where = 'Child="' .. esc(person) .. '"',
limit = 10
}
)
if not targetRows[1] then
return { person }
end
local targetUnion = trim(targetRows[1].UnionID)
local targetP1 = trim(targetRows[1].Parent1)
local targetP2 = trim(targetRows[1].Parent2)
local whereParts = {}
if targetUnion then
table.insert(whereParts, 'UnionID="' .. esc(targetUnion) .. '"')
end
if targetP1 and targetP2 then
table.insert(whereParts, '(Parent1="' .. esc(targetP1) .. '" AND Parent2="' .. esc(targetP2) .. '")')
end
if #whereParts == 0 then
return { person }
end
local rows = cargoQuery(
'ParentChild',
'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
{
where = table.concat(whereParts, ' OR '),
limit = 100
}
)
table.sort(rows, function(a, b)
local aOrder = tonumber(a.BirthOrder) or 9999
local bOrder = tonumber(b.BirthOrder) or 9999
if aOrder == bOrder then
return tostring(a.Child):lower() < tostring(b.Child):lower()
end
return aOrder < bOrder
end)
local people = {}
local seen = {}
for _, row in ipairs(rows) do
addUnique(people, seen, row.Child)
end
if #people == 0 then
table.insert(people, person)
end
return people
end
local function buildCoupleGroups(people)
local groups = {}
local used = {}
local working = {}
for _, person in ipairs(people or {}) do
table.insert(working, person)
end
sorted(working)
for _, person in ipairs(working) do
if not used[person] then
local partners = getPartners(person)
local matchedPartner = nil
for _, partner in ipairs(partners) do
for _, candidate in ipairs(working) do
if candidate == partner and not used[candidate] then
matchedPartner = partner
break
end
end
if matchedPartner then
break
end
end
if matchedPartner then
used[person] = true
used[matchedPartner] = true
local union = getUnionBetween(person, matchedPartner)
table.insert(groups, {
type = 'couple',
left = person,
right = matchedPartner,
marriageYear = union and formatYear(union.MarriageDate) or nil
})
else
used[person] = true
table.insert(groups, {
type = 'single',
person = person
})
end
end
end
return groups
end
local function buildFamilyUnitsForGeneration(people)
local units = {}
for _, person in ipairs(people or {}) do
local partners = getPartners(person)
local children = getChildren(person)
if partners[1] then
local union = getUnionBetween(person, partners[1])
table.insert(units, {
person = person,
partner = partners[1],
marriageYear = union and formatYear(union.MarriageDate) or nil,
children = children
})
else
table.insert(units, {
person = person,
partner = nil,
marriageYear = nil,
children = children
})
end
end
return units
end
local function makeCard(pageName)
pageName = trim(pageName)
if not pageName then
return ''
end
local c = getCharacter(pageName)
local displayName = getDisplayName(pageName)
local birthYear = c and formatYear(c.BirthDate) or nil
local deathYear = c and formatYear(c.DeathDate) or nil
local years = ''
if birthYear or deathYear then
years = '<div class="ft-years">' .. (birthYear or '?') .. '–' .. (deathYear or '') .. '</div>'
end
return '<div class="ft-card">[[' .. pageName .. '|' .. displayName .. ']]' .. years .. '</div>'
end
local function makeCoupleGroupRow(groups)
if not groups or #groups == 0 then
return ''
end
local html = {}
table.insert(html, '<div class="ft-row">')
for _, group in ipairs(groups) do
if group.type == 'single' then
table.insert(html, '<div class="ft-unit ft-unit-single">')
table.insert(html, makeCard(group.person))
table.insert(html, '</div>')
else
table.insert(html, '<div class="ft-unit ft-unit-couple">')
table.insert(html, makeCard(group.left))
table.insert(html, '<div class="ft-marriage">')
if group.marriageYear then
table.insert(html, '<div class="ft-marriage-year">' .. group.marriageYear .. '</div>')
end
table.insert(html, '<div class="ft-marriage-line"></div>')
table.insert(html, '</div>')
table.insert(html, makeCard(group.right))
table.insert(html, '</div>')
end
end
table.insert(html, '</div>')
return table.concat(html)
end
local function makeFamilyUnitsRow(units)
if not units or #units == 0 then
return ''
end
local html = {}
table.insert(html, '<div class="ft-family-row">')
for _, unit in ipairs(units) do
table.insert(html, '<div class="ft-family-unit">')
table.insert(html, '<div class="ft-family-main">')
if unit.partner then
table.insert(html, '<div class="ft-unit ft-unit-couple">')
table.insert(html, makeCard(unit.person))
table.insert(html, '<div class="ft-marriage">')
if unit.marriageYear then
table.insert(html, '<div class="ft-marriage-year">' .. unit.marriageYear .. '</div>')
end
table.insert(html, '<div class="ft-marriage-line"></div>')
table.insert(html, '</div>')
table.insert(html, makeCard(unit.partner))
table.insert(html, '</div>')
else
table.insert(html, '<div class="ft-unit ft-unit-single">')
table.insert(html, makeCard(unit.person))
table.insert(html, '</div>')
end
table.insert(html, '</div>')
if unit.children and #unit.children > 0 then
table.insert(html, '<div class="ft-family-desc-line"></div>')
table.insert(html, '<div class="ft-family-children">')
for _, child in ipairs(unit.children) do
table.insert(html, makeCard(child))
end
table.insert(html, '</div>')
end
table.insert(html, '</div>')
end
table.insert(html, '</div>')
return table.concat(html)
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 visited = {}
local queue = {}
local head = 1
visited[root] = true
table.insert(queue, root)
while head <= #queue do
local current = queue[head]
head = head + 1
local neighbors = {}
local parents = getParents(current)
local children = getChildren(current)
local partners = getPartners(current)
for _, person in ipairs(parents) do
addSet(neighbors, person)
end
for _, person in ipairs(children) do
addSet(neighbors, person)
end
for _, person in ipairs(partners) do
addSet(neighbors, person)
end
for neighbor, _ in pairs(neighbors) do
if neighbor and not visited[neighbor] then
visited[neighbor] = true
table.insert(queue, neighbor)
end
end
end
local people = {}
for name, _ in pairs(visited) do
table.insert(people, name)
end
sorted(people)
local lines = {}
table.insert(lines, "'''Connected component for " .. getDisplayName(root) .. "'''")
table.insert(lines, '* Total people found: ' .. tostring(#people))
for _, person in ipairs(people) do
table.insert(lines, '* ' .. makeLinkedName(person))
end
return table.concat(lines, '\n')
end
function p.profile(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 parents = getParents(root)
local siblings = getSiblingRows(root)
local partners = getPartners(root)
local children = getChildren(root)
local siblingList = {}
for _, person in ipairs(siblings) do
if person ~= root then
table.insert(siblingList, person)
end
end
local lines = {}
table.insert(lines, '{| class="wikitable" style="width:100%; max-width:900px;"')
table.insert(lines, '|-')
table.insert(lines, '! colspan="2" | Family profile for ' .. getDisplayName(root))
table.insert(lines, '|-')
table.insert(lines, '! style="width:20%;" | Person')
table.insert(lines, '| ' .. makeLinkedName(root))
table.insert(lines, '|-')
table.insert(lines, '! Parents')
table.insert(lines, '| ' .. (#parents > 0 and linkList(parents) or '—'))
table.insert(lines, '|-')
table.insert(lines, '! Siblings')
table.insert(lines, '| ' .. (#siblingList > 0 and linkList(siblingList) or '—'))
table.insert(lines, '|-')
table.insert(lines, '! Partners')
table.insert(lines, '| ' .. (#partners > 0 and linkList(partners) or '—'))
table.insert(lines, '|-')
table.insert(lines, '! Children')
table.insert(lines, '| ' .. (#children > 0 and linkList(children) or '—'))
table.insert(lines, '|}')
return table.concat(lines, '\n')
end
function p.tree(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 parents = getParents(root)
local siblingGeneration = getSiblingRows(root)
local grandparents = {}
local gpSeen = {}
for _, parentName in ipairs(parents) do
local parentParents = getParents(parentName)
for _, gp in ipairs(parentParents) do
addUnique(grandparents, gpSeen, gp)
end
end
sorted(grandparents)
local grandparentGroups = buildCoupleGroups(grandparents)
local parentGroups = buildCoupleGroups(parents)
local familyUnits = buildFamilyUnitsForGeneration(siblingGeneration)
local html = {}
table.insert(html, '<div class="ft-tree">')
table.insert(html, '<div class="ft-title">Family tree for ' .. getDisplayName(root) .. '</div>')
if #grandparents > 0 then
table.insert(html, '<div class="ft-generation">')
table.insert(html, makeCoupleGroupRow(grandparentGroups))
table.insert(html, '</div>')
end
if #grandparents > 0 and #parents > 0 then
table.insert(html, '<div class="ft-connector"></div>')
end
if #parents > 0 then
table.insert(html, '<div class="ft-generation">')
table.insert(html, makeCoupleGroupRow(parentGroups))
table.insert(html, '</div>')
end
if #parents > 0 and #familyUnits > 0 then
table.insert(html, '<div class="ft-connector"></div>')
end
if #familyUnits > 0 then
table.insert(html, '<div class="ft-generation">')
table.insert(html, makeFamilyUnitsRow(familyUnits))
table.insert(html, '</div>')
end
table.insert(html, '</div>')
return table.concat(html)
end
return p