Module:SscCitations: Difference between revisions
From Bodhicitta
(Citations of a text in the Śikṣāsamuccaya, grouping consecutive segments into single quotations) |
(Citations of a text in the Śikṣāsamuccaya, grouping consecutive segments into single quotations) |
||
| Line 56: | Line 56: | ||
-- Every quote segment naming this id, grouped into quotations. | -- Every quote segment naming this id, grouped into quotations. | ||
local function fetchQuotations(id) | local function fetchQuotations(id) | ||
-- Matched with | -- Matched with a wildcard rather than an exact value: QuoteSource holds the | ||
-- label, "Ratnameghasūtra (RKTSK 231)", not the bare id. | -- whole label, "Ratnameghasūtra (RKTSK 231)", not the bare id. | ||
-- | |||
-- The trailing ")" is essential. Without it "RKTSK 12" also matches | |||
-- "RKTSK 127" and "RKTSK 129", which turned the Aṣṭasāhasrikā's 13 | |||
-- quotations into 39. Every one of the 65 QuoteSource values that carries an | |||
-- id closes the parenthesis immediately after it, so this is a safe anchor. | |||
local query = mw.smw.ask{ | local query = mw.smw.ask{ | ||
'[[' .. SSC_PREFIX .. ']][[QuoteSource::~*' .. id .. '*]]', | '[[' .. SSC_PREFIX .. ']][[QuoteSource::~*' .. id .. ')*]]', | ||
'?#-=Page', | '?#-=Page', | ||
'?SegmentOrder#=SegmentOrder', | '?SegmentOrder#=SegmentOrder', | ||
Revision as of 17:16, 25 August 2026
Documentation for this module may be created at Module:SscCitations/doc
local p = {}
-- Citations of a text in the Śikṣāsamuccaya.
--
-- The SSC is segmented and aligned in the Translation Memory namespace, one page
-- per segment. A segment that quotes another work carries SegmentFormat=Quote
-- and a QuoteSource naming the work, usually with its RKTS catalogue number:
--
-- QuoteSource=Ratnameghasūtra (RKTSK 231)
--
-- Text pages hold the same identifier in DrlPageName ("RKTSK 231"), so that is
-- the join: no title matching, unaffected by spelling, and it keeps Kangyur
-- (RKTSK) and Tengyur (RKTST) texts distinct. 60 of the 63 works the SSC quotes
-- have a page here, covering 92% of quote segments.
--
-- THE POINT OF THIS MODULE: one quotation is often split across several
-- consecutive segments. Counting segments therefore overstates the citations
-- badly — the Pitāputrasamāgamanasūtra has 62 quote segments but is quoted 14
-- times, and one run is 36 segments long. Across the SSC, 633 segments are 345
-- quotations. SMW has no notion of "consecutive", so the grouping is done here.
local SSC_PREFIX = '~Translation Memory:016-Tsadra-SSC-Root/*'
local SEGMENT_LIMIT = 1000
-- A quotation past this many words of translated text is flagged as long.
-- Word count, not segment count: segments vary wildly in size, and the single
-- longest quotation in the corpus (Ratnolkādhāraṇī, 8,330 words) is ONE
-- segment, so a segment count would have called it small. The median quotation
-- is 139 words and the 75th percentile 218, so 400 is clearly beyond normal
-- without being so rare it never shows.
local LONG_QUOTE_WORDS = 400
local function firstValue(v)
if type(v) == 'table' then return v[1] end
return v
end
-- The RKTS id for this page, e.g. "RKTSK 231". Absent on anything that is not a
-- canonical text, which is what keeps the feature off every other page without
-- needing a flag.
local function rktsId(frame)
local title = mw.title.getCurrentTitle().prefixedText
local query = mw.smw.ask{
'[[' .. title .. ']]',
'?DrlPageName#=DrlPageName',
limit = 1
}
if not query or not query[1] then return nil end
local id = firstValue(query[1].DrlPageName)
if type(id) ~= 'string' then return nil end
id = mw.text.trim(id)
if id:match('^RKTS[KT]%s') then return id end
return nil
end
-- Every quote segment naming this id, grouped into quotations.
local function fetchQuotations(id)
-- Matched with a wildcard rather than an exact value: QuoteSource holds the
-- whole label, "Ratnameghasūtra (RKTSK 231)", not the bare id.
--
-- The trailing ")" is essential. Without it "RKTSK 12" also matches
-- "RKTSK 127" and "RKTSK 129", which turned the Aṣṭasāhasrikā's 13
-- quotations into 39. Every one of the 65 QuoteSource values that carries an
-- id closes the parenthesis immediately after it, so this is a safe anchor.
local query = mw.smw.ask{
'[[' .. SSC_PREFIX .. ']][[QuoteSource::~*' .. id .. ')*]]',
'?#-=Page',
'?SegmentOrder#=SegmentOrder',
'?TranslationChapter#=TranslationChapter',
'?TranslationPageNumber#=TranslationPageNumber',
'?SegmentTranslation#=SegmentTranslation',
limit = SEGMENT_LIMIT
}
if not query then return {} end
local segments = {}
for _, row in ipairs(query) do
local order = tonumber(firstValue(row.SegmentOrder))
if order then
local text = firstValue(row.SegmentTranslation)
local words = 0
if type(text) == 'string' then
for _ in text:gmatch('%S+') do words = words + 1 end
end
table.insert(segments, {
order = order,
page = firstValue(row.Page),
chapter = firstValue(row.TranslationChapter),
pageNum = firstValue(row.TranslationPageNumber),
words = words,
})
end
end
table.sort(segments, function(a, b) return a.order < b.order end)
-- Collapse runs of consecutive SegmentOrder into one quotation, anchored to
-- the lowest order so a link lands at the start of the passage rather than
-- somewhere in its middle.
--
-- A chapter change breaks a run: two adjacent quotes from the same work in
-- different chapters of the SSC are two quotations. No run in the current
-- data actually crosses a chapter boundary, but the rule is correct and
-- costs nothing.
local quotations = {}
for _, seg in ipairs(segments) do
local last = quotations[#quotations]
if last and seg.order == last.lastOrder + 1 and seg.chapter == last.chapter then
last.lastOrder = seg.order
last.words = last.words + seg.words
else
table.insert(quotations, {
page = seg.page,
order = seg.order,
lastOrder = seg.order,
chapter = seg.chapter,
pageNum = seg.pageNum,
words = seg.words,
})
end
end
return quotations
end
-- Number of distinct quotations, for the button label and for deciding whether
-- to render anything at all.
function p.count(frame)
local id = rktsId(frame)
if not id then return '' end
local quotations = fetchQuotations(id)
if #quotations == 0 then return '' end
return tostring(#quotations)
end
-- The list itself.
function p.list(frame)
local id = rktsId(frame)
if not id then return '' end
local quotations = fetchQuotations(id)
if #quotations == 0 then return '' end
local out = {'<div class="ssc-citations">'}
for i, q in ipairs(quotations) do
table.insert(out, '<div class="ssc-citation">')
table.insert(out, '<span class="ssc-citation-index">' .. i .. '</span>')
table.insert(out, '<span class="ssc-citation-body">')
if q.chapter and q.chapter ~= '' then
table.insert(out, '<span class="ssc-citation-chapter">Chapter ' ..
tostring(q.chapter) .. '</span>')
end
if q.pageNum and q.pageNum ~= '' then
table.insert(out, '<span class="ssc-citation-page">p. ' ..
tostring(q.pageNum) .. '</span>')
end
if q.words > LONG_QUOTE_WORDS then
table.insert(out, '<span class="ssc-citation-long">long quotation</span>')
end
table.insert(out, '</span>')
if q.page then
table.insert(out, '<span class="ssc-citation-link">[[' .. q.page ..
'|Read the passage]]</span>')
end
table.insert(out, '</div>')
end
table.insert(out, '</div>')
return frame:preprocess(table.concat(out))
end
return p