Module:HPB-SB authors
Documentation for this module may be created at Module:HPB-SB authors/doc
local p = {}
-- Separators matching Template:HPB-SB authors raw row
local FS = mw.ustring.char(0x2063) -- field separator (INVISIBLE SEPARATOR)
local RS = mw.ustring.char(0x2064) -- record terminator (INVISIBLE PLUS)
local AS = mw.ustring.char(0x2062) -- author-value separator (INVISIBLE TIMES)
local function isempty(s)
return s == nil or s == ''
end
-- plain-text split, no Lua pattern magic involved
local function split_plain(str, sep)
local parts = {}
local start = 1
while true do
local s, e = string.find(str, sep, start, true)
if not s then
table.insert(parts, string.sub(str, start))
break
end
table.insert(parts, string.sub(str, start, s - 1))
start = e + 1
end
return parts
end
local function trim(s)
return (mw.ustring.gsub(s or '', '^%s*(.-)%s*$', '%1'))
end
-- Fetch one page (offset-limited) of raw item rows via the helper template.
local function fetchRows(frame, offset, limit)
local query = table.concat({
'{{#ask: [[HPB SB item author::+]]',
'|?HPB SB item author=a|+sep=' .. AS,
'|?HPB SB item ID=id',
'|?HPB SB volume=vol',
'|?HPB SB volume page=pg',
'|?HPB SB item title=t',
'|?HPB SB item untitled=u',
'|?HPB SB item source title=s',
'|?HPB SB item source details=d',
'|?HPB SB item publication date=p',
'|?#=page',
'| mainlabel=-',
'| link=none',
'| headers=hide',
'| named args=yes',
'| format=template',
'| template=HPB-SB authors raw row',
'| sort=HPB SB item author',
'| order=asc',
'| limit=' .. tostring(limit),
'| offset=' .. tostring(offset),
'}}'
}, '\n')
local text = frame:preprocess(query)
local records = split_plain(text, RS)
local rows = {}
for _, rec in ipairs(records) do
if rec ~= '' then
local f = split_plain(rec, FS)
if #f >= 9 then
table.insert(rows, {
a = f[1], page = f[2], t = f[3], u = f[4],
vol = f[5], pg = f[6], s = f[7], d = f[8], p = f[9]
})
end
end
end
return rows
end
-- Collect ALL matching item rows across the whole dataset, working around
-- Semantic MediaWiki's server-side per-query result cap ($smwgQMaxLimit)
-- by chaining several offset-paginated #ask calls. A page can legitimately
-- come back shorter than requested without being the last one, so a short
-- page is NOT treated as a stop condition -- only a truly empty page is.
local function fetchAllRows(frame)
local allRows = {}
local step = 500
local offset = 0
local maxIterations = 20 -- safety stop, far above what's needed today
for i = 1, maxIterations do
local rows = fetchRows(frame, offset, step)
if #rows == 0 then
break
end
for _, r in ipairs(rows) do
table.insert(allRows, r)
end
offset = offset + step
end
return allRows
end
-- Build a name -> {items...} map, splitting any multi-valued author field.
local function groupByAuthor(rows)
local map = {}
local order = {}
for _, r in ipairs(rows) do
local names = split_plain(r.a, AS)
for _, name in ipairs(names) do
name = trim(name)
if name ~= '' then
if not map[name] then
map[name] = {}
table.insert(order, name)
end
table.insert(map[name], r)
end
end
end
return map, order
end
local function renderItem(frame, r)
return frame:expandTemplate{ title = 'HPB-SB author items', args = {
['untitled'] = r.u, ['wiki page'] = r.page, ['title'] = r.t,
['source'] = r.s, ['details'] = r.d, ['publication'] = r.p,
['volume'] = r.vol, ['page'] = r.pg
}}
end
local function showName(frame, name)
local ok, result = pcall(function()
return frame:expandTemplate{ title = 'CTD show name', args = { [1] = name, ['mode'] = 'full LF' } }
end)
if ok and not isempty(result) then
return result
end
return name
end
-- Main entry point: {{#invoke: HPB-SB authors | list }}
function p.list(frame)
local rows = fetchAllRows(frame)
local map, order = groupByAuthor(rows)
table.sort(order)
local out = {}
for _, name in ipairs(order) do
table.insert(out, '<h3>' .. showName(frame, name) .. '</h3>')
table.insert(out, '<ol>')
local items = map[name]
table.sort(items, function(a, b) return (a.t or '') < (b.t or '') end)
for _, r in ipairs(items) do
table.insert(out, renderItem(frame, r))
end
table.insert(out, '</ol>')
end
return table.concat(out, '\n')
end
-- Diagnostic helper: {{#invoke: HPB-SB authors | debug }}
function p.debug(frame)
local rows = fetchAllRows(frame)
local map, order = groupByAuthor(rows)
table.sort(order)
local out = {}
table.insert(out, 'Total rows fetched: ' .. tostring(#rows))
table.insert(out, 'Unique authors: ' .. tostring(#order))
table.insert(out, 'Has "Kingsford, Anna": ' .. tostring(map['Kingsford, Anna'] ~= nil))
table.insert(out, 'Has "Kislingbury, Emily": ' .. tostring(map['Kislingbury, Emily'] ~= nil))
return table.concat(out, '<br>\n')
end
-- TEMPORARY diagnostic helper for the author-name cleanup project: dumps every
-- unique raw author string with the wiki pages it appears on, tab/semicolon
-- separated, for offline analysis. Safe to remove once that project is done.
function p.rawnames(frame)
local rows = fetchAllRows(frame)
local map, order = groupByAuthor(rows)
table.sort(order)
local out = {}
for _, name in ipairs(order) do
local pages = {}
local seen = {}
for _, r in ipairs(map[name]) do
if not seen[r.page] then
seen[r.page] = true
table.insert(pages, r.page)
end
end
table.insert(out, name .. '\t' .. table.concat(pages, ';'))
end
return '<pre>' .. table.concat(out, '\n') .. '</pre>'
end
return p