Module:BCIChapters: Difference between revisions

From Candypedia
No edit summary
No edit summary
Line 1: Line 1:
local p = {}
local p = {}


-- Special cases mapping: Actual title to MediaWiki-safe title and sequence number
-- Utility function to log tables
local special_cases = {
local function logTable(tbl, prefix)
     ["Augustus, You Jerk #2"] = { safe_title = "Augustus, You Jerk 2", seq = 24 }
    local result = {}
}
     local function log(tbl, level)
        for k, v in pairs(tbl) do
            local key = prefix .. string.rep(" ", level * 2) .. tostring(k)
            if type(v) == "table" then
                result[#result + 1] = key .. " = table"
                log(v, level + 1)
            else
                result[#result + 1] = key .. " = " .. tostring(v)
            end
        end
    end
    log(tbl, 0)
    mw.log(result)
end


function p.urlDecode(str)
function p.urlDecode(str)
Line 16: Line 29:
     str = string.gsub(str, "%%27", "'")
     str = string.gsub(str, "%%27", "'")
     return str
     return str
end
function p.getSafeTitle(title)
    if special_cases[title] then
        return special_cases[title].safe_title
    end
    return title
end
function p.getOriginalTitle(safeTitle)
    for original, data in pairs(special_cases) do
        if data.safe_title == safeTitle then
            return original
        end
    end
    return safeTitle
end
end


Line 50: Line 47:
     local chapters = {}
     local chapters = {}
     for seq, title, date, pagecount in string.gmatch(content, "{{BCIChapter|seq=(%d+)|title=([^|]+)|date=([^|]+)|pagecount=(%d+)}}") do
     for seq, title, date, pagecount in string.gmatch(content, "{{BCIChapter|seq=(%d+)|title=([^|]+)|date=([^|]+)|pagecount=(%d+)}}") do
         table.insert(chapters, {seq = tonumber(seq), title = title, date = date, pagecount = tonumber(pagecount)})
         table.insert(chapters, {seq = seq, title = title, date = date, pagecount = tonumber(pagecount)})
     end
     end


     mw.logObject(chapters, "Chapters list") -- Debug: log the parsed chapters
     logTable(chapters, "Chapters list = ")
 
     return chapters
     return chapters
end
end


function p.getChapterBySeq(seq)
function p.getChapterDetails(chapterTitle, detail)
     local chapters = p.getChaptersList()
     local chapters = p.getChaptersList()
    local decodedTitle = p.urlDecode(chapterTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
     for _, chapter in ipairs(chapters) do
     for _, chapter in ipairs(chapters) do
         if chapter.seq == tonumber(seq) then
         if chapter.title == normalizedTitle then
             mw.logObject(chapter, "Found chapter by seq: " .. seq) -- Debug: log the found chapter
             mw.log("Found detail for title:", normalizedTitle, detail, chapter[detail])
             return chapter
             return chapter[detail]
         end
         end
     end
     end
     mw.log("No chapter found for seq: " .. seq) -- Debug: log if no chapter is found
     mw.log("No detail found for title:", chapterTitle, "detail:", detail)
     return nil
     return nil
end
end


function p.getChapterDetailsBySeq(seq, detail)
function p.getChapterDetailsBySeq(seq, detail)
    local chapter = p.getChapterBySeq(seq)
    if chapter then
        mw.log("Detail found for seq " .. seq .. ": " .. detail .. " = " .. tostring(chapter[detail]))  -- Debug: log the detail found
        return chapter[detail]
    end
    mw.log("No detail found for seq: " .. seq .. ", detail: " .. detail)  -- Debug: log if no detail is found
    return nil
end
function p.getChapterDetails(chapterTitle, detail)
     local chapters = p.getChaptersList()
     local chapters = p.getChaptersList()
    local safeTitle = p.getSafeTitle(chapterTitle)
    local decodedTitle = p.urlDecode(safeTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
     for _, chapter in ipairs(chapters) do
     for _, chapter in ipairs(chapters) do
         if chapter.title == normalizedTitle then
         if tonumber(chapter.seq) == tonumber(seq) then
             mw.log("Detail found for title " .. chapterTitle .. ": " .. detail .. " = " .. tostring(chapter[detail]))  -- Debug: log the detail found
             mw.log("Found detail for seq:", seq, detail, chapter[detail])
             return chapter[detail]
             return chapter[detail]
         end
         end
     end
     end
     mw.log("No detail found for title: " .. chapterTitle .. ", detail: " .. detail) -- Debug: log if no detail is found
     mw.log("No detail found for seq:", seq, "detail:", detail)
     return nil
     return nil
end
end
Line 97: Line 82:
function p.getNextChapter(chapterTitle)
function p.getNextChapter(chapterTitle)
     local chapters = p.getChaptersList()
     local chapters = p.getChaptersList()
    local safeTitle = p.getSafeTitle(chapterTitle)
     local decodedTitle = p.urlDecode(chapterTitle)
     local decodedTitle = p.urlDecode(safeTitle)
     local normalizedTitle = p.normalizeApostrophes(decodedTitle)
     local normalizedTitle = p.normalizeApostrophes(decodedTitle)
     for i, chapter in ipairs(chapters) do
     for i, chapter in ipairs(chapters) do
         if chapter.title == normalizedTitle and i < #chapters then
         if chapter.title == normalizedTitle and i < #chapters then
             mw.log("Next chapter for title " .. chapterTitle .. ": " .. chapters[i + 1].title) -- Debug: log the next chapter
             mw.log("Next chapter for title:", normalizedTitle, "is", chapters[i + 1].title)
             return chapters[i + 1].title
             return chapters[i + 1].title
         end
         end
     end
     end
     mw.log("No next chapter found for title: " .. chapterTitle) -- Debug: log if no next chapter is found
     mw.log("No next chapter found for title:", chapterTitle)
     return nil
     return nil
end
end


function p.getPreviousChapter(chapterTitle)
function p.getNextChapterBySeq(seq)
     local chapters = p.getChaptersList()
     local chapters = p.getChaptersList()
    local safeTitle = p.getSafeTitle(chapterTitle)
    local decodedTitle = p.urlDecode(safeTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
     for i, chapter in ipairs(chapters) do
     for i, chapter in ipairs(chapters) do
         if chapter.title == normalizedTitle and i > 1 then
         if tonumber(chapter.seq) == tonumber(seq) and i < #chapters then
             mw.log("Previous chapter for title " .. chapterTitle .. ": " .. chapters[i - 1].title) -- Debug: log the previous chapter
             mw.log("Next chapter for seq:", seq, "is", chapters[i + 1].title)
             return chapters[i - 1].title
             return chapters[i + 1].title
         end
         end
     end
     end
     mw.log("No previous chapter found for title: " .. chapterTitle) -- Debug: log if no previous chapter is found
     mw.log("No next chapter found for seq:", seq)
     return nil
     return nil
end
end


function p.getNextChapterBySeq(seq)
function p.getPreviousChapter(chapterTitle)
     local chapters = p.getChaptersList()
     local chapters = p.getChaptersList()
    local decodedTitle = p.urlDecode(chapterTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
     for i, chapter in ipairs(chapters) do
     for i, chapter in ipairs(chapters) do
         if chapter.seq == tonumber(seq) and i < #chapters then
         if chapter.title == normalizedTitle and i > 1 then
             mw.log("Next chapter for seq " .. seq .. ": " .. chapters[i + 1].title) -- Debug: log the next chapter by seq
             mw.log("Previous chapter for title:", normalizedTitle, "is", chapters[i - 1].title)
             return chapters[i + 1].title
             return chapters[i - 1].title
         end
         end
     end
     end
     mw.log("No next chapter found for seq: " .. seq) -- Debug: log if no next chapter is found by seq
     mw.log("No previous chapter found for title:", chapterTitle)
     return nil
     return nil
end
end
Line 140: Line 123:
     local chapters = p.getChaptersList()
     local chapters = p.getChaptersList()
     for i, chapter in ipairs(chapters) do
     for i, chapter in ipairs(chapters) do
         if chapter.seq == tonumber(seq) and i > 1 then
         if tonumber(chapter.seq) == tonumber(seq) and i > 1 then
             mw.log("Previous chapter for seq " .. seq .. ": " .. chapters[i - 1].title) -- Debug: log the previous chapter by seq
             mw.log("Previous chapter for seq:", seq, "is", chapters[i - 1].title)
             return chapters[i - 1].title
             return chapters[i - 1].title
         end
         end
     end
     end
     mw.log("No previous chapter found for seq: " .. seq) -- Debug: log if no previous chapter is found by seq
     mw.log("No previous chapter found for seq:", seq)
     return nil
     return nil
end
end
Line 160: Line 143:
function p.infoboxBCIChapter(frame)
function p.infoboxBCIChapter(frame)
     local chapterTitle = frame.args.chapter_title or mw.title.getCurrentTitle().text
     local chapterTitle = frame.args.chapter_title or mw.title.getCurrentTitle().text
     local specialCaseData = special_cases[chapterTitle]
     local decodedTitle = p.urlDecode(chapterTitle)
    local seq = specialCaseData and specialCaseData.seq or frame.args.seq
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
    local chapter, date, pagecount, next_chapter, prev_chapter
    local seq = frame.args.seq
 
    if specialCaseData then
        -- Use sequence number for special cases
        chapter = p.getChapterBySeq(seq)
        if not chapter then
            mw.log("No data found for chapter sequence number: " .. seq)  -- Debug: log if no chapter is found for the special case seq
            return "No data found for chapter sequence number: " .. seq
        end
        date = chapter.date
        pagecount = chapter.pagecount
        next_chapter = p.getNextChapterBySeq(seq) or "N/A"
        prev_chapter = p.getPreviousChapterBySeq(seq) or "N/A"
        chapterTitle = specialCaseData.safe_title
        mw.logObject({
            chapterTitle = chapterTitle,
            date = date,
            pagecount = pagecount,
            next_chapter = next_chapter,
            prev_chapter = prev_chapter,
            seq = seq
        }, "Infobox data for special case")  -- Debug: log the infobox data for the special case
    else
        -- Use title for regular chapters
        chapterTitle = p.getOriginalTitle(chapterTitle)
        date = p.getChapterDetails(chapterTitle, "date") or "N/A"
        pagecount = p.getChapterDetails(chapterTitle, "pagecount") or "N/A"
        next_chapter = p.getNextChapter(chapterTitle) or "N/A"
        prev_chapter = p.getPreviousChapter(chapterTitle) or "N/A"
        mw.logObject({
            chapterTitle = chapterTitle,
            date = date,
            pagecount = pagecount,
            next_chapter = next_chapter,
            prev_chapter = prev_chapter
        }, "Infobox data for regular chapter")  -- Debug: log the infobox data for regular chapters
    end
 
     local image = frame.args.image
     local image = frame.args.image
     local caption = frame.args.caption
     local caption = frame.args.caption
Line 207: Line 153:


     caption = caption and caption ~= "" and caption or nil
     caption = caption and caption ~= "" and caption or nil
    local date, pagecount, next_chapter, prev_chapter
    if seq and special_cases[normalizedTitle] then
        date = p.getChapterDetailsBySeq(seq, "date") or "N/A"
        pagecount = p.getChapterDetailsBySeq(seq, "pagecount") or "N/A"
        next_chapter = p.getNextChapterBySeq(seq) or "N/A"
        prev_chapter = p.getPreviousChapterBySeq(seq) or "N/A"
    else
        date = p.getChapterDetails(normalizedTitle, "date") or "N/A"
        pagecount = p.getChapterDetails(normalizedTitle, "pagecount") or "N/A"
        next_chapter = p.getNextChapter(normalizedTitle) or "N/A"
        prev_chapter = p.getPreviousChapter(normalizedTitle) or "N/A"
    end
    mw.log("Infobox data for regular chapter =", {
        chapterTitle = chapterTitle,
        date = date,
        pagecount = pagecount,
        next_chapter = next_chapter,
        prev_chapter = prev_chapter
    })


     local infobox = mw.html.create('table')
     local infobox = mw.html.create('table')
     infobox:addClass('infobox')
     infobox:addClass('infobox')
-- Add title as heading
 
    -- Add title as heading
     infobox:tag('tr')
     infobox:tag('tr')
         :tag('th')
         :tag('th')

Revision as of 13:13, 27 May 2024

Documentation for this module may be created at Module:BCIChapters/doc

local p = {}

-- Utility function to log tables
local function logTable(tbl, prefix)
    local result = {}
    local function log(tbl, level)
        for k, v in pairs(tbl) do
            local key = prefix .. string.rep(" ", level * 2) .. tostring(k)
            if type(v) == "table" then
                result[#result + 1] = key .. " = table"
                log(v, level + 1)
            else
                result[#result + 1] = key .. " = " .. tostring(v)
            end
        end
    end
    log(tbl, 0)
    mw.log(result)
end

function p.urlDecode(str)
    str = string.gsub(str, '%%(%x%x)', function(x) return string.char(tonumber(x, 16)) end)
    return str
end

function p.normalizeApostrophes(str)
    str = string.gsub(str, "’", "'")
    str = string.gsub(str, "&#39;", "'")
    str = string.gsub(str, "%%27", "'")
    return str
end

function p.getChaptersList()
    local listPage = mw.title.new('Template:BCIChapterList')
    if not listPage then
        return {}
    end

    local content = listPage:getContent()
    if not content then
        return {}
    end

    -- Normalize apostrophes in content
    content = p.normalizeApostrophes(content)

    local chapters = {}
    for seq, title, date, pagecount in string.gmatch(content, "{{BCIChapter|seq=(%d+)|title=([^|]+)|date=([^|]+)|pagecount=(%d+)}}") do
        table.insert(chapters, {seq = seq, title = title, date = date, pagecount = tonumber(pagecount)})
    end

    logTable(chapters, "Chapters list = ")
    return chapters
end

function p.getChapterDetails(chapterTitle, detail)
    local chapters = p.getChaptersList()
    local decodedTitle = p.urlDecode(chapterTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
    for _, chapter in ipairs(chapters) do
        if chapter.title == normalizedTitle then
            mw.log("Found detail for title:", normalizedTitle, detail, chapter[detail])
            return chapter[detail]
        end
    end
    mw.log("No detail found for title:", chapterTitle, "detail:", detail)
    return nil
end

function p.getChapterDetailsBySeq(seq, detail)
    local chapters = p.getChaptersList()
    for _, chapter in ipairs(chapters) do
        if tonumber(chapter.seq) == tonumber(seq) then
            mw.log("Found detail for seq:", seq, detail, chapter[detail])
            return chapter[detail]
        end
    end
    mw.log("No detail found for seq:", seq, "detail:", detail)
    return nil
end

function p.getNextChapter(chapterTitle)
    local chapters = p.getChaptersList()
    local decodedTitle = p.urlDecode(chapterTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
    for i, chapter in ipairs(chapters) do
        if chapter.title == normalizedTitle and i < #chapters then
            mw.log("Next chapter for title:", normalizedTitle, "is", chapters[i + 1].title)
            return chapters[i + 1].title
        end
    end
    mw.log("No next chapter found for title:", chapterTitle)
    return nil
end

function p.getNextChapterBySeq(seq)
    local chapters = p.getChaptersList()
    for i, chapter in ipairs(chapters) do
        if tonumber(chapter.seq) == tonumber(seq) and i < #chapters then
            mw.log("Next chapter for seq:", seq, "is", chapters[i + 1].title)
            return chapters[i + 1].title
        end
    end
    mw.log("No next chapter found for seq:", seq)
    return nil
end

function p.getPreviousChapter(chapterTitle)
    local chapters = p.getChaptersList()
    local decodedTitle = p.urlDecode(chapterTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
    for i, chapter in ipairs(chapters) do
        if chapter.title == normalizedTitle and i > 1 then
            mw.log("Previous chapter for title:", normalizedTitle, "is", chapters[i - 1].title)
            return chapters[i - 1].title
        end
    end
    mw.log("No previous chapter found for title:", chapterTitle)
    return nil
end

function p.getPreviousChapterBySeq(seq)
    local chapters = p.getChaptersList()
    for i, chapter in ipairs(chapters) do
        if tonumber(chapter.seq) == tonumber(seq) and i > 1 then
            mw.log("Previous chapter for seq:", seq, "is", chapters[i - 1].title)
            return chapters[i - 1].title
        end
    end
    mw.log("No previous chapter found for seq:", seq)
    return nil
end

function p.formatDate(date, frame)
    local year, month, day = string.match(date, "(%d+)%-(%d+)%-(%d+)")
    if year and month and day then
        return frame:expandTemplate{title = "Start date", args = {year, month, day}}
    else
        return date
    end
end

function p.infoboxBCIChapter(frame)
    local chapterTitle = frame.args.chapter_title or mw.title.getCurrentTitle().text
    local decodedTitle = p.urlDecode(chapterTitle)
    local normalizedTitle = p.normalizeApostrophes(decodedTitle)
    local seq = frame.args.seq
    local image = frame.args.image
    local caption = frame.args.caption
    local major_characters = frame.args.major_characters
    local minor_characters = frame.args.minor_characters
    local locations = frame.args.locations

    caption = caption and caption ~= "" and caption or nil

    local date, pagecount, next_chapter, prev_chapter

    if seq and special_cases[normalizedTitle] then
        date = p.getChapterDetailsBySeq(seq, "date") or "N/A"
        pagecount = p.getChapterDetailsBySeq(seq, "pagecount") or "N/A"
        next_chapter = p.getNextChapterBySeq(seq) or "N/A"
        prev_chapter = p.getPreviousChapterBySeq(seq) or "N/A"
    else
        date = p.getChapterDetails(normalizedTitle, "date") or "N/A"
        pagecount = p.getChapterDetails(normalizedTitle, "pagecount") or "N/A"
        next_chapter = p.getNextChapter(normalizedTitle) or "N/A"
        prev_chapter = p.getPreviousChapter(normalizedTitle) or "N/A"
    end

    mw.log("Infobox data for regular chapter =", {
        chapterTitle = chapterTitle,
        date = date,
        pagecount = pagecount,
        next_chapter = next_chapter,
        prev_chapter = prev_chapter
    })

    local infobox = mw.html.create('table')
    infobox:addClass('infobox')

    -- Add title as heading
    infobox:tag('tr')
        :tag('th')
            :attr('colspan', '2')
            :css('text-align', 'center')
            :css('font-size', '125%')
            :css('padding', '5px')
            :wikitext(chapterTitle)
            :done()
        :done()

    -- Optionally add image with standard size
    if image and image ~= "" then
        infobox:tag('tr')
            :tag('td')
                :attr('colspan', '2')
                :css('text-align', 'center')
                :wikitext('[[File:' .. image .. '|250px]]')
                :done()
            :done()

        -- Add caption below image if provided
        if caption then
            infobox:tag('tr')
                :tag('td')
                    :attr('colspan', '2')
                    :css('text-align', 'center')
                    :css('font-size', '90%')
                    :css('padding', '5px')
                    :wikitext(caption)
                    :done()
                :done()
        end
    end

    infobox:tag('tr')
        :tag('th'):wikitext('Published'):done()
        :tag('td'):wikitext(p.formatDate(date, frame)):done()
        :done()

    infobox:tag('tr')
        :tag('th'):wikitext('Page count'):done()
        :tag('td'):wikitext(pagecount):done()
        :done()

    -- Add additional manual entry parameters
    if major_characters and major_characters ~= "" then
        infobox:tag('tr')
            :tag('th'):wikitext('Major characters'):done()
            :tag('td'):wikitext(major_characters):done()
            :done()
    end

    if minor_characters and minor_characters ~= "" then
        infobox:tag('tr')
            :tag('th'):wikitext('Minor characters'):done()
            :tag('td'):wikitext(minor_characters):done()
            :done()
    end

    if locations and locations ~= "" then
        infobox:tag('tr')
            :tag('th'):wikitext('Locations'):done()
            :tag('td'):wikitext(locations):done()
            :done()
    end

    -- Add chapter navigation heading
    infobox:tag('tr')
        :tag('th')
            :attr('colspan', '2')
            :css('text-align', 'center')
            :css('font-size', '110%')
            :css('padding', '5px')
            :css('background-color', '#f0f0f0')
            :wikitext('Chapter navigation')
            :done()
        :done()

    -- Add chapter navigation links in separate columns
    local nav_row = infobox:tag('tr')
    nav_row:tag('td')
        :css('text-align', 'left')
        :wikitext(prev_chapter ~= "N/A" and string.format("[[%s:%s|← %s]]", mw.title.getCurrentTitle().nsText, prev_chapter, prev_chapter) or "← Previous")
        :done()
    nav_row:tag('td')
        :css('text-align', 'right')
        :wikitext(next_chapter ~= "N/A" and string.format("[[%s:%s|%s →]]", mw.title.getCurrentTitle().nsText, next_chapter, next_chapter) or "Next →")
        :done()
    nav_row:done()

    -- Add link to read chapter in BCI Members' Library
    infobox:tag('tr')
        :tag('td')
            :attr('colspan', '2')
            :css('text-align', 'center')
            :wikitext("[https://bittersweetcandybowl.com/members/ Read in the BCI Members' Library]")
            :done()
        :done()

    -- Return the complete wikitext
    return frame:preprocess(tostring(infobox))
end

return p