Module:Timeline

From Cyraxx Wiki - lolcow.city
Jump to navigation Jump to search

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

local p = {}

function p.generateTable(frame)
    -- Define the page where the events are stored
    local eventsPage = mw.title.new('GlobalEvents')
    -- Get the content of the events page
    local eventsContent = eventsPage and eventsPage:getContent() or ''
    local events = {}
    local unknownEvents = {}
    local sources = {}
    local sourceCounter = 1

    if eventsContent and eventsContent ~= '' then
        -- Split the content into individual event blocks
        for eventBlock in mw.text.gsplit(eventsContent, '{{EventRow', true) do
            if eventBlock and eventBlock:find('|%s*date%s*=') then
                local event = {}
                -- Extract the date from the event block
                event.date = mw.text.trim(eventBlock:match('|%s*date%s*=%s*(%d%d%d%d%-%d%d%-%d%d)') or eventBlock:match('|%s*date%s*=%s*(%d%d%d%d%-%d%d)') or eventBlock:match('|%s*date%s*=%s*(%d%d%d%d)') or eventBlock:match('|%s*date%s*=%s*(unknown)') or '')
                -- Extract the title from the event block, handling newlines correctly
                event.title = eventBlock:match('|%s*title%s*=%s*(.-)%s*\n') or ''
                -- Extract the description from the event block, handling newlines correctly
                event.description = eventBlock:match('|%s*description%s*=%s*(.-)%s*}}') or ''

                -- Function to clean standalone | characters but keep them inside links
                local function cleanText(text)
                    local cleanedText = ""
                    local inLink = false
                    for i = 1, #text do
                        local char = text:sub(i, i)
                        if char == "[" and text:sub(i, i+1) == "[[" then
                            inLink = true
                        elseif char == "]" and text:sub(i-1, i) == "]]" then
                            inLink = false
                        end

                        if char == "|" and not inLink then
                            -- skip standalone |
                        else
                            cleanedText = cleanedText .. char
                        end
                    end
                    return cleanedText
                end

                event.title = cleanText(event.title)
                event.description = cleanText(event.description)

                if event.date ~= '' then
                    if event.date == 'unknown' then
                        table.insert(unknownEvents, event)
                    else
                        table.insert(events, event)
                    end
                end
            end
        end

        -- Function to process links
        local function processLinks(text)
            return mw.ustring.gsub(text, "(https?://[%w%-_.~!*'();:@&=+$,/?%[%]#]+)", function(url)
                local sourceTag = "[" .. sourceCounter .. "]"
                table.insert(sources, "* " .. url)
                sourceCounter = sourceCounter + 1
                return url
            end):gsub("%[(https?://[%w%-_.~!*'();:@&=+$,/?%[%]#]+)%]", function(url)
                local sourceTag = "[" .. sourceCounter .. "]"
                table.insert(sources, "* " .. url)
                sourceCounter = sourceCounter + 1
                return "[" .. url .. "] " .. sourceTag
            end):gsub("%[(https?://[%w%-_.~!*'();:@&=+$,/?%[%]#]+)|([^\]]+)%]", function(url, title)
                local sourceTag = "[" .. sourceCounter .. "]"
                table.insert(sources, "* " .. url)
                sourceCounter = sourceCounter + 1
                return "[" .. url .. "|" .. title .. "] " .. sourceTag
            end)
        end

        -- Sort the events by date in descending order, placing year-only dates before specific dates within the same year
        table.sort(events, function(a, b)
            local function parseDate(date)
                local year, month, day = date:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)$")
                if not year then
                    year, month = date:match("^(%d%d%d%d)%-(%d%d)$")
                    day = "00"
                end
                if not year then
                    year = date:match("^(%d%d%d%d)$")
                    month = "00"
                    day = "00"
                end
                return year, month, day
            end

            local a_year, a_month, a_day = parseDate(a.date)
            local b_year, b_month, b_day = parseDate(b.date)

            if a_year ~= b_year then
                return a_year > b_year
            elseif a_month ~= b_month then
                if a_month == "00" or b_month == "00" then
                    return a_month < b_month
                else
                    return a_month > b_month
                end
            else
                return a_day > b_day
            end
        end)

        -- Function to create a table from events
        local function createTable(events)
            local result = '{| class="wikitable" style="width: 100%; overflow-x: auto;"\n'
            result = result .. '! style="width: 120px;" | Date !! News\n'
            for _, event in ipairs(events) do
                local date = mw.text.nowiki(event.date or '')
                local title = processLinks(frame:preprocess(event.title) or '')
                local description = processLinks(frame:preprocess(event.description) or '')

                -- Fix link encoding
                title = title:gsub("%%7C", "|")
                description = description:gsub("%%7C", "|")

                result = result .. '|-\n'
                result = result .. '| ' .. date .. ' || ' .. title .. '<br>' .. description .. '\n'
            end
            result = result .. '|}\n'
            return result
        end

        -- Group events by year and decade
        local groupedEvents = {future = {count = 0, events = {}}}
        local currentYear = tonumber(os.date("%Y"))
        for _, event in ipairs(events) do
            local year = tonumber(event.date:sub(1, 4))
            local month = tonumber(event.date:sub(6, 7))
            local decade = math.floor(year / 10) * 10

            if year > currentYear then
                table.insert(groupedEvents.future.events, event)
                groupedEvents.future.count = groupedEvents.future.count + 1
            else
                if not groupedEvents[decade] then
                    groupedEvents[decade] = {years = {}, count = 0, events = {}}
                end

                if not groupedEvents[decade].years[year] then
                    groupedEvents[decade].years[year] = {months = {}, count = 0, events = {}}
                end

                if not month then
                    month = 0 -- Assign 0 for "Exact Date Unknown"
                end

                if not groupedEvents[decade].years[year].months[month] then
                    groupedEvents[decade].years[year].months[month] = {count = 0, events = {}}
                end

                table.insert(groupedEvents[decade].years[year].events, event)
                groupedEvents[decade].years[year].count = groupedEvents[decade].years[year].count + 1
                table.insert(groupedEvents[decade].years[year].months[month].events, event)
                groupedEvents[decade].years[year].months[month].count = groupedEvents[decade].years[year].months[month].count + 1
                table.insert(groupedEvents[decade].events, event)
                groupedEvents[decade].count = groupedEvents[decade].count + 1
            end
        end

        -- Sort decades from newest to oldest
        local sortedDecades = {}
        for decade, _ in pairs(groupedEvents) do
            if decade ~= "future" then
                table.insert(sortedDecades, tonumber(decade))
            end
        end
        table.sort(sortedDecades, function(a, b) return a > b end)

        -- Build the result table
        local result = ""
        if groupedEvents.future.count > 0 then
            result = result .. "\n= Future =\n"
            result = result .. createTable(groupedEvents.future.events)
        end

        local oldestDecade = sortedDecades[#sortedDecades]
        for i, decade in ipairs(sortedDecades) do
            if decade ~= "future" then
                local decadeData = groupedEvents[decade]

                -- Sort years from newest to oldest
                local sortedYears = {}
                for year, _ in pairs(decadeData.years) do
                    table.insert(sortedYears, tonumber(year))
                end
                table.sort(sortedYears, function(a, b) return a > b end)

                -- Adjust the title of the current decade
                local endYear = (decade == math.floor(currentYear / 10) * 10) and currentYear or (decade + 9)

                if decadeData.count <= 3 and decade ~= oldestDecade then
                    result = result .. "\n= " .. decade .. " - " .. endYear .. " =\n"
                    result = result .. createTable(decadeData.events)
                elseif decadeData.count > 3 then
                    result = result .. "\n= " .. decade .. " - " .. endYear .. " =\n"
                    for _, year in ipairs(sortedYears) do
                        local yearData = decadeData.years[year]

                        result = result .. "\n== " .. year .. " ==\n"
                        if yearData.count > 3 then
                            -- Sort months from newest to oldest, placing "Exact Date Unknown" (month 0) after the highest month
                            local sortedMonths = {}
                            for month, _ in pairs(yearData.months) do
                                table.insert(sortedMonths, tonumber(month))
                            end
                            table.sort(sortedMonths, function(a, b)
                                if a == 0 then
                                    return false
                                elseif b == 0 then
                                    return true
                                else
                                    return a > b
                                end
                            end)

                            for _, month in ipairs(sortedMonths) do
                                local monthData = yearData.months[month]
                                if month == 0 then
                                    result = result .. "\n=== Exact Date Unknown (" .. year .. ") ===\n"
                                else
                                    result = result .. "\n=== " .. os.date("%B %Y", os.time{year=year, month=month, day=1}) .. " ===\n"
                                end
                                result = result .. createTable(monthData.events)
                            end
                        else
                            result = result .. createTable(yearData.events)
                        end
                    end
                else
                    result = result .. "\n= Before " .. (decade + 10) .. " =\n"
                    result = result .. createTable(decadeData.events)
                end
            end
        end

        -- Add unknown events section
        if #unknownEvents > 0 then
            result = result .. "\n= Date Unknown =\n"
            result = result .. createTable(unknownEvents)
        end

        -- Add sources section
        if #sources > 0 then
            result = result .. "\n= Sources =\n"
            for _, source in ipairs(sources) do
                result = result .. source .. "\n"
            end
        end

        return result
    else
        return "No events found in GlobalEvents page."
    end
end

return p