Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Rankings: Difference between revisions

From eSportsAmaze
No edit summary
No edit summary
Line 113: Line 113:
             local endTs = getTimestamps(r.end_date)
             local endTs = getTimestamps(r.end_date)
             if p and p ~= "" and endTs > 0 and endTs <= targetTs then
             if p and p ~= "" and endTs > 0 and endTs <= targetTs then
                 if not pData[p] then pData[p] = { name = p, player = p, team = r.team, latest_tourney = r.tournament, points = 0, events = 0, total_finishes = 0 }  
                 if not pData[p] then pData[p] = { name = p, player = p, team = r.team, latest_tourney = r.tournament, points = 0, events = 0, total_finishes = 0, stars = "" }  
                 else
                 else
                     if (not pData[p].team or pData[p].team == "") and r.team and r.team ~= "" then pData[p].team = r.team end
                     if (not pData[p].team or pData[p].team == "") and r.team and r.team ~= "" then pData[p].team = r.team end
Line 128: Line 128:
                 local base = rawFinishes * mult
                 local base = rawFinishes * mult
                  
                  
                 if tonumber(r.mvp_tourney) == 1 then base = base + 20 end
                 if tonumber(r.mvp_tourney) == 1 then base = base + 20; pData[p].stars = pData[p].stars .. "<span title='Event MVP'>⭐</span>" end
                 if tonumber(r.mvp_finals) == 1 then base = base + 10 end
                 if tonumber(r.mvp_finals) == 1 then base = base + 10; pData[p].stars = pData[p].stars .. "<span title='Finals MVP'>🌟</span>" end
                 if tonumber(r.igl) == 1 then base = base + 10 end
                 if tonumber(r.igl) == 1 then base = base + 10; pData[p].stars = pData[p].stars .. "<span title='IGL'>🧠</span>" end
                 if tonumber(r.survivor) == 1 then base = base + 10 end
                 if tonumber(r.survivor) == 1 then base = base + 10 end
                 if tonumber(r.emerging) == 1 then base = base + 5 end
                 if tonumber(r.emerging) == 1 then base = base + 5 end
Line 163: Line 163:
     local statType = (args.type or "team"):lower()
     local statType = (args.type or "team"):lower()
     local limit = tonumber(args.limit) or 100
     local limit = tonumber(args.limit) or 100
     local isCompact = args.compact == "true"
   
    -- Improved compact check
     local isCompact = false
    if args.compact == "true" or args.compact == "1" or args.compact == "yes" then isCompact = true end
      
      
     local list = p.getRankings(statType, args.as_of_date)
     local list = p.getRankings(statType, args.as_of_date)
Line 189: Line 192:
      
      
     if statType == "team" then
     if statType == "team" then
         th:tag('th'):addClass('sticky-col sticky-1'):wikitext('Rank')
         th:tag('th'):addClass('sticky-col sticky-1'):wikitext('#') -- CHANGED FROM 'Rank'
         th:tag('th'):addClass('sticky-col sticky-2 stat-team'):wikitext('Team')
         th:tag('th'):addClass('sticky-col sticky-2 stat-team'):wikitext('Team')
          
          
         if not isCompact then
         if not isCompact then
             th:tag('th'):addClass('stat-col'):wikitext('Events Counted')
             th:tag('th'):addClass('stat-col'):wikitext('Tournaments') -- CHANGED FROM 'Events Counted'
             th:tag('th'):addClass('stat-col'):wikitext('2024 Pts')
             th:tag('th'):addClass('stat-col'):wikitext('2024 Pts')
             th:tag('th'):addClass('stat-col'):wikitext('2025 Pts')
             th:tag('th'):addClass('stat-col'):wikitext('2025 Pts')
Line 208: Line 211:
             elseif i == 3 then tr:addClass('rk-bronze') end
             elseif i == 3 then tr:addClass('rk-bronze') end


             tr:tag('td'):addClass('sticky-col sticky-1 dyn-rank'):css('text-align','center'):css('font-weight','800')
             tr:tag('td'):addClass('sticky-col sticky-1 dyn-rank'):css('text-align','center'):css('font-weight','900'):wikitext(i)
              
              
             local tD = tourneyDb[d.latest_tourney:lower() .. "|" .. d.team:lower()]
             local tD = tourneyDb[d.latest_tourney:lower() .. "|" .. d.team:lower()]
Line 223: Line 226:
             end
             end
              
              
             tr:tag('td'):addClass('stat-col'):css('font-weight','800'):css('color','#00509d'):css('font-size','1.15em'):wikitext(string.format("%.2f", d.points))
             tr:tag('td'):addClass('stat-col'):tag('span'):addClass('rk-score'):wikitext(string.format("%.2f", d.points))
         end
         end


     elseif statType == "player" then
     elseif statType == "player" then
         th:tag('th'):addClass('sticky-col sticky-1'):wikitext('Rank')
         th:tag('th'):addClass('sticky-col sticky-1'):wikitext('#') -- CHANGED FROM 'Rank'
         th:tag('th'):addClass('sticky-col sticky-logo'):wikitext('Team')
         th:tag('th'):addClass('sticky-col sticky-logo'):wikitext('') -- CHANGED: Removed 'Team' text from header to save space
         th:tag('th'):addClass('sticky-col sticky-player stat-team'):wikitext('Player')
         th:tag('th'):addClass('sticky-col sticky-player stat-team'):wikitext('Player')
          
          
         if not isCompact then
         if not isCompact then
             th:tag('th'):addClass('stat-col'):wikitext('Events')
             th:tag('th'):addClass('stat-col'):wikitext('Tournaments') -- CHANGED FROM 'Events'
             th:tag('th'):addClass('stat-col'):wikitext('Finishes')
             th:tag('th'):addClass('stat-col'):wikitext('Finishes')
         end
         end
Line 245: Line 248:
             elseif i == 3 then tr:addClass('rk-bronze') end
             elseif i == 3 then tr:addClass('rk-bronze') end


             tr:tag('td'):addClass('sticky-col sticky-1 dyn-rank'):css('text-align','center'):css('font-weight','800')
             tr:tag('td'):addClass('sticky-col sticky-1 dyn-rank'):css('text-align','center'):css('font-weight','900'):wikitext(i)
              
              
             local logoCell = tr:tag('td'):addClass('sticky-col sticky-logo')
             local logoCell = tr:tag('td'):addClass('sticky-col sticky-logo')
Line 251: Line 254:
             local infoDiv = pCell:tag('div'):css('line-height','1.1')
             local infoDiv = pCell:tag('div'):css('line-height','1.1')
              
              
             infoDiv:tag('div'):addClass('rk-name-txt'):wikitext('[[' .. d.player .. ']]')
            local nameStr = '[[' .. d.player .. ']]'
            if d.stars ~= "" then nameStr = nameStr .. ' <span style="font-size:0.85em; margin-left:6px;">' .. d.stars .. '</span>' end
             infoDiv:tag('div'):addClass('rk-name-txt'):wikitext(nameStr)
              
              
             if d.team and d.team ~= "" then
             if d.team and d.team ~= "" then
Line 257: Line 262:
                 local mD = masterDb[d.team:lower()]
                 local mD = masterDb[d.team:lower()]
                 logoCell:wikitext(buildLogo(d.team, tD, mD))
                 logoCell:wikitext(buildLogo(d.team, tD, mD))
                 infoDiv:tag('div'):css('font-size','0.75em'):css('color','var(--text-muted)'):css('text-transform','uppercase'):css('font-weight','600'):css('margin-top','2px'):wikitext('[[' .. d.team .. ']]')
                 infoDiv:tag('div'):css('font-size','0.7em'):css('color','var(--text-muted)'):css('text-transform','uppercase'):css('font-weight','800'):css('margin-top','2px'):wikitext('[[' .. d.team .. ']]')
             else
             else
                 logoCell:wikitext(buildLogo("", nil, nil))
                 logoCell:wikitext(buildLogo("", nil, nil))
                 infoDiv:tag('div'):css('font-size','0.75em'):css('color','var(--text-muted)'):css('text-transform','uppercase'):css('font-weight','600'):css('opacity','0.5'):css('margin-top','2px'):wikitext('Free Agent')
                 infoDiv:tag('div'):css('font-size','0.7em'):css('color','var(--text-muted)'):css('text-transform','uppercase'):css('font-weight','800'):css('opacity','0.5'):css('margin-top','2px'):wikitext('Free Agent')
             end
             end
              
              
Line 267: Line 272:
                 tr:tag('td'):addClass('stat-col'):css('font-weight','700'):wikitext(d.total_finishes)
                 tr:tag('td'):addClass('stat-col'):css('font-weight','700'):wikitext(d.total_finishes)
             end
             end
             tr:tag('td'):addClass('stat-col'):css('font-weight','800'):css('color','#00509d'):css('font-size','1.15em'):wikitext(string.format("%.2f", d.points))
             tr:tag('td'):addClass('stat-col'):tag('span'):addClass('rk-score'):wikitext(string.format("%.2f", d.points))
         end
         end
     end
     end

Revision as of 18:16, 22 May 2026

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

local p = {}
local cargo = mw.ext.cargo
local html = mw.html

local function sqlEscape(s) if not s then return "" end return s:gsub("\\", "\\\\"):gsub("'", "\\'") end

local function getTimestamps(dateStr)
    if not dateStr or dateStr == "" then return 0 end
    local y, m, d = dateStr:match("(%d+)-(%d+)-(%d+)")
    if not y then return 0 end
    return os.time({year=y, month=m, day=d, hour=12, min=0, sec=0})
end

local function buildLogo(teamName, tData, mData)
    local lightFile = "Shield_team.png"
    local darkFile = "Shield_team_dark.png"

    if tData and tData.image and tData.image ~= "" then
        lightFile = tData.image; darkFile = (tData.image_dark and tData.image_dark ~= "") and tData.image_dark or lightFile
    elseif mData and mData.image and mData.image ~= "" then
        lightFile = mData.image; darkFile = (mData.image_dark and mData.image_dark ~= "") and mData.image_dark or lightFile
    end

    local container = html.create('span'):addClass('team-logo-wrapper')
    container:tag('span'):addClass('logo-lightmode'):wikitext('[[File:' .. lightFile .. '|24px|link=|class=team-logo]]')
    container:tag('span'):addClass('logo-darkmode'):wikitext('[[File:' .. darkFile .. '|24px|link=|class=team-logo]]')
    return tostring(container)
end

local function getTeamBasePts(tier, rank)
    local t = (tier or ""):lower()
    local col = 0
    if t:find("publisher") then col = 1
    elseif t:find("tier 1") then col = 2
    elseif t:find("tier 2") then col = 3
    elseif t:find("tier 3") then col = 4
    else return 0 end

    if rank == 1 then return ({1000, 800, 600, 400})[col]
    elseif rank == 2 then return ({800, 700, 500, 350})[col]
    elseif rank == 3 then return ({700, 600, 400, 300})[col]
    elseif rank == 4 then return ({600, 500, 300, 250})[col]
    elseif rank == 5 then return ({500, 400, 250, 200})[col]
    elseif rank >= 6 and rank <= 10 then return ({400, 300, 200, 150})[col]
    elseif rank >= 11 and rank <= 20 then return ({300, 200, 150, 100})[col]
    elseif rank >= 21 and rank <= 30 then return ({200, 100, 75, 50})[col]
    elseif rank >= 31 and rank <= 48 then return ({100, 50, 35, 25})[col]
    end return 0
end

local function getTeamDecay(days)
    if days <= 180 then return 1
    elseif days <= 270 then return 0.75
    elseif days <= 365 then return 0.5
    elseif days <= 1095 then return 0.1
    else return 0 end
end

local function getPlayerDecay(days)
    if days <= 180 then return 1
    elseif days <= 240 then return 0.75
    elseif days <= 300 then return 0.5
    elseif days <= 365 then return 0.25
    elseif days <= 1095 then return 0.1
    else return 0 end
end

function p.getRankings(statType, targetDateStr)
    local targetTs = os.time() 
    if targetDateStr and targetDateStr ~= "" then
        local targetVal = getTimestamps(targetDateStr)
        if targetVal > 0 then targetTs = targetVal end
    end
    
    statType = (statType or "team"):lower()
    local list = {}
    
    if statType == "team" then
        local results = cargo.query("RankingData_Team", "tournament, tier, end_date, team, rank", { orderBy = "end_date DESC", limit = 5000 })
        if not results then return {} end
        local tData = {}
        for _, r in ipairs(results) do
            local t = r.team
            local endTs = getTimestamps(r.end_date)
            if t and t ~= "" and endTs > 0 and endTs <= targetTs then
                if not tData[t] then tData[t] = { name = t, team = t, points = 0, events = 0, latest_tourney = r.tournament, p2024 = 0, p2025 = 0, p2026 = 0 } end
                
                local diff = os.difftime(targetTs, endTs)
                local days = math.max(0, math.floor(diff / 86400))
                
                local base = getTeamBasePts(r.tier, tonumber(r.rank) or 99)
                local finalPts = base * getTeamDecay(days)
                
                local year = r.end_date:match("^(%d+)")
                if year == "2024" then tData[t].p2024 = tData[t].p2024 + base
                elseif year == "2025" then tData[t].p2025 = tData[t].p2025 + base
                elseif year == "2026" then tData[t].p2026 = tData[t].p2026 + base end
                
                if finalPts > 0 then
                    tData[t].points = tData[t].points + finalPts
                    tData[t].events = tData[t].events + 1
                end
            end
        end
        for _, d in pairs(tData) do if d.points > 0 then table.insert(list, d) end end

    elseif statType == "player" then
        local results = cargo.query("RankingData_Player", "tournament, tier, end_date, player, team, finishes, mvp_tourney, igl, survivor, mvp_finals, emerging", { orderBy = "end_date DESC", limit = 5000 })
        if not results then return {} end
        local pData = {}
        for _, r in ipairs(results) do
            local p = r.player
            local endTs = getTimestamps(r.end_date)
            if p and p ~= "" and endTs > 0 and endTs <= targetTs then
                if not pData[p] then pData[p] = { name = p, player = p, team = r.team, latest_tourney = r.tournament, points = 0, events = 0, total_finishes = 0, stars = "" } 
                else
                    if (not pData[p].team or pData[p].team == "") and r.team and r.team ~= "" then pData[p].team = r.team end
                end
                
                local diff = os.difftime(targetTs, endTs)
                local days = math.max(0, math.floor(diff / 86400))
                
                local t = (r.tier or ""):lower()
                local mult = 1
                if t:find("publisher") then mult = 2 elseif t:find("tier 1") then mult = 1.5 end
                
                local rawFinishes = tonumber(r.finishes) or 0
                local base = rawFinishes * mult
                
                if tonumber(r.mvp_tourney) == 1 then base = base + 20; pData[p].stars = pData[p].stars .. "<span title='Event MVP'>⭐</span>" end
                if tonumber(r.mvp_finals) == 1 then base = base + 10; pData[p].stars = pData[p].stars .. "<span title='Finals MVP'>🌟</span>" end
                if tonumber(r.igl) == 1 then base = base + 10; pData[p].stars = pData[p].stars .. "<span title='IGL'>🧠</span>" end
                if tonumber(r.survivor) == 1 then base = base + 10 end
                if tonumber(r.emerging) == 1 then base = base + 5 end
                
                local finalPts = base * getPlayerDecay(days)
                if finalPts > 0 then
                    pData[p].total_finishes = pData[p].total_finishes + rawFinishes
                    pData[p].points = pData[p].points + finalPts
                    pData[p].events = pData[p].events + 1
                end
            end
        end
        for _, d in pairs(pData) do if d.points > 0 then table.insert(list, d) end end
    end
    
    table.sort(list, function(a, b) return a.points > b.points end)
    for i, d in ipairs(list) do d.rank = i end
    return list
end

function p.getRank(name, statType, targetDateStr)
    local list = p.getRankings(statType, targetDateStr)
    for _, d in ipairs(list) do
        if d.name:lower() == name:lower() then return d.rank, d.points end
    end
    return nil, nil
end

function p.main(frame)
    local args = frame:getParent().args
    if not args.type then args = frame.args end
    local statType = (args.type or "team"):lower()
    local limit = tonumber(args.limit) or 100
    
    -- Improved compact check
    local isCompact = false
    if args.compact == "true" or args.compact == "1" or args.compact == "yes" then isCompact = true end
    
    local list = p.getRankings(statType, args.as_of_date)
    if #list == 0 then return 'No data.' end

    local uniqueTeams = {}
    for _, d in ipairs(list) do
        if d.team and d.team ~= "" then uniqueTeams[d.team] = true end
    end
    
    local tourneyDb = {}; local masterDb = {}; local teamListQuoted = {}
    for t, _ in pairs(uniqueTeams) do table.insert(teamListQuoted, "'" .. sqlEscape(t) .. "'") end
    local teamSql = table.concat(teamListQuoted, ",")
    
    if teamSql ~= "" and cargo and cargo.query then
        local mResults = cargo.query("Teams", "name, image, image_dark", { where = "name IN (" .. teamSql .. ")", limit = 500 })
        if mResults then for _, row in ipairs(mResults) do masterDb[row.name:lower()] = row end end
        local tResults = cargo.query("Tournament_Teams", "tournament, team, image, image_dark", { where = "team IN (" .. teamSql .. ")", limit = 5000 })
        if tResults then for _, row in ipairs(tResults) do tourneyDb[row.tournament:lower() .. "|" .. row.team:lower()] = row end end
    end

    local root = html.create('div'):addClass('standings-wrapper')
    local tbl = root:tag('table'):addClass('standings-card-table stats-card-table premium-ranking-table')
    local th = tbl:tag('tr')
    
    if statType == "team" then
        th:tag('th'):addClass('sticky-col sticky-1'):wikitext('#') -- CHANGED FROM 'Rank'
        th:tag('th'):addClass('sticky-col sticky-2 stat-team'):wikitext('Team')
        
        if not isCompact then
            th:tag('th'):addClass('stat-col'):wikitext('Tournaments') -- CHANGED FROM 'Events Counted'
            th:tag('th'):addClass('stat-col'):wikitext('2024 Pts')
            th:tag('th'):addClass('stat-col'):wikitext('2025 Pts')
            th:tag('th'):addClass('stat-col'):wikitext('2026 Pts')
        end
        th:tag('th'):addClass('stat-col'):wikitext('Total Points')
        
        for i = 1, math.min(#list, limit) do
            local d = list[i]
            local tr = tbl:tag('tr'):addClass('standings-row')
            
            if i == 1 then tr:addClass('rk-gold')
            elseif i == 2 then tr:addClass('rk-silver')
            elseif i == 3 then tr:addClass('rk-bronze') end

            tr:tag('td'):addClass('sticky-col sticky-1 dyn-rank'):css('text-align','center'):css('font-weight','900'):wikitext(i)
            
            local tD = tourneyDb[d.latest_tourney:lower() .. "|" .. d.team:lower()]
            local mD = masterDb[d.team:lower()]
            local teamCell = tr:tag('td'):addClass('sticky-col sticky-2 stat-team team-name-cell')
            teamCell:wikitext(buildLogo(d.team, tD, mD))
            teamCell:tag('span'):addClass('rk-name-txt'):wikitext('[[' .. d.team .. ']]')
            
            if not isCompact then
                tr:tag('td'):addClass('stat-col'):css('color','var(--text-muted)'):css('font-weight','600'):wikitext(d.events)
                tr:tag('td'):addClass('stat-col'):css('color','var(--text-muted)'):css('font-size','0.9em'):wikitext(d.p2024)
                tr:tag('td'):addClass('stat-col'):css('color','var(--text-muted)'):css('font-size','0.9em'):wikitext(d.p2025)
                tr:tag('td'):addClass('stat-col'):css('color','var(--text-muted)'):css('font-size','0.9em'):wikitext(d.p2026)
            end
            
            tr:tag('td'):addClass('stat-col'):tag('span'):addClass('rk-score'):wikitext(string.format("%.2f", d.points))
        end

    elseif statType == "player" then
        th:tag('th'):addClass('sticky-col sticky-1'):wikitext('#') -- CHANGED FROM 'Rank'
        th:tag('th'):addClass('sticky-col sticky-logo'):wikitext('') -- CHANGED: Removed 'Team' text from header to save space
        th:tag('th'):addClass('sticky-col sticky-player stat-team'):wikitext('Player')
        
        if not isCompact then
            th:tag('th'):addClass('stat-col'):wikitext('Tournaments') -- CHANGED FROM 'Events'
            th:tag('th'):addClass('stat-col'):wikitext('Finishes')
        end
        th:tag('th'):addClass('stat-col'):wikitext('Total Points')
        
        for i = 1, math.min(#list, limit) do
            local d = list[i]
            local tr = tbl:tag('tr'):addClass('standings-row')
            
            if i == 1 then tr:addClass('rk-gold')
            elseif i == 2 then tr:addClass('rk-silver')
            elseif i == 3 then tr:addClass('rk-bronze') end

            tr:tag('td'):addClass('sticky-col sticky-1 dyn-rank'):css('text-align','center'):css('font-weight','900'):wikitext(i)
            
            local logoCell = tr:tag('td'):addClass('sticky-col sticky-logo')
            local pCell = tr:tag('td'):addClass('sticky-col sticky-player stat-team team-name-cell')
            local infoDiv = pCell:tag('div'):css('line-height','1.1')
            
            local nameStr = '[[' .. d.player .. ']]'
            if d.stars ~= "" then nameStr = nameStr .. ' <span style="font-size:0.85em; margin-left:6px;">' .. d.stars .. '</span>' end
            infoDiv:tag('div'):addClass('rk-name-txt'):wikitext(nameStr)
            
            if d.team and d.team ~= "" then
                local tD = tourneyDb[d.latest_tourney:lower() .. "|" .. d.team:lower()]
                local mD = masterDb[d.team:lower()]
                logoCell:wikitext(buildLogo(d.team, tD, mD))
                infoDiv:tag('div'):css('font-size','0.7em'):css('color','var(--text-muted)'):css('text-transform','uppercase'):css('font-weight','800'):css('margin-top','2px'):wikitext('[[' .. d.team .. ']]')
            else
                logoCell:wikitext(buildLogo("", nil, nil))
                infoDiv:tag('div'):css('font-size','0.7em'):css('color','var(--text-muted)'):css('text-transform','uppercase'):css('font-weight','800'):css('opacity','0.5'):css('margin-top','2px'):wikitext('Free Agent')
            end
            
            if not isCompact then
                tr:tag('td'):addClass('stat-col'):css('color','var(--text-muted)'):css('font-weight','600'):wikitext(d.events)
                tr:tag('td'):addClass('stat-col'):css('font-weight','700'):wikitext(d.total_finishes)
            end
            tr:tag('td'):addClass('stat-col'):tag('span'):addClass('rk-score'):wikitext(string.format("%.2f", d.points))
        end
    end
    return tostring(root)
end

return p