Module:SidebarPeople
From Bodhicitta
Documentation for this module may be created at Module:SidebarPeople/doc
local p = {}
-- Fetch display data for everyone in the sidebar in one query.
--
-- The badge template used to look each value up itself, which meant roughly
-- eight #show calls per person — a guard call and a value call for FirstImage,
-- MainNamePhon and MainNameTib each. A sidebar with six people ran about fifty
-- store lookups for what is really one question.
--
-- Two things this gets right that the old badge did not.
--
-- FirstImage is a Page-type property. Asked as ?FirstImage it renders as a
-- thumbnail and is unusable as a string, so the badge's #if guard on it always
-- saw nothing and every portrait fell back to the placeholder. The # suffix asks
-- for the raw title, which is what the filepath chain needs.
--
-- Person and organisation pages live at the TOP LEVEL, not under People/. All
-- 1701 pages in Category:People are top-level, and the properties that feed this
-- sidebar (BcaAuthor, Translator, Editor and so on) already store the bare page
-- name. The old 'People/' prefix therefore pointed at titles that do not exist.
--
-- Organisations carry Orgname rather than MainNamePhon, so both are fetched and
-- the badge falls back from one to the other.
local function fetchPeopleData(people)
local data = {}
if #people == 0 then
return data
end
-- One disjunctive query rather than one query per person.
local ok, result = pcall(mw.smw.ask, {
'[[' .. table.concat(people, '||') .. ']]',
'?FirstImage#=img',
'?MainNamePhon=phon',
'?MainNameTib=tib',
'?Orgname=org',
'mainlabel=page',
'link=none',
limit = 200
})
if not ok or type(result) ~= 'table' then
return data
end
for _, row in ipairs(result) do
local page = row.page
if type(page) == 'table' then
page = page.fulltext or page[1]
end
if page then
data[tostring(page)] = {
img = row.img or '',
phon = row.phon or '',
tib = row.tib or '',
org = row.org or ''
}
end
end
return data
end
function p.main(frame)
local pargs = frame:getParent().args
local typesIndexed = {
[1] = 'sourceauthor',
[2] = 'authors',
[3] = 'translators',
[4] = 'contributors',
[5] = 'editors',
[6] = 'compilers',
[7] = 'publisher',
[8] = 'creators',
[9] = 'performers',
[10] = 'producers'
}
local typeLabels = {
sourceauthor = "source author",
authors = "author",
translators = "translator",
contributors = "contributor",
editors = "editor",
compilers = "compiler",
publisher = "publisher",
creators = "creator",
performers = "performer",
producers = "producer"
}
local pplTbl = {}
for role, people in pairs(pargs) do
for person in string.gmatch(people, "([^;]+)") do
person = mw.text.trim(person)
if person ~= '' then
if pplTbl[person] then else
pplTbl[person] = {}
end
table.insert(pplTbl[person], role)
end
end
end
if next(pplTbl) == nil then
return ''
end
-- One query for every person in the sidebar, instead of the badge looking
-- each value up for itself. Sorted so the query text — and therefore the
-- rendered output and its parser-cache entry — is stable between renders.
local names = {}
for person, _ in pairs(pplTbl) do
if person ~= 'Anon' then
table.insert(names, person)
end
end
table.sort(names)
local peopleData = fetchPeopleData(names)
local output = '<div class="row mx-0 justify-content-center">'
for _, ptype in ipairs(typesIndexed) do
for person,roles in pairs(pplTbl) do
if person == 'Anon' then
elseif type(roles) == 'string' then
if ptype == roles then
local d = peopleData[person] or {}
output = output .. frame:expandTemplate{ title = 'SidebarPersonBadge', args = {
person ,
typeLabels[roles],
img = d.img or '',
phon = d.phon or '',
tib = d.tib or '',
org = d.org or ''
} }
pplTbl[person] = ''
end
elseif type(roles) == 'table' then
for _,role in ipairs(roles) do
if ptype == role then
local d = peopleData[person] or {}
output = output .. frame:expandTemplate{ title = 'SidebarPersonBadge', args = {
person ,
listRoles(roles, typeLabels, typesIndexed),
img = d.img or '',
phon = d.phon or '',
tib = d.tib or '',
org = d.org or ''
} }
pplTbl[person] = ''
end
end
end
end
end
output = output .. '</div>'
return output
end
function sortRoles(roles, typesIndexed)
local typeIndices = {}
for i, role in ipairs(typesIndexed) do
typeIndices[role] = i
end
local sortedRoles = {}
for role, _ in pairs(roles) do
table.insert(sortedRoles, role)
end
table.sort(sortedRoles, function(a, b)
return typeIndices[a] < typeIndices[b]
end)
return sortedRoles
end
function listRoles(roles, typeLabels, typesIndexed)
local uniqueRoles = {}
for _, role in pairs(roles) do
uniqueRoles[role] = true
end
local sortedRoles = sortRoles(uniqueRoles, typesIndexed)
local list = {}
for _, role in ipairs(sortedRoles) do
table.insert(list, typeLabels[role])
end
return table.concat(list, ';')
end
function makeTblUnique(indexedTable)
local uniqueTbl = {}
for _, value in ipairs(indexedTable) do
uniqueTbl[value] = true
end
indexedTable = {}
for value,_ in pairs(uniqueTbl) do
table.insert(indexedTable, value)
end
return indexedTable
end
return p