Module:SegmentQuery: Difference between revisions
From Bodhicitta
(VSCode edit) |
(VSCode edit) |
||
| Line 27: | Line 27: | ||
local ALL_PRINTOUTS = { | local ALL_PRINTOUTS = { | ||
'SegmentSource', 'SourceTranslit', 'SegmentTranslation', | 'SegmentSource', 'SourceTranslit', 'SegmentTranslation', | ||
'SegmentOrder', 'RootTextChapVerseNum', 'SegmentFormat', | 'SegmentOrder', 'RootTextChapVerseNum', 'RootTextVerseNum', 'SegmentFormat', | ||
'SanskritSource', 'SanskritTranslit', 'TransMemNum', | 'SanskritSource', 'SanskritTranslit', 'TransMemNum', | ||
'SegmentSourceTsadra', 'SegmentTranslitTsadra', | 'SegmentSourceTsadra', 'SegmentTranslitTsadra', | ||
Revision as of 17:34, 23 April 2026
Documentation for this module may be created at Module:SegmentQuery/doc
-- Module:SegmentQuery
-- Unified module for bilingual segment queries and formatting.
-- Handles chunked deferred queries, row formatting, and language rendering.
--
-- Entry points:
-- {{#invoke:SegmentQuery|renderChunks|chapter}} — from DisplayTmSection
-- {{#invoke:SegmentQuery|formatRow}} — from Template:DisplaySegmentRow (SMW result template)
local p = {}
-------------------------------------------------------------------------------
-- Configuration
-------------------------------------------------------------------------------
local CHUNK_SIZE_NORMAL = 20
local CHUNK_SIZE_LARGE = 3 -- smaller chunks for chapters with "Large content" segments
local MAX_SEGMENTS = 500 -- absolute safety cap
-- TransMemID prefixes that indicate a non-English SegmentTranslation
local TRANSMEM_LANG = {
['004-Tsadra-BCA-Root-Padmakara-French'] = 'french',
['021-Tsadra-BCA-Padmakara-Spanish'] = 'spanish',
}
-- All printouts the #ask query can request.
-- LeaveOutArgs suppresses individual printouts by name.
local ALL_PRINTOUTS = {
'SegmentSource', 'SourceTranslit', 'SegmentTranslation',
'SegmentOrder', 'RootTextChapVerseNum', 'RootTextVerseNum', 'SegmentFormat',
'SanskritSource', 'SanskritTranslit', 'TransMemNum',
'SegmentSourceTsadra', 'SegmentTranslitTsadra',
'SourceSabcheNumber', 'QuoteSource', 'Flag', 'TransMemID',
}
-- Printouts that are always included regardless of LeaveOutArgs
local ALWAYS_INCLUDE = {
SegmentOrder = true, RootTextChapVerseNum = true, SegmentFormat = true,
TransMemNum = true, SourceSabcheNumber = true, QuoteSource = true,
Flag = true, TransMemID = true,
}
-- Priority order for default visible language per side
local leftPriority = { 'tibetan', 'tibetan-tsadra', 'devanagari', 'wylie', 'wylie-tsadra', 'sanskrit' }
local rightPriority = { 'english', 'french', 'spanish', 'wylie', 'wylie-tsadra' }
-------------------------------------------------------------------------------
-- Language helpers (shared by formatRow)
-------------------------------------------------------------------------------
-- Build language table, routing SegmentTranslation to the correct language
-- based on TransMemID (defaults to english)
local function buildLanguages(transMemLang)
local englishParams = (transMemLang == 'english') and {'SegmentTranslation'} or {}
local frenchParams = (transMemLang == 'french') and {'SegmentTranslation', 'FrenchTranslation', 'French'} or {'FrenchTranslation', 'French'}
local spanishParams = (transMemLang == 'spanish') and {'SegmentTranslation', 'Spanish'} or {'Spanish'}
return {
{ id = 'tibetan', params = {'SegmentSource'}, classPrefix = 'tibetan-version', textClass = 'text-160', langType = 'source' },
{ id = 'tibetan-tsadra', params = {'SegmentSourceTsadra'}, classPrefix = 'tibetan-tsadra-version', textClass = 'text-160', langType = 'source' },
{ id = 'english', params = englishParams, classPrefix = 'english-version', textClass = 'text-article', langType = 'translation' },
{ id = 'wylie', params = {'SourceTranslit'}, classPrefix = 'wylie-version', textClass = 'text-article', langType = 'translation' },
{ id = 'wylie-tsadra', params = {'SegmentTranslitTsadra'}, classPrefix = 'wylie-tsadra-version', textClass = 'text-article', langType = 'translation' },
{ id = 'french', params = frenchParams, classPrefix = 'french-version', textClass = 'text-article', langType = 'translation' },
{ id = 'spanish', params = spanishParams, classPrefix = 'spanish-version', textClass = 'text-article', langType = 'translation' },
{ id = 'devanagari', params = {'SanskritSource'}, classPrefix = 'devanagari-version', textClass = 'text-160', langType = 'source' },
{ id = 'sanskrit', params = {'SanskritTranslit'}, classPrefix = 'sanskrit-version', textClass = 'text-article', langType = 'translation' },
}
end
-- Get first non-empty value from a list of param names
local function getParamValue(args, paramNames)
for _, name in ipairs(paramNames) do
local val = args[name]
if val and mw.text.trim(val) ~= '' then
return mw.text.trim(val)
end
end
return nil
end
-- Resolve default language for a side: first priority-list entry with content in args
local function resolveDefault(args, priorityList, languages)
local paramsByLang = {}
for _, lang in ipairs(languages) do
paramsByLang[lang.id] = lang.params
end
for _, id in ipairs(priorityList) do
local params = paramsByLang[id]
if params and #params > 0 and getParamValue(args, params) then
return id
end
end
return priorityList[1]
end
-- Filter languages to only the requested one (single-language mode)
local function filterLanguages(langs, targetId)
local filtered = {}
for _, lang in ipairs(langs) do
if lang.id == targetId then
table.insert(filtered, lang)
end
end
return filtered
end
-- Check if a language id belongs to a priority list
local function inPriorityList(priorityList, langId)
for _, id in ipairs(priorityList) do
if id == langId then return true end
end
return false
end
-------------------------------------------------------------------------------
-- Formatting helpers (absorbed from FormatSegmentRow)
-------------------------------------------------------------------------------
-- Convert semicolon-separated page names into wiki links
local function wikiLinkList(str)
local links = {}
for page in mw.text.gsplit(str, '%s*;%s*') do
if page ~= '' then
table.insert(links, '[[' .. page .. ']]')
end
end
return table.concat(links, ', ')
end
-- Wrap content with poem extension tag (for translation-side verse formats)
local function wrapPoem(frame, content)
return frame:extensionTag('poem', '<p class="m-0">' .. content .. '</p>')
end
-- Apply SegmentFormat styling to content
-- isSource: true for source-type languages, false for translation-type
local function formatContent(content, format, isSource, frame, sabcheNum, quoteSource)
if not content then return nil end
if format == 'Root' then
if isSource then
return '<div class="pl-4" style="color: #8c1409;"><p>' .. content .. '</p></div>'
else
return '<div class="p-bl-fix pl-4 my-2" style="color: #8c1409;"><p>' .. content .. '</p></div>'
end
elseif format == 'Title' then
if isSource then
return '<span class="text-130" style="color: #467083;">' .. content .. '</span>'
else
return '<div class="h3 font-serif-alt" style="color: #467083; white-space: break-spaces;">' .. content .. '</div>'
end
elseif format == 'Commentary' or format == 'Commentary2' or format == 'Colophon' then
return content
elseif format == 'Quote' then
local noteSuffix = ''
if quoteSource and quoteSource ~= '' then
noteSuffix = frame:expandTemplate{ title = 'note', args = { 'source', wikiLinkList(quoteSource) } }
end
if isSource then
return '<div class="pl-4" style="color: #897435;">' .. content .. noteSuffix .. '</div>'
else
return '<div class="pl-4" style="color: #897435;">' .. wrapPoem(frame, content .. noteSuffix) .. '</div>'
end
elseif format == 'DoubleQuote' or format == 'Verse inside a quote' then
local noteSuffix = ''
if quoteSource and quoteSource ~= '' then
noteSuffix = frame:expandTemplate{ title = 'note', args = { 'source', wikiLinkList(quoteSource) } }
end
if isSource then
return '<div class="pl-5" style="color: #897435;">' .. content .. noteSuffix .. '</div>'
else
return '<div class="pl-5" style="color: #897435;">' .. wrapPoem(frame, content .. noteSuffix) .. '</div>'
end
elseif format == 'Sabche' then
if isSource then
return '<span style="color: #467083;">' .. content .. '</span>'
else
local sabche = '<div class="p-bl-fix"><span style="color: #467083;">' .. content .. '</span>'
if sabcheNum then
sabche = sabche .. ' <span class="float-right">' .. sabcheNum .. '</span>'
end
return sabche .. '</div>'
end
elseif format == 'SabcheHeading' then
if isSource then
return '<div class="mt-3" style="color: #467083;">' .. content .. '</div>'
else
local heading = '<div class="h5 mt-3 font-serif-alt" style="color: #467083;">' .. content
if sabcheNum then
heading = heading .. ' <span class="float-right">' .. sabcheNum .. '</span>'
end
return heading .. '</div>'
end
elseif format == 'SabcheSubHeading' then
if isSource then
return '<div class="mt-2" style="color: #467083;">' .. content .. '</div>'
else
local subheading = '<div class="h6 mt-2 font-serif-alt" style="color: #467083;">' .. content
if sabcheNum then
subheading = subheading .. ' <span class="float-right">' .. sabcheNum .. '</span>'
end
return subheading .. '</div>'
end
elseif format == 'Section Title1' then
if isSource then
return content
else
return '<div class="h3 font-serif-alt" style="color: #467083; white-space: break-spaces;">' .. content .. '</div>'
end
elseif format == 'Section Title2' then
if isSource then
return '<span class="text-120" style="color: #467083;">' .. content .. '</span>'
else
return '<div class="h4 font-serif-alt" style="color: #467083; white-space: break-spaces;">' .. content .. '</div>'
end
elseif format == 'Indented Verse' then
if isSource then
return '<div class="pl-4">' .. content .. '</div>'
else
return '<div class="pl-4">' .. wrapPoem(frame, content) .. '</div>'
end
elseif format == 'Root non-verse' then
return '<span style="color: #8c1409;">' .. content .. '</span>'
else
-- Default: no special formatting
return content
end
end
-- Render all language divs for one side (left or right)
local function renderSide(args, side, format, frame, defaultLang, languages)
local sabcheNum = getParamValue(args, {'SourceSabcheNumber'})
local quoteSource = getParamValue(args, {'QuoteSource'})
local parts = {}
for _, lang in ipairs(languages) do
if #lang.params > 0 then
local content = getParamValue(args, lang.params)
if content then
local isSource = (lang.langType == 'source')
local formatted = formatContent(content, format, isSource, frame, sabcheNum, quoteSource)
if formatted then
local hidden = lang.id ~= defaultLang and ' hidden' or ''
local classes = 'toggleable ' .. lang.classPrefix .. '-' .. side .. ' ' .. lang.textClass .. hidden
table.insert(parts, '<div class="' .. classes .. '">' .. formatted .. '</div>')
end
end
end
end
return table.concat(parts)
end
-------------------------------------------------------------------------------
-- Staff group check (cached per page parse)
-------------------------------------------------------------------------------
local isStaffCache = nil
local function isStaff(frame)
if isStaffCache == nil then
isStaffCache = mw.text.trim(frame:preprocess('{{#ifingroup:staff|1|0}}')) == '1'
end
return isStaffCache
end
-------------------------------------------------------------------------------
-- SingleLanguage / SingleColumn check (cached per page parse)
-------------------------------------------------------------------------------
local singleLangCache -- nil = not checked yet, false = not set
local singleColumnCache
local function getSingleLang(frame)
if singleLangCache == nil then
local pageTitle = mw.title.getCurrentTitle().prefixedText
local raw = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleLanguage'))
singleLangCache = (raw ~= '') and raw:lower() or false
end
return singleLangCache
end
local function getSingleColumn(frame)
if singleColumnCache == nil then
local pageTitle = mw.title.getCurrentTitle().prefixedText
local raw = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleColumn')):lower()
singleColumnCache = (raw == 'true' or raw == '1' or raw == 't')
end
return singleColumnCache
end
-------------------------------------------------------------------------------
-- formatRow: called by Template:DisplaySegmentRow for each SMW result
-- (absorbs the old DisplaySegmentRow template wrapper + FormatSegmentRow module)
-------------------------------------------------------------------------------
function p.formatRow(frame)
local args = frame:getParent().args
local format = getParamValue(args, {'SegmentFormat'}) or 'Commentary'
-- Determine which language SegmentTranslation holds based on TransMemID
local transMemId = getParamValue(args, {'TransMemID'}) or ''
local transMemLang = TRANSMEM_LANG[transMemId] or 'english'
local languages = buildLanguages(transMemLang)
local defaultLeft = resolveDefault(args, leftPriority, languages)
local defaultRight = resolveDefault(args, rightPriority, languages)
local singleLang = getSingleLang(frame)
local singleColumn = getSingleColumn(frame)
local singleIsLeft = singleLang and inPriorityList(leftPriority, singleLang)
local singleIsRight = singleLang and inPriorityList(rightPriority, singleLang)
-- Build content columns
local content
if singleColumn then
local rendered = renderSide(args, 'left', format, frame, defaultLeft, languages)
content = '<div class="col-12 font-serif line14 p-bl-fix mb-3">' .. rendered .. '</div>'
elseif singleLang then
if singleIsLeft then
local rendered = renderSide(args, 'left', format, frame, singleLang, filterLanguages(languages, singleLang))
content = '<div class="col-12 font-serif line14 p-bl-fix mb-3">' .. rendered .. '</div>'
elseif singleIsRight then
local rendered = renderSide(args, 'right', format, frame, singleLang, filterLanguages(languages, singleLang))
content = '<div class="col-12 line14 p-bl-fix mb-3">' .. rendered .. '</div>'
else
local left = renderSide(args, 'left', format, frame, defaultLeft, languages)
local right = renderSide(args, 'right', format, frame, defaultRight, languages)
content = '<div class="col-md-6 font-serif line14 p-bl-fix mb-3">' .. left .. '</div>'
.. '<div class="col-md-6 line14 p-bl-fix mb-3">' .. right .. '</div>'
end
else
local left = renderSide(args, 'left', format, frame, defaultLeft, languages)
local right = renderSide(args, 'right', format, frame, defaultRight, languages)
content = '<div class="col-md-6 font-serif line14 p-bl-fix mb-3">' .. left .. '</div>'
.. '<div class="col-md-6 line14 p-bl-fix mb-3">' .. right .. '</div>'
end
-- Wrap in row div (previously done by Template:DisplaySegmentRow)
local parts = { '<div class="row bilingual-row py-1 filterable">' }
-- Staff debug info
if isStaff(frame) then
local resultNum = args['#'] or ''
local pageTitle = args[1] or ''
local segOrder = getParamValue(args, {'SegmentOrder'}) or ''
local titleObj = pageTitle ~= '' and mw.title.new(pageTitle)
local url = titleObj and tostring(titleObj:fullUrl()) or ''
table.insert(parts,
'<div class="col-12 text-center font-sans-wiki text-90 py-1 mb-2">'
.. 'Query result #' .. resultNum
.. ' · '
.. '[' .. url .. ' '
.. '<i class="fa-sharp-duotone fa-memo fa-sm"></i> TM segment #' .. segOrder
.. ']</div>'
)
end
table.insert(parts, content)
table.insert(parts, '</div>')
return table.concat(parts)
end
-------------------------------------------------------------------------------
-- renderChunks: emits deferred #ask queries, called from DisplayTmSection
-- Now also builds the #ask wikitext directly (absorbs Template:SegmentQuery)
-------------------------------------------------------------------------------
function p.renderChunks(frame)
local currentTitle = mw.title.getCurrentTitle()
local pargs = frame:getParent().args
local leaveOutArgs = pargs.LeaveOutArgs or ''
local transMemID = mw.text.trim(pargs.TransMemID or '')
-- Mirror SegmentQuery's fallback: if TranslationChapter not passed, use SUBPAGENAME
local chapterRaw = mw.text.trim(frame.args[1] or '')
local hasChapter = chapterRaw ~= ''
-- Build chapter condition
local chapterCondition = ''
if hasChapter then
local values = {}
for v in chapterRaw:gmatch('[^;]+') do
table.insert(values, mw.text.trim(v))
end
chapterCondition = '[[TranslationChapter::' .. table.concat(values, '||') .. ']]'
end
local baseConditions =
'[[TransMemID::' .. transMemID .. ']]' ..
chapterCondition ..
'[[SegmentFormat::!Deprecated]]'
-- 1. Count total segments
local countResult = mw.smw.ask(
baseConditions ..
'|?SegmentOrder' ..
'|limit=' .. MAX_SEGMENTS ..
'|link=none'
)
local totalSegments = countResult and #countResult or 0
if totalSegments == 0 then
return '<!-- SegmentQuery: 0 segments found for TransMemID=' .. transMemID .. ' -->'
end
-- 2. Check for any "Large content" flag in this chapter
local largeCheck = mw.smw.ask(
baseConditions ..
'[[Flag::Large content]]' ..
'|?SegmentOrder' ..
'|limit=1' ..
'|link=none'
)
local chunkSize = (largeCheck and #largeCheck > 0) and CHUNK_SIZE_LARGE or CHUNK_SIZE_NORMAL
-- 3. Build printout list, respecting LeaveOutArgs
local printouts = {}
for _, name in ipairs(ALL_PRINTOUTS) do
if ALWAYS_INCLUDE[name] or not leaveOutArgs:find(name, 1, true) then
table.insert(printouts, '|?' .. name)
end
end
local printoutStr = table.concat(printouts)
-- 4. Emit one deferred #ask per chunk
local parts = {}
local offset = 0
while offset < totalSegments do
-- Build the full #ask wikitext
local ask = '{{#ask:'
.. baseConditions
.. printoutStr
.. '|named args=true'
.. '|sort=SegmentOrder'
.. '|order=asc'
.. '|format=template'
.. '|template=DisplaySegmentRow'
.. '|limit=' .. chunkSize
.. '|offset=' .. offset
.. '|link=none'
.. '|intro=<div class="filterableContent mb-4">'
.. '|outro=</div>'
.. '|searchlabel='
.. '|default='
.. '|@deferred'
.. '}}'
table.insert(parts, frame:preprocess(ask))
offset = offset + chunkSize
end
return table.concat(parts)
end
return p