Module:Lucifer authors
Documentation for this module may be created at Module:Lucifer authors/doc
local p = {}
local FS, RS, AS = mw.ustring.char(0x2063), mw.ustring.char(0x2064), mw.ustring.char(0x2062)
local function split(s, sep)
local out, start = {}, 1
while true do
local a, b = string.find(s, sep, start, true)
if not a then table.insert(out, string.sub(s, start)); return out end
table.insert(out, string.sub(s, start, a - 1)); start = b + 1
end
end
local function trim(s) return mw.text.trim(s or '') end
-- Legacy records sometimes separate linked coauthors with commas rather than
-- the property's semicolon separator. Never split commas inside a page name.
local function authors(value)
local out = {}
for _, part in ipairs(split(value, AS)) do
if part:find('[[', 1, true) then
local start = 1
while true do
local a, b = part:find('%[%[[^]]+%]%]', start)
local rest = part:sub(start, a and a - 1 or #part)
rest = trim(rest:gsub('^[%s,;]+', ''):gsub('[%s,;]+$', ''))
if rest ~= '' and rest ~= 'and' then table.insert(out, rest) end
if not a then break end
table.insert(out, part:sub(a, b))
start = b + 1
end
else
table.insert(out, part)
end
end
return out
end
local function fetch(frame)
local rows, offset = {}, 0
for batch = 1, 100 do
local query = '{{#ask:[[Lucifer item author::+]]'
.. '|?Lucifer item author=a|+sep=' .. AS
.. '|?Lucifer item ID=id|?Lucifer item title=t'
.. '|?Lucifer item volume=v|?Lucifer item number=n|?Lucifer item page=pg'
.. '|?Lucifer item month=m|?Lucifer item year=y'
.. '|?Lucifer item links to text=links|+sep=; '
.. '|mainlabel=-|link=none|headers=hide|named args=yes'
.. '|format=template|template=Lucifer authors raw row'
.. '|sort=Lucifer item ID|order=asc|searchlabel=|limit=500|offset=' .. offset .. '}}'
local text = frame:preprocess(query):gsub('%[%[SMW::off%]%]', ''):gsub('%[%[SMW::on%]%]', '')
local count = 0
for _, record in ipairs(split(text, RS)) do
local f = split(record, FS)
if #f == 9 and trim(f[2]) ~= '' then
table.insert(rows, {a=trim(f[1]), id=f[2], t=f[3], v=f[4], n=f[5], pg=f[6], m=f[7], y=f[8], links=f[9]})
count = count + 1
end
end
if count == 0 then return rows end
offset = offset + count
end
error('Lucifer author index exceeds the pagination safety limit.')
end
local function group(rows)
local map, order = {}, {}
for _, r in ipairs(rows) do
local seen = {}
for _, raw in ipairs(authors(r.a)) do
raw = trim(raw)
local target = raw:match('^%[%[([^]|]+)|[^]]+%]%]$') or raw:match('^%[%[([^]]+)%]%]$')
local name = trim(target or raw)
if name ~= '' and not seen[name] then
seen[name] = true
if not map[name] then
map[name] = {target=target, label=raw, items={}}
table.insert(order, name)
end
table.insert(map[name].items, r)
end
end
end
table.sort(order, function(a,b) return mw.ustring.lower(a) < mw.ustring.lower(b) end)
return map, order
end
function p.list(frame)
local map, order = group(fetch(frame))
local out = {}
for _, name in ipairs(order) do
local author = map[name]
local label = author.label
if author.target then
label = frame:expandTemplate{title='CTD show name', args={[1]=author.target, mode='full LF'}}
label = '[[' .. author.target .. '|' .. label .. ']]'
end
table.insert(out, '<h3>' .. label .. '</h3>\n<ol>')
table.sort(author.items, function(a,b)
if a.t == b.t then return a.id < b.id end
return mw.ustring.lower(a.t) < mw.ustring.lower(b.t)
end)
for _, r in ipairs(author.items) do
local location = 'Lucifer/Vol' .. r.v .. '#n' .. r.n
local details = 'vol. ' .. r.v .. ', no. ' .. r.n .. ', ' .. r.m .. ' ' .. r.y .. ', pp. ' .. r.pg
if not r.pg:find('-', 1, true) then details = details:gsub(', pp%. ', ', p. ') end
local links = trim(r.links) ~= '' and ('; ' .. r.links) or ''
table.insert(out, '<li><i>' .. r.t .. '</i> <span style="color:grey; font-size:85%;">([[' .. location .. '|' .. details .. ']])' .. links .. '</span></li>')
end
table.insert(out, '</ol>')
end
return table.concat(out, '\n')
end
function p.debug(frame)
local rows = fetch(frame)
local _, order = group(rows)
return 'Total rows fetched: ' .. #rows .. '; unique authors: ' .. #order
end
return p