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

Module:Statistics: Difference between revisions

From eSportsAmaze
No edit summary
No edit summary
Line 4: Line 4:
local text = mw.text
local text = mw.text


-- ============================================================
-- [CONFIGURATION SECTION SAME AS BEFORE]
-- CONFIGURATION
-- ============================================================
-- Heatmap Color (Blue: 59, 130, 246)
local HEATMAP_R, HEATMAP_G, HEATMAP_B = 59, 130, 246  
local HEATMAP_R, HEATMAP_G, HEATMAP_B = 59, 130, 246  
local HEADERS = {
local HEADERS = {
    -- BASICS
     rank = "#", team = "Team", player = "Player", matches_played = "Matches",
     rank = "#",
     finishes = "Finishes", fpm = "FPM", knocks = "Knocks", damage = "Damage",
    team = "Team",
     headshots = "Headshots", longest = "Longest", assists = "Assists",
    player = "Player",
     grenade_kills = "Grenade Kills", vehicle_kills = "Vehicle Kills", contribution = "Contrib %",  
    matches_played = "Matches",
     survival = "Surv. Time", healings = "Heals", revives = "Revives", damage_taken = "Dmg Recv",
   
     grenades_used = "Nades Used", smokes_used = "Smokes Used", utility_used = "Util Used",
    -- KILLS / OFFENSE
     dist_drive = "Drive Dist", dist_walk = "Walk Dist", dist_total = "Total Dist",
     finishes = "Finishes",
     bluezone = "Bluezone Time", air_drops = "Air Drops",
    fpm = "FPM",  
     total_pts = "Total Pts", place_pts = "Place Pts", elims = "Elims",
    knocks = "Knocks",
     avg_place = "Avg Place", avg_place_pts = "Avg Place Pts", avg_elims = "Avg Elims", avg_total = "Avg Pts",
    damage = "Damage",
     wwcd = "🥇", place_2 = "🥈", place_3 = "🥉", top_5 = "Top 5", top_8 = "Top 8", place_low = "> 8th",
     headshots = "Headshots",
     g_0 = "0", g_1_5 = "1–5", g_6_10 = "6–10", g_11_15 = "11–15", g_16_20 = "16–20", g_20_plus = "20+"
    longest = "Longest",  
    assists = "Assists",
     grenade_kills = "Grenade Kills",
    vehicle_kills = "Vehicle Kills",
    contribution = "Contrib %",  
   
    -- SURVIVAL / SUPPORT
     survival = "Surv. Time",
    healings = "Heals",
    revives = "Revives",  
    damage_taken = "Dmg Recv",
   
    -- UTILITY
     grenades_used = "Nades Used",
    smokes_used = "Smokes Used",
    utility_used = "Util Used",
   
    -- MOVEMENT
     dist_drive = "Drive Dist",
    dist_walk = "Walk Dist",
    dist_total = "Total Dist",
     bluezone = "Bluezone Time",
    air_drops = "Air Drops",
   
    -- TEAM SPECIFIC
     total_pts = "Total Pts",
    place_pts = "Place Pts",
    elims = "Elims",
   
    -- AVERAGES (Team)
     avg_place = "Avg Place",
    avg_place_pts = "Avg Place Pts",
    avg_elims = "Avg Elims",
    avg_total = "Avg Pts",
   
    -- PLACEMENT COUNTS
     wwcd = "🥇",  
    place_2 = "🥈",  
    place_3 = "🥉",  
    top_5 = "Top 5",
    top_8 = "Top 8",
    place_low = "> 8th",
   
    -- POINT BUCKETS
     g_0 = "0",
    g_1_5 = "1–5",
    g_6_10 = "6–10",
    g_11_15 = "11–15",
    g_16_20 = "16–20",
    g_20_plus = "20+"
}
}
-- Columns that should NOT get a heatmap background
local NO_HEATMAP = { rank=true, team=true, player=true, matches_played=true }
local NO_HEATMAP = { rank=true, team=true, player=true, matches_played=true }


-- ============================================================
-- [HELPER FUNCTIONS SAME AS BEFORE: getHeatmapStyle, getTeamLogo]
-- HELPER: Heatmap Style
-- ============================================================
local function getHeatmapStyle(value, maxVal)
local function getHeatmapStyle(value, maxVal)
     if not tonumber(value) or not tonumber(maxVal) or maxVal == 0 then return "" end
     if not tonumber(value) or not tonumber(maxVal) or maxVal == 0 then return "" end
Line 89: Line 31:
end
end


-- ============================================================
-- HELPER: Get Team Logo
-- ============================================================
local function getTeamLogo(teamName)
local function getTeamLogo(teamName)
     if not teamName then return "" end
     if not teamName then return "" end
Line 97: Line 36:
     local lightFile = cleanName .. '.png'
     local lightFile = cleanName .. '.png'
     local darkFile = cleanName .. '_dark.png'
     local darkFile = cleanName .. '_dark.png'
   
     local hasLight = mw.title.new('File:' .. lightFile).exists
     local hasLight = mw.title.new('File:' .. lightFile).exists
     local hasDark = mw.title.new('File:' .. darkFile).exists
     local hasDark = mw.title.new('File:' .. darkFile).exists
   
     local html = ""
     local html = ""
    -- Light Mode
     if hasLight then html = html .. '[[File:' .. lightFile .. '|25px|link=' .. teamName .. '|class=logo-lightmode]]'
     if hasLight then
     else html = html .. '[[File:Shield_team.png|25px|link=' .. teamName .. '|class=logo-lightmode]]' end
        html = html .. '[[File:' .. lightFile .. '|25px|link=' .. teamName .. '|class=logo-lightmode]]'
     if hasDark then html = html .. '[[File:' .. darkFile .. '|25px|link=' .. teamName .. '|class=logo-darkmode]]'
     else
     elseif hasLight then html = html .. '[[File:' .. lightFile .. '|25px|link=' .. teamName .. '|class=logo-darkmode]]'
        html = html .. '[[File:Shield_team.png|25px|link=' .. teamName .. '|class=logo-lightmode]]'
     else html = html .. '[[File:Shield_team_dark.png|25px|link=' .. teamName .. '|class=logo-darkmode]]' end
    end
    -- Dark Mode
     if hasDark then
        html = html .. '[[File:' .. darkFile .. '|25px|link=' .. teamName .. '|class=logo-darkmode]]'
     elseif hasLight then
        html = html .. '[[File:' .. lightFile .. '|25px|link=' .. teamName .. '|class=logo-darkmode]]'
     else
        html = html .. '[[File:Shield_team_dark.png|25px|link=' .. teamName .. '|class=logo-darkmode]]'
    end
     return html .. " "
     return html .. " "
end
end


-- ============================================================
-- ============================================================
-- MAIN GENERATOR
-- MAIN GENERATOR (UPDATED FOR STAGE/GROUP)
-- ============================================================
-- ============================================================
function p.main(frame)
function p.main(frame)
     local args = frame:getParent().args
     local args = frame:getParent().args
     local type = args.type or "player"
     local type = args.type or "player"
     local tournament = args.tournament or mw.title.getCurrentTitle().subpageText
     local tournament = args.tournament or mw.title.getCurrentTitle().text
   
    -- Clean up tournament name (remove subpages if BASEPAGENAME wasn't used)
    if tournament:find('/') then
        tournament = mw.text.split(tournament, '/')[1]
    end
 
     local map = args.map or "All"
     local map = args.map or "All"
    local stage = args.stage
    local group = args.group
      
      
     -- 1. Determine Columns to Show
     -- 1. Determine Columns
     local colsInput = args.columns or ""
     local colsInput = args.columns or ""
     local colKeys = {}
     local colKeys = {}
   
     if colsInput == "" then
     if colsInput == "" then
         if type == "player" then  
         if type == "player" then colKeys = {"rank", "player", "team", "matches_played", "finishes", "damage"}
            colKeys = {"rank", "player", "team", "matches_played", "finishes", "damage"}
         else colKeys = {"rank", "team", "matches_played", "total_pts", "wwcd"} end
         else  
            colKeys = {"rank", "team", "matches_played", "total_pts", "wwcd"}
        end
     else
     else
         colKeys = text.split(colsInput, ",")
         colKeys = text.split(colsInput, ",")
     end
     end
      
      
     -- 2. Build Query Fields (Exclude 'rank' from SQL)
     -- 2. Build Query Fields (Exclude 'rank')
     local queryFields = {}
     local queryFields = {}
     for _, k in ipairs(colKeys) do
     for _, k in ipairs(colKeys) do
         local cleanK = k:match("^%s*(.-)%s*$")
         local cleanK = k:match("^%s*(.-)%s*$")
         if cleanK ~= "rank" then -- Don't ask DB for 'rank'
         if cleanK ~= "rank" then table.insert(queryFields, cleanK) end
            table.insert(queryFields, cleanK)
    end
         end
   
    -- 3. Build WHERE Clause dynamically
    local whereParts = {}
    table.insert(whereParts, string.format("tournament='%s'", tournament:gsub("'", "\\'")))
   
    if map ~= "Any" then -- Use 'Any' to show ALL maps mixed, otherwise filter
        table.insert(whereParts, string.format("map='%s'", map:gsub("'", "\\'")))
    end
   
    if stage and stage ~= "" then
         table.insert(whereParts, string.format("stage='%s'", stage:gsub("'", "\\'")))
    end
   
    if group and group ~= "" then
        table.insert(whereParts, string.format("groupname='%s'", group:gsub("'", "\\'")))
     end
     end
      
      
    local where = table.concat(whereParts, " AND ")
   
    -- 4. Fetch Data
     local table_name = (type == "player") and "Player_Stats" or "Team_Stats"
     local table_name = (type == "player") and "Player_Stats" or "Team_Stats"
     local fieldsSQL = table.concat(queryFields, ",")
     local results = cargo.query(table_name, table.concat(queryFields, ",") .. ", team", {
    local where = string.format("tournament='%s' AND map='%s'", tournament:gsub("'", "\\'"), map)
   
    -- 3. Fetch Data
    local results = cargo.query(table_name, fieldsSQL .. ", team", {
         where = where,
         where = where,
         orderBy = (type == "player" and "finishes DESC" or "total_pts DESC"),
         orderBy = (type == "player" and "finishes DESC" or "total_pts DESC"),
Line 163: Line 108:
      
      
     if not results or #results == 0 then
     if not results or #results == 0 then
         return '<div style="padding:20px; color:#666;">No statistics available for ' .. map .. '.</div>'
         return '<div style="padding:20px; color:var(--text-muted); font-style:italic;">No statistics found for these filters.</div>'
     end
     end
      
      
     -- 4. Find Max Values
     -- 5. Find Max Values
     local maxValues = {}
     local maxValues = {}
     for _, row in ipairs(results) do
     for _, row in ipairs(results) do
Line 175: Line 120:
     end
     end
      
      
     -- 5. Build Table
     -- 6. Build Table
     local root = html.create('div'):addClass('stats-table-wrapper')
     local root = html.create('div'):addClass('stats-table-wrapper')
     local tbl = root:tag('table'):addClass('wikitable flat-table sortable stats-table')
     local tbl = root:tag('table'):addClass('wikitable flat-table sortable stats-table')
      
      
     -- Header Row
     -- Header
     local trHead = tbl:tag('tr')
     local trHead = tbl:tag('tr')
     for _, key in ipairs(colKeys) do
     for _, key in ipairs(colKeys) do
Line 186: Line 131:
     end
     end
      
      
     -- Data Rows
     -- Rows
     for i, row in ipairs(results) do
     for i, row in ipairs(results) do
         local tr = tbl:tag('tr')
         local tr = tbl:tag('tr')
       
         for _, key in ipairs(colKeys) do
         for _, key in ipairs(colKeys) do
             local cleanKey = key:match("^%s*(.-)%s*$")
             local cleanKey = key:match("^%s*(.-)%s*$")
Line 195: Line 139:
              
              
             if cleanKey == "rank" then
             if cleanKey == "rank" then
                -- Auto-generate Rank (1, 2, 3...)
                 cell:wikitext(i .. '.'):css("font-weight", "bold"):css("text-align", "center")
                 cell:wikitext(i .. '.')  
                cell:css("font-weight", "bold")
                cell:css("text-align", "center")
               
             elseif cleanKey == "team" then
             elseif cleanKey == "team" then
                 local teamName = row.team or ""
                 local teamName = row.team or ""
                 cell:wikitext(getTeamLogo(teamName) .. '[[' .. teamName .. ']]')
                 cell:wikitext(getTeamLogo(teamName) .. '[[' .. teamName .. ']]'):css("text-align", "left"):css("white-space", "nowrap")
                cell:css("text-align", "left")
                cell:css("white-space", "nowrap")
               
             elseif cleanKey == "player" then
             elseif cleanKey == "player" then
                 cell:wikitext('[[' .. (row.player or "") .. ']]')
                 cell:wikitext('[[' .. (row.player or "") .. ']]'):css("text-align", "left"):css("font-weight", "bold")
                cell:css("text-align", "left")
                cell:css("font-weight", "bold")
               
             else
             else
                -- Numeric Data
                 local rawVal = row[cleanKey]
                 local rawVal = row[cleanKey]
                 cell:wikitext(rawVal)
                 cell:wikitext(rawVal):css("text-align", "center")
                cell:css("text-align", "center")
               
                -- Heatmap
                 if not NO_HEATMAP[cleanKey] then
                 if not NO_HEATMAP[cleanKey] then
                     local style = getHeatmapStyle(rawVal, maxValues[cleanKey])
                     local style = getHeatmapStyle(rawVal, maxValues[cleanKey])
                     if style ~= "" then
                     if style ~= "" then cell:attr("style", style .. " text-align:center;") end
                        cell:attr("style", style .. " text-align:center;")
                    end
                 end
                 end
             end
             end

Revision as of 14:00, 30 January 2026

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

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

-- [CONFIGURATION SECTION SAME AS BEFORE]
local HEATMAP_R, HEATMAP_G, HEATMAP_B = 59, 130, 246 
local HEADERS = {
    rank = "#", team = "Team", player = "Player", matches_played = "Matches",
    finishes = "Finishes", fpm = "FPM", knocks = "Knocks", damage = "Damage",
    headshots = "Headshots", longest = "Longest", assists = "Assists",
    grenade_kills = "Grenade Kills", vehicle_kills = "Vehicle Kills", contribution = "Contrib %", 
    survival = "Surv. Time", healings = "Heals", revives = "Revives", damage_taken = "Dmg Recv",
    grenades_used = "Nades Used", smokes_used = "Smokes Used", utility_used = "Util Used",
    dist_drive = "Drive Dist", dist_walk = "Walk Dist", dist_total = "Total Dist",
    bluezone = "Bluezone Time", air_drops = "Air Drops",
    total_pts = "Total Pts", place_pts = "Place Pts", elims = "Elims",
    avg_place = "Avg Place", avg_place_pts = "Avg Place Pts", avg_elims = "Avg Elims", avg_total = "Avg Pts",
    wwcd = "🥇", place_2 = "🥈", place_3 = "🥉", top_5 = "Top 5", top_8 = "Top 8", place_low = "> 8th",
    g_0 = "0", g_1_5 = "1–5", g_6_10 = "6–10", g_11_15 = "11–15", g_16_20 = "16–20", g_20_plus = "20+"
}
local NO_HEATMAP = { rank=true, team=true, player=true, matches_played=true }

-- [HELPER FUNCTIONS SAME AS BEFORE: getHeatmapStyle, getTeamLogo]
local function getHeatmapStyle(value, maxVal)
    if not tonumber(value) or not tonumber(maxVal) or maxVal == 0 then return "" end
    local v = tonumber(value)
    local ratio = v / maxVal
    local alpha = 0.05 + (ratio * 0.45) 
    return string.format("background-color: rgba(%d, %d, %d, %.2f);", HEATMAP_R, HEATMAP_G, HEATMAP_B, alpha)
end

local function getTeamLogo(teamName)
    if not teamName then return "" end
    local cleanName = teamName:gsub("'", "")
    local lightFile = cleanName .. '.png'
    local darkFile = cleanName .. '_dark.png'
    local hasLight = mw.title.new('File:' .. lightFile).exists
    local hasDark = mw.title.new('File:' .. darkFile).exists
    local html = ""
    if hasLight then html = html .. '[[File:' .. lightFile .. '|25px|link=' .. teamName .. '|class=logo-lightmode]]'
    else html = html .. '[[File:Shield_team.png|25px|link=' .. teamName .. '|class=logo-lightmode]]' end
    if hasDark then html = html .. '[[File:' .. darkFile .. '|25px|link=' .. teamName .. '|class=logo-darkmode]]'
    elseif hasLight then html = html .. '[[File:' .. lightFile .. '|25px|link=' .. teamName .. '|class=logo-darkmode]]'
    else html = html .. '[[File:Shield_team_dark.png|25px|link=' .. teamName .. '|class=logo-darkmode]]' end
    return html .. " "
end

-- ============================================================
-- MAIN GENERATOR (UPDATED FOR STAGE/GROUP)
-- ============================================================
function p.main(frame)
    local args = frame:getParent().args
    local type = args.type or "player"
    local tournament = args.tournament or mw.title.getCurrentTitle().text
    
    -- Clean up tournament name (remove subpages if BASEPAGENAME wasn't used)
    if tournament:find('/') then
        tournament = mw.text.split(tournament, '/')[1]
    end

    local map = args.map or "All"
    local stage = args.stage
    local group = args.group
    
    -- 1. Determine Columns
    local colsInput = args.columns or ""
    local colKeys = {}
    if colsInput == "" then
        if type == "player" then colKeys = {"rank", "player", "team", "matches_played", "finishes", "damage"}
        else colKeys = {"rank", "team", "matches_played", "total_pts", "wwcd"} end
    else
        colKeys = text.split(colsInput, ",")
    end
    
    -- 2. Build Query Fields (Exclude 'rank')
    local queryFields = {}
    for _, k in ipairs(colKeys) do
        local cleanK = k:match("^%s*(.-)%s*$")
        if cleanK ~= "rank" then table.insert(queryFields, cleanK) end
    end
    
    -- 3. Build WHERE Clause dynamically
    local whereParts = {}
    table.insert(whereParts, string.format("tournament='%s'", tournament:gsub("'", "\\'")))
    
    if map ~= "Any" then -- Use 'Any' to show ALL maps mixed, otherwise filter
        table.insert(whereParts, string.format("map='%s'", map:gsub("'", "\\'")))
    end
    
    if stage and stage ~= "" then
        table.insert(whereParts, string.format("stage='%s'", stage:gsub("'", "\\'")))
    end
    
    if group and group ~= "" then
        table.insert(whereParts, string.format("groupname='%s'", group:gsub("'", "\\'")))
    end
    
    local where = table.concat(whereParts, " AND ")
    
    -- 4. Fetch Data
    local table_name = (type == "player") and "Player_Stats" or "Team_Stats"
    local results = cargo.query(table_name, table.concat(queryFields, ",") .. ", team", {
        where = where,
        orderBy = (type == "player" and "finishes DESC" or "total_pts DESC"),
        limit = 100
    })
    
    if not results or #results == 0 then
        return '<div style="padding:20px; color:var(--text-muted); font-style:italic;">No statistics found for these filters.</div>'
    end
    
    -- 5. Find Max Values
    local maxValues = {}
    for _, row in ipairs(results) do
        for _, key in ipairs(queryFields) do
            local val = tonumber(row[key]) or 0
            if val > (maxValues[key] or 0) then maxValues[key] = val end
        end
    end
    
    -- 6. Build Table
    local root = html.create('div'):addClass('stats-table-wrapper')
    local tbl = root:tag('table'):addClass('wikitable flat-table sortable stats-table')
    
    -- Header
    local trHead = tbl:tag('tr')
    for _, key in ipairs(colKeys) do
        local label = HEADERS[key:match("^%s*(.-)%s*$")] or key:upper()
        trHead:tag('th'):wikitext(label)
    end
    
    -- Rows
    for i, row in ipairs(results) do
        local tr = tbl:tag('tr')
        for _, key in ipairs(colKeys) do
            local cleanKey = key:match("^%s*(.-)%s*$")
            local cell = tr:tag('td')
            
            if cleanKey == "rank" then
                cell:wikitext(i .. '.'):css("font-weight", "bold"):css("text-align", "center")
            elseif cleanKey == "team" then
                local teamName = row.team or ""
                cell:wikitext(getTeamLogo(teamName) .. '[[' .. teamName .. ']]'):css("text-align", "left"):css("white-space", "nowrap")
            elseif cleanKey == "player" then
                cell:wikitext('[[' .. (row.player or "") .. ']]'):css("text-align", "left"):css("font-weight", "bold")
            else
                local rawVal = row[cleanKey]
                cell:wikitext(rawVal):css("text-align", "center")
                if not NO_HEATMAP[cleanKey] then
                    local style = getHeatmapStyle(rawVal, maxValues[cleanKey])
                    if style ~= "" then cell:attr("style", style .. " text-align:center;") end
                end
            end
        end
    end
    
    return tostring(root)
end

return p