Модуль:Stats/Penalties
Для документации этого модуля может быть создана страница Модуль:Stats/Penalties/doc
local p = {}
local Config = require('Module:Config')
function p.render(frame)
local json_data = mw.text.jsonDecode(mw.title.new('Module:Data/GrandStats.json'):getContent())
local data = json_data.StatsPages.Penalties
local global_played = json_data.Players
local function calc_pok(g, u) return g - (u - g) * 2 end
local function bld_pok(pok) return {text = (pok > 0 and "+" .. pok or tostring(pok)), style = Config.styles.center .. " font-weight:bold;"} end
local function format_pct(g, u) return u == 0 and "0" or string.gsub(string.format("%.2f", math.floor((g / u * 100) * 100 + 0.5) / 100), "%.", ",") end
local function get_medal(p_name, metric, y_num)
if not metric then return "" end
local gp = global_played[p_name]
if not gp or not gp.AS_compiled or not gp.AS_compiled.metrics[metric] then return "" end
if y_num then
local y_data = gp.AS_compiled.metrics[metric].years[y_num] or gp.AS_compiled.metrics[metric].years[tostring(y_num)]
return y_data and y_data.color or ""
else
return gp.AS_compiled.metrics[metric].color or ""
end
end
local function build_chrono(data_type)
local list = {}
for name, p in pairs(data.players) do if p[data_type].u > 0 then p.name = name; table.insert(list, {name = name, d = p[data_type]}) end end
table.sort(list, function(a, b)
local pokA, pokB = calc_pok(a.d.g, a.d.u), calc_pok(b.d.g, b.d.u)
if pokA ~= pokB then return pokA > pokB end
if a.d.g ~= b.d.g then return a.d.g > b.d.g end
local sA, sB = a.d.g, b.d.g
for i = #Config.years, 1, -1 do
local y = Config.years[i]
local a_y = a.d.years[y] or a.d.years[tostring(y)]
local b_y = b.d.years[y] or b.d.years[tostring(y)]
sA = sA - (a_y and a_y.g or 0); sB = sB - (b_y and b_y.g or 0)
if sA ~= sB then return sA > sB end
end
return a.name < b.name
end)
local cols = Config.builder.merge({'Место', 'Игрок'}, Config.builder.years(Config.years), {'ВСЕГО', '%', '[[Лучший пенальтист#Система определения победителя|Показатель]]'})
local tbl = Config.builder.start(cols)
local col_u, col_g, tot_u, tot_g = {}, {}, 0, 0
for _, y in ipairs(Config.years) do col_u[y]=0; col_g[y]=0 end
local metric = (data_type == "all") and "pens_scored" or nil
for rank, item in ipairs(list) do
local r_data = { rank, {text = "[[" .. string.gsub(item.name, "_", " ") .. "]]", style = Config.styles.center_nowrap} }
for _, y in ipairs(Config.years) do
local y_node = item.d.years[y] or item.d.years[tostring(y)]
local u = y_node and y_node.u or 0
local g = y_node and y_node.g or 0
col_u[y] = col_u[y] + u; col_g[y] = col_g[y] + g
local y_medal = get_medal(item.name, metric, y)
local cell_style = Config.styles.center_nowrap
if y_medal ~= "" then cell_style = cell_style .. " " .. y_medal end
if u > 0 then
table.insert(r_data, {text = u .. '/' .. g, style = cell_style})
else
local gp = global_played[item.name]
local played = gp and gp.AS_compiled.played_years and (gp.AS_compiled.played_years[y] or gp.AS_compiled.played_years[tostring(y)])
if played then
table.insert(r_data, {text = "0/0", style = cell_style})
else
table.insert(r_data, "")
end
end
end
tot_u = tot_u + item.d.u; tot_g = tot_g + item.d.g
local tot_medal = get_medal(item.name, metric, nil)
local tot_style = Config.styles.center .. " font-weight:bold;"
if tot_medal ~= "" then tot_style = tot_style .. " " .. tot_medal end
table.insert(r_data, {text = item.d.u .. '/' .. item.d.g, style = tot_style})
table.insert(r_data, format_pct(item.d.g, item.d.u) .. '%')
table.insert(r_data, bld_pok(calc_pok(item.d.g, item.d.u)))
Config.builder.row(tbl, r_data)
end
local f_data = { "", {text="'''ВСЕГО'''", style = Config.styles.center} }
for _, y in ipairs(Config.years) do table.insert(f_data, {text = col_u[y] > 0 and (col_u[y] .. '/' .. col_g[y]) or '0', style = Config.styles.center .. " font-weight:bold;"}) end
table.insert(f_data, {text = tot_u .. '/' .. tot_g, style = Config.styles.center .. " font-weight:bold;"})
table.insert(f_data, {text = format_pct(tot_g, tot_u) .. '%', style = Config.styles.center .. " font-weight:bold;"})
table.insert(f_data, bld_pok(calc_pok(tot_g, tot_u)))
Config.builder.row(tbl, f_data)
return tostring(tbl)
end
local function build_results(data_type, spec_year)
local list, t_u, t_g, t_k, t_o, t_w, t_p, t_c = {}, 0, 0, 0, 0, 0, 0, 0
for name, p in pairs(data.players) do
local d = spec_year and (p[data_type].years[spec_year] or p[data_type].years[tostring(spec_year)]) or p[data_type]
if d and d.u > 0 then table.insert(list, {name = name, d = d, stvor = d.g + d.k, frame = d.g + d.k + d.p + d.c}) end
end
table.sort(list, function(a, b)
local pA, pB = calc_pok(a.d.g, a.d.u), calc_pok(b.d.g, b.d.u)
if pA ~= pB then return pA > pB end
if a.d.g ~= b.d.g then return a.d.g > b.d.g end
if not spec_year then
local sA, sB = a.d.g, b.d.g
for i = #Config.years, 1, -1 do
local y = Config.years[i]
local a_y = data.players[a.name][data_type].years[y] or data.players[a.name][data_type].years[tostring(y)]
local b_y = data.players[b.name][data_type].years[y] or data.players[b.name][data_type].years[tostring(y)]
sA = sA - (a_y and a_y.g or 0); sB = sB - (b_y and b_y.g or 0)
if sA ~= sB then return sA > sB end
end
end
if a.stvor ~= b.stvor then return a.stvor > b.stvor end
if a.frame ~= b.frame then return a.frame > b.frame end
return a.name < b.name
end)
local tbl = Config.builder.start({'Место', 'Игрок', '%', '[[Лучший пенальтист#Система определения победителя|Показатель]]', 'Удары', 'Голы', 'вр.', 'в.', 'м.', 'шт.', 'п.'})
for rank, item in ipairs(list) do
local d = item.d
t_u=t_u+d.u; t_g=t_g+d.g; t_k=t_k+d.k; t_o=t_o+d.o; t_w=t_w+d.w; t_p=t_p+d.p; t_c=t_c+d.c
Config.builder.row(tbl, { rank, {text = "[[" .. string.gsub(item.name, "_", " ") .. "]]", style = Config.styles.center_nowrap}, format_pct(d.g, d.u), bld_pok(calc_pok(d.g, d.u)), {text=d.u, style=Config.styles.center.." font-weight:bold;"}, {text=d.g, style=Config.styles.center.." font-weight:bold;"}, d.k, d.o, d.w, d.p, d.c })
end
if t_u > 0 then
Config.builder.row(tbl, { {text="'''ВСЕГО'''", colspan=2, style=Config.styles.center}, {text=format_pct(t_g, t_u), style=Config.styles.center.." font-weight:bold;"}, bld_pok(calc_pok(t_g, t_u)), {text=t_u, style=Config.styles.center.." font-weight:bold;"}, {text=t_g, style=Config.styles.center.." font-weight:bold;"}, {text=t_k, style=Config.styles.center.." font-weight:bold;"}, {text=t_o, style=Config.styles.center.." font-weight:bold;"}, {text=t_w, style=Config.styles.center.." font-weight:bold;"}, {text=t_p, style=Config.styles.center.." font-weight:bold;"}, {text=t_c, style=Config.styles.center.." font-weight:bold;"} })
end
return tostring(tbl)
end
local function build_tournaments(data_type)
local tbl = Config.builder.start({'№', 'ЧТМ', '%', '[[Лучший пенальтист#Система определения победителя|Показатель]]', 'Удары', 'Голы', 'вр.', 'в.', 'м.', 'шт.', 'п.'})
local tot_u, tot_g, tot_k, tot_o, tot_w, tot_p, tot_c, rank = 0,0,0,0,0,0,0,1
for _, year in ipairs(Config.years) do
local d = data.tournaments[year] or data.tournaments[tostring(year)]
if d and d[data_type].u > 0 then
local td = d[data_type]
Config.builder.row(tbl, { rank, "[[" .. year .. "]]", format_pct(td.g, td.u), bld_pok(calc_pok(td.g, td.u)), {text=td.u, style=Config.styles.center.." font-weight:bold;"}, {text=td.g, style=Config.styles.center.." font-weight:bold;"}, td.k, td.o, td.w, td.p, td.c })
rank = rank + 1; tot_u=tot_u+td.u; tot_g=tot_g+td.g; tot_k=tot_k+td.k; tot_o=tot_o+td.o; tot_w=tot_w+td.w; tot_p=tot_p+td.p; tot_c=tot_c+td.c
end
end
Config.builder.row(tbl, { {text="'''ВСЕГО'''", colspan=2, style=Config.styles.center}, {text=format_pct(tot_g, tot_u), style=Config.styles.center.." font-weight:bold;"}, bld_pok(calc_pok(tot_g, tot_u)), {text=tot_u, style=Config.styles.center.." font-weight:bold;"}, {text=tot_g, style=Config.styles.center.." font-weight:bold;"}, {text=tot_k, style=Config.styles.center.." font-weight:bold;"}, {text=tot_o, style=Config.styles.center.." font-weight:bold;"}, {text=tot_w, style=Config.styles.center.." font-weight:bold;"}, {text=tot_p, style=Config.styles.center.." font-weight:bold;"}, {text=tot_c, style=Config.styles.center.." font-weight:bold;"} })
return tostring(tbl)
end
local function build_simple(data_type, metric_key)
local start_year = Config.years[1]
if metric_key and Config.metrics[metric_key] and Config.metrics[metric_key].start then start_year = Config.metrics[metric_key].start end
local valid_years = {}
for _, y in ipairs(Config.years) do if y >= start_year then table.insert(valid_years, y) end end
local list = {}
for name, p in pairs(data.players) do if p[data_type].total > 0 then table.insert(list, {name = name, d = p[data_type]}) end end
table.sort(list, function(a, b)
if a.d.total ~= b.d.total then return a.d.total > b.d.total end
local sA, sB = a.d.total, b.d.total
for i = #valid_years, 1, -1 do
local y = valid_years[i]
sA = sA - (a.d.years[y] or a.d.years[tostring(y)] or 0)
sB = sB - (b.d.years[y] or b.d.years[tostring(y)] or 0)
if sA ~= sB then return sA > sB end
end
return a.name < b.name
end)
local cols = Config.builder.merge({'Место', 'Игрок'}, Config.builder.years(valid_years), {'ВСЕГО'})
local tbl = Config.builder.start(cols)
local col_totals = {}; for _, y in ipairs(valid_years) do col_totals[y] = 0 end
local grand_total = 0
for rank, item in ipairs(list) do
local r_data = { rank, {text="[[" .. string.gsub(item.name, "_", " ") .. "]]", style=Config.styles.center_nowrap} }
for _, y in ipairs(valid_years) do
local val = item.d.years[y] or item.d.years[tostring(y)] or 0
local y_medal = get_medal(item.name, metric_key, y)
local cell_style = Config.styles.center_nowrap
if y_medal ~= "" then cell_style = cell_style .. " " .. y_medal end
if val > 0 then
table.insert(r_data, {text = tostring(val), style = cell_style})
col_totals[y] = col_totals[y] + val
else
local gp = global_played[item.name]
local played = gp and gp.AS_compiled.played_years and (gp.AS_compiled.played_years[y] or gp.AS_compiled.played_years[tostring(y)])
if played then
table.insert(r_data, {text = "0", style = cell_style})
else
table.insert(r_data, "")
end
end
end
local tot_medal = get_medal(item.name, metric_key, nil)
local tot_style = Config.styles.center .. " font-weight:bold;"
if tot_medal ~= "" then tot_style = tot_style .. " " .. tot_medal end
table.insert(r_data, {text=tostring(item.d.total), style=tot_style}); grand_total = grand_total + item.d.total
Config.builder.row(tbl, r_data)
end
if metric_key and Config.metrics[metric_key] and Config.metrics[metric_key].adjustments then
for k, v in pairs(Config.metrics[metric_key].adjustments) do
if k == "total" then grand_total = grand_total + v
else
local k_num = tonumber(k)
if k_num and col_totals[k_num] then col_totals[k_num] = col_totals[k_num] + v; grand_total = grand_total + v end
end
end
end
local footer_data = { "", {text="'''ВСЕГО'''", style=Config.styles.center} }
for _, year in ipairs(valid_years) do table.insert(footer_data, {text="'''" .. tostring(col_totals[year]) .. "'''", style=Config.styles.center}) end
table.insert(footer_data, {text="'''" .. tostring(grand_total) .. "'''", style=Config.styles.center})
Config.builder.row(tbl, footer_data)
return tostring(tbl)
end
local output = { Config.styles.wiki_templates }
table.insert(output, "== Пробитые пенальти =="); table.insert(output, "=== Всего ===\n''(Всего ударов/Забитых ударов)''"); table.insert(output, build_chrono("all"))
table.insert(output, "=== Только в игровое время ===\n''(Без учёта серий пенальти)''"); table.insert(output, build_chrono("ingame"))
table.insert(output, "== Результаты ударов =="); table.insert(output, "=== Все пенальти ==="); table.insert(output, build_results("all"))
for _, y in ipairs(Config.years) do
local t_data = data.tournaments[y] or data.tournaments[tostring(y)]
if t_data and t_data.all.u > 0 then table.insert(output, "==== [[" .. y .. "]] ===="); table.insert(output, build_results("all", y)) end
end
table.insert(output, "=== Только в игровое время ==="); table.insert(output, build_results("ingame"))
for _, y in ipairs(Config.years) do
local t_data = data.tournaments[y] or data.tournaments[tostring(y)]
if t_data and t_data.ingame.u > 0 then table.insert(output, "==== [[" .. y .. "]] ===="); table.insert(output, build_results("ingame", y)) end
end
table.insert(output, "== Статистика по чемпионатам =="); table.insert(output, "=== Все пенальти ==="); table.insert(output, build_tournaments("all"))
table.insert(output, "=== Только в игровое время ==="); table.insert(output, build_tournaments("ingame"))
table.insert(output, "== Отбитые пенальти ==\n''Учитываются только сэйвы. Удары мимо и выше ворот, а также в каркас в данную статистику не входят.''"); table.insert(output, build_simple("saves", "pens_saved"))
table.insert(output, "== Привезённые пенальти ==\n''Официально подсчитываются начиная с [[ЧТМ-2026]]. Данный термин означает фол в собственной штрафной, после которого был назначен пенальти.''"); table.insert(output, build_simple("fouls", "caused_pens"))
return frame:preprocess(table.concat(output, "\n\n"))
end
return p