Module:Statistics: Difference between revisions
From eSportsAmaze
More actions
Esportsamaze (talk | contribs) No edit summary |
Esportsamaze (talk | contribs) No edit summary |
||
| Line 7: | Line 7: | ||
-- CONFIGURATION | -- 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 | -- BASICS | ||
| Line 16: | Line 15: | ||
team = "Team", | team = "Team", | ||
player = "Player", | player = "Player", | ||
matches_played = "Matches", | matches_played = "Matches", | ||
-- KILLS / OFFENSE | -- KILLS / OFFENSE | ||
| Line 60: | Line 59: | ||
-- PLACEMENT COUNTS | -- PLACEMENT COUNTS | ||
wwcd = "🥇", | wwcd = "🥇", | ||
place_2 = "🥈", | place_2 = "🥈", | ||
place_3 = "🥉", | place_3 = "🥉", | ||
top_5 = "Top 5", | top_5 = "Top 5", | ||
top_8 = "Top 8", | top_8 = "Top 8", | ||
| Line 80: | Line 79: | ||
-- ============================================================ | -- ============================================================ | ||
-- HELPER: | -- 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 | ||
local v = tonumber(value) | local v = tonumber(value) | ||
local ratio = v / maxVal | local ratio = v / maxVal | ||
local alpha = 0.05 + (ratio * 0.45) | 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 | |||
-- ============================================================ | |||
-- HELPER: Get Team Logo | |||
-- ============================================================ | |||
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 = "" | |||
-- Light Mode | |||
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 | |||
-- 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 .. " " | |||
end | end | ||
| Line 99: | Line 124: | ||
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().subpageText | ||
local map = args.map or "All" | local map = args.map or "All" | ||
| Line 108: | Line 133: | ||
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 | else | ||
colKeys = {"rank", "team", "matches_played", "total_pts", "wwcd | colKeys = {"rank", "team", "matches_played", "total_pts", "wwcd"} | ||
end | end | ||
else | else | ||
| Line 118: | Line 142: | ||
end | end | ||
-- 2. | -- 2. Build Query Fields (Exclude 'rank' from SQL) | ||
local queryFields = {} | |||
for _, k in ipairs(colKeys) do | |||
local cleanK = k:match("^%s*(.-)%s*$") | |||
if cleanK ~= "rank" then -- Don't ask DB for 'rank' | |||
table.insert(queryFields, cleanK) | |||
end | |||
end | |||
local table_name = (type == "player") and "Player_Stats" or "Team_Stats" | local table_name = (type == "player") and "Player_Stats" or "Team_Stats" | ||
local | local fieldsSQL = table.concat(queryFields, ",") | ||
local where = string.format("tournament='%s' AND map='%s'", tournament:gsub("'", "\\'"), map) | local where = string.format("tournament='%s' AND map='%s'", tournament:gsub("'", "\\'"), map) | ||
-- | -- 3. Fetch Data | ||
local results = cargo.query(table_name, | 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 134: | Line 166: | ||
end | end | ||
-- | -- 4. Find Max Values | ||
local maxValues = {} | local maxValues = {} | ||
for _, row in ipairs(results) do | for _, row in ipairs(results) do | ||
for _, key in ipairs( | for _, key in ipairs(queryFields) do | ||
local val = tonumber(row[key]) or 0 | local val = tonumber(row[key]) or 0 | ||
if val > (maxValues[key] or 0) then | if val > (maxValues[key] or 0) then maxValues[key] = val end | ||
end | end | ||
end | end | ||
-- | -- 5. 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 | ||
local trHead = tbl:tag('tr') | local trHead = tbl:tag('tr') | ||
for _, key in ipairs(colKeys) do | for _, key in ipairs(colKeys) do | ||
| Line 157: | Line 186: | ||
end | end | ||
-- Rows | -- Data 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') | ||
| Line 163: | Line 192: | ||
for _, key in ipairs(colKeys) do | for _, key in ipairs(colKeys) do | ||
local cleanKey = key:match("^%s*(.-)%s*$") | local cleanKey = key:match("^%s*(.-)%s*$") | ||
local cell = tr:tag('td') | local cell = tr:tag('td') | ||
if cleanKey == "rank" then | if cleanKey == "rank" then | ||
cell:wikitext(i) | -- Auto-generate Rank (1, 2, 3...) | ||
cell:wikitext(i .. '.') | |||
cell:css("font-weight", "bold") | cell:css("font-weight", "bold") | ||
cell:css("text-align", "center") | |||
elseif cleanKey == "team" then | elseif cleanKey == "team" then | ||
cell:wikitext('[[' .. | local teamName = row.team or "" | ||
cell:wikitext(getTeamLogo(teamName) .. '[[' .. teamName .. ']]') | |||
cell:css("text-align", "left") | cell:css("text-align", "left") | ||
cell:css("white-space", "nowrap") | |||
elseif cleanKey == "player" then | elseif cleanKey == "player" then | ||
cell:wikitext('[[' .. ( | cell:wikitext('[[' .. (row.player or "") .. ']]') | ||
cell:css("text-align", "left") | cell:css("text-align", "left") | ||
cell:css("font-weight", "bold") | cell:css("font-weight", "bold") | ||
else | else | ||
-- Numeric Data | -- Numeric Data | ||
local rawVal = row[cleanKey] | |||
cell:wikitext(rawVal) | cell:wikitext(rawVal) | ||
cell:css("text-align", "center") | cell:css("text-align", "center") | ||
-- Heatmap | -- 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]) | ||
Revision as of 13:41, 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
-- ============================================================
-- Heatmap Color (Blue: 59, 130, 246)
local HEATMAP_R, HEATMAP_G, HEATMAP_B = 59, 130, 246
local HEADERS = {
-- BASICS
rank = "#",
team = "Team",
player = "Player",
matches_played = "Matches",
-- KILLS / OFFENSE
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 / 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 }
-- ============================================================
-- HELPER: Heatmap Style
-- ============================================================
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
-- ============================================================
-- HELPER: Get Team Logo
-- ============================================================
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 = ""
-- Light Mode
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
-- 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 .. " "
end
-- ============================================================
-- MAIN GENERATOR
-- ============================================================
function p.main(frame)
local args = frame:getParent().args
local type = args.type or "player"
local tournament = args.tournament or mw.title.getCurrentTitle().subpageText
local map = args.map or "All"
-- 1. Determine Columns to Show
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' from SQL)
local queryFields = {}
for _, k in ipairs(colKeys) do
local cleanK = k:match("^%s*(.-)%s*$")
if cleanK ~= "rank" then -- Don't ask DB for 'rank'
table.insert(queryFields, cleanK)
end
end
local table_name = (type == "player") and "Player_Stats" or "Team_Stats"
local fieldsSQL = table.concat(queryFields, ",")
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,
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:#666;">No statistics available for ' .. map .. '.</div>'
end
-- 4. 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
-- 5. Build Table
local root = html.create('div'):addClass('stats-table-wrapper')
local tbl = root:tag('table'):addClass('wikitable flat-table sortable stats-table')
-- Header Row
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
-- Data 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
-- Auto-generate Rank (1, 2, 3...)
cell:wikitext(i .. '.')
cell:css("font-weight", "bold")
cell:css("text-align", "center")
elseif cleanKey == "team" then
local teamName = row.team or ""
cell:wikitext(getTeamLogo(teamName) .. '[[' .. teamName .. ']]')
cell:css("text-align", "left")
cell:css("white-space", "nowrap")
elseif cleanKey == "player" then
cell:wikitext('[[' .. (row.player or "") .. ']]')
cell:css("text-align", "left")
cell:css("font-weight", "bold")
else
-- Numeric Data
local rawVal = row[cleanKey]
cell:wikitext(rawVal)
cell:css("text-align", "center")
-- Heatmap
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