Модуль:Megarating
Перейти к навигации
Перейти к поиску
[просмотр] [просмотр кода] [история] [обновить]
Модуль для подсчёта и вывода таблиц мегарейтинга.
Главная функция, работающая на основной странице:
{{#invoke:Megarating|drawAll}}
Вспомогательная функция, выдающая сворачиваемые таблички по конкретным ЧТМ:
{{#invoke:Megarating|drawTables|years=2006,2010, 2014,2018,2022,2026,2030,2034,2038,2042,2046|expanded=no}}
Можно выбирать любые годы, перечисляя их через запятую и выводить сразу развёрнутую таблицу, задав yes параметру expanded.
-- ==========================================================
-- Модуль:Megarating
-- Архитектура подсчёта мегарейтинга ЧТМ
-- Версия 1.3.
-- ==========================================================
-- ВНИМАНИЕ!!!
-- Для использования в качестве движка для других модулей
-- используйте ТОЛЬКО function Megarating.evaluate_raw_stats,
-- чтобы не гонять БД лишний раз.
-- ==========================================================
local Megarating = {}
local Config = require('Module:Config')
local StatEngine = require('Module:StatEngine')
local TournamentAwards = require('Module:StatEngine/TournamentAwards')
local POINTS_10 = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
local POINTS_5 = {5, 4, 3, 2, 1}
-- =========================================================
-- НАСТРОЙКИ КОЛОНОК И ИХ ЗАГОЛОВКОВ
-- =========================================================
local COLUMNS = {
[1] = "[[Лучший бомбардир|1]]",
[2] = "[[Игрок матча|2]]",
[3] = "[[Голы|3]]",
[4] = "[[Мега-трики ЧТМ|4]]",
[5] = "[[Лучший пенальтист|5]]",
[6] = "[[Отбитые пенальти|6]]",
[7] = "[[Игрок матча как вратарь|7]]",
[8] = '<abbr title="Сумма очков за признание игроком матча в плей-офф">8</abbr>',
[9] = '<abbr title="Сумма очков за победы в плей-офф">9</abbr>',
[10] = "[[Процент набранных очков|10]]",
[11] = "[[Показатель полезности|11]]",
[12] = "[[Коэффициент пропущенных голов|12]]",
[13] = "[[Матчи|13]]",
[14] = "[[Голы головой|14]]",
[15] = "[[Выносы из пустых|15]]",
[16] = "[[Средняя результативность|16]]",
[17] = "[[Матчи на ноль|17]]",
[18] = "[[Лучший ассистент|18]]",
[19] = "[[Передачи|19]]",
[20] = "[[Голы пяточкой|20]]",
[21] = "[[Голы со штрафных|21]]",
[22] = '<abbr title="Голевые передачи (ср. за матч)">22</abbr>',
[23] = "[[Мега-трики голевых передач|23]]",
[24] = '<abbr title="Бонусные очки">Б</abbr>'
}
-- Порядок показателей для правила разрешения равенства
local TIE_BREAKER_ORDER = {1, 9, 8, 24, 18, 10, 2, 3, 19, 11, 12, 13, 7, 4, 23, 16, 22, 17, 6, 5, 15, 14, 21, 20}
-- =========================================================
-- УТИЛИТЫ ДЛЯ СОРТИРОВКИ И ОТОБРАЖЕНИЯ
-- =========================================================
local function fmt(num)
if not num or num == 0 then return "0" end
local s = string.format("%.2f", num)
if s:find("%.") then
s = s:gsub("0+$", "")
s = s:gsub("%.$", "")
end
return s
end
local function get_bg_color(rank)
if rank == 1 then return Config.styles.gold
elseif rank == 2 then return Config.styles.silver
elseif rank == 3 then return Config.styles.bronze
elseif rank == 4 then return Config.styles.wood
end
return ""
end
-- =========================================================
-- ЛОГИКА ВЫЧИСЛЕНИЙ (ТАЙБРЕЙКЕР И АЛЛОКАЦИЯ ОЧКОВ)
-- =========================================================
local function extract_spheres(year_db)
local spheres = {}
for _, match in pairs(year_db) do
if match.golden_sphere then spheres[match.golden_sphere] = 4 end
if match.silver_sphere then spheres[match.silver_sphere] = math.max(spheres[match.silver_sphere] or 0, 3) end
if match.bronze_sphere then spheres[match.bronze_sphere] = math.max(spheres[match.bronze_sphere] or 0, 2) end
if match.wooden_sphere then spheres[match.wooden_sphere] = math.max(spheres[match.wooden_sphere] or 0, 1) end
end
return spheres
end
local function is_perfect_tie(a, b, year, spheres)
if a.total_points ~= b.total_points then return false end
local sphere_a = spheres[a.name] or 0
local sphere_b = spheres[b.name] or 0
if sphere_a ~= sphere_b then return false end
for _, metric_id in ipairs(TIE_BREAKER_ORDER) do
local valid = true
if metric_id >= 10 and metric_id <= 17 and year < Config.eras.plus_minus then valid = false end
if metric_id >= 18 and metric_id <= 23 and year < Config.eras.assists then valid = false end
if valid then
local pts_a = a.breakdown[metric_id] or 0
local pts_b = b.breakdown[metric_id] or 0
if pts_a ~= pts_b then return false end
end
end
return true
end
local function compare_players(a, b, year, spheres)
if not is_perfect_tie(a, b, year, spheres) then
if a.total_points ~= b.total_points then return a.total_points > b.total_points end
local sphere_a = spheres[a.name] or 0
local sphere_b = spheres[b.name] or 0
if sphere_a ~= sphere_b then return sphere_a > sphere_b end
for _, metric_id in ipairs(TIE_BREAKER_ORDER) do
local valid = true
if metric_id >= 10 and metric_id <= 17 and year < Config.eras.plus_minus then valid = false end
if metric_id >= 18 and metric_id <= 23 and year < Config.eras.assists then valid = false end
if valid then
local pts_a = a.breakdown[metric_id] or 0
local pts_b = b.breakdown[metric_id] or 0
if pts_a ~= pts_b then return pts_a > pts_b end
end
end
end
return a.name < b.name
end
local function allocate_points(sorted_list, point_scheme, val_func, add_points_func, metric_id)
local i = 1
while i <= #sorted_list and i <= #point_scheme do
local current_val = val_func(sorted_list[i])
local group = { sorted_list[i] }
local j = i + 1
while j <= #sorted_list and val_func(sorted_list[j]) == current_val do
table.insert(group, sorted_list[j])
j = j + 1
end
local total_pts = 0
for k = 1, #group do
if (i + k - 1) <= #point_scheme then total_pts = total_pts + point_scheme[i + k - 1] end
end
local shared_pts = total_pts / #group
for _, p in ipairs(group) do
add_points_func(p.name, metric_id, shared_pts)
end
i = j
end
end
-- =========================================================
-- ЧИСТЫЙ ДВИЖОК МЕГАРЕЙТИНГА (Не вызывает Комбайн, жрёт готовое)
-- =========================================================
function Megarating.evaluate_raw_stats(year, year_db, players)
local results = {}
for name, _ in pairs(players) do
results[name] = { name = name, total_points = 0, breakdown = {} }
end
local function add_pts(name, metric_id, pts)
if pts > 0 then
results[name].total_points = results[name].total_points + pts
results[name].breakdown[metric_id] = (results[name].breakdown[metric_id] or 0) + pts
end
end
local function process_metric(metric_id, scheme, sort_asc, condition_func, get_val_func)
local list = {}
for name, p in pairs(players) do
local val = get_val_func(p)
if condition_func(p, val) then table.insert(list, { name = name, val = val }) end
end
table.sort(list, function(a, b)
if sort_asc then return a.val < b.val else return a.val > b.val end
end)
allocate_points(list, scheme, function(item) return item.val end, add_pts, metric_id)
end
local function process_award_metric(metric_id, award_type)
local raw_list = TournamentAwards.getTournamentAwards(year, year_db, award_type)
local dedup_list = {}
local seen = {}
for _, p in ipairs(raw_list) do
if not seen[p.player] and p.total > 0 then
seen[p.player] = true
table.insert(dedup_list, { name = p.player, val = p.total })
end
end
table.sort(dedup_list, function(a, b)
if a.val ~= b.val then return a.val > b.val end
return a.name < b.name
end)
allocate_points(dedup_list, POINTS_10, function(item) return item.val end, add_pts, metric_id)
end
if year >= Config.eras.goals then process_award_metric(1, "goals") end
if year >= Config.eras.assists then process_award_metric(18, "assists") end
process_metric(2, POINTS_10, false, function(p, v) return v > 0 end, function(p) return p.mvp.is_mvp end)
process_metric(3, POINTS_10, false, function(p, v) return v > 0 end, function(p) return p.goals.total end)
local mt_goals_list = {}
for name, p in pairs(players) do
local total_mt = p.goals.hat_trick + p.goals.poker + p.goals.penta + p.goals.hexa
if total_mt > 0 then table.insert(mt_goals_list, { name = name, total = total_mt, hexa = p.goals.hexa, penta = p.goals.penta, poker = p.goals.poker }) end
end
table.sort(mt_goals_list, function(a, b)
if a.total ~= b.total then return a.total > b.total end
if a.hexa ~= b.hexa then return a.hexa > b.hexa end
if a.penta ~= b.penta then return a.penta > b.penta end
if a.poker ~= b.poker then return a.poker > b.poker end
return a.name < b.name
end)
allocate_points(mt_goals_list, POINTS_5, function(item) return item.total .. "_" .. item.hexa .. "_" .. item.penta .. "_" .. item.poker end, add_pts, 4)
local pen_list = {}
for name, p in pairs(players) do
local a = p.penalties.in_game.u + p.penalties.shootout.u
local b = p.penalties.in_game.g + p.penalties.shootout.g
if a > 0 then table.insert(pen_list, { name = name, val = b - (a - b) * 2, scored = b }) end
end
table.sort(pen_list, function(a, b)
if a.val ~= b.val then return a.val > b.val end
if a.scored ~= b.scored then return a.scored > b.scored end
return a.name < b.name
end)
allocate_points(pen_list, POINTS_5, function(item) return item.val .. "_" .. item.scored end, add_pts, 5)
process_metric(6, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.penalties.saved_as_goalie end)
process_metric(7, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.mvp.is_goalie_mvp end)
if year >= Config.eras.plus_minus then
process_metric(10, POINTS_10, false, function(p, v) return p.megarating.mr_matches >= 20 end, function(p) return (p.megarating.mr_points / (p.megarating.mr_matches * 3)) * 100 end)
process_metric(11, POINTS_10, false, function(p, v) return p.megarating.mr_matches > 0 end, function(p) return p.plus_minus end)
local kpg_list = {}
for name, p in pairs(players) do
if p.matches_goalie > 0 then table.insert(kpg_list, { name = name, val = p.weighted_ga / p.matches_goalie, matches = p.matches_goalie }) end
end
table.sort(kpg_list, function(a, b)
local t_a = (a.matches >= 5) and 1 or 2
local t_b = (b.matches >= 5) and 1 or 2
if t_a ~= t_b then return t_a < t_b end
if a.val ~= b.val then return a.val < b.val end
return a.name < b.name
end)
allocate_points(kpg_list, POINTS_10, function(item) return ((item.matches >= 5) and 1 or 2) .. "_" .. string.format("%.3f", item.val) end, add_pts, 12)
process_metric(13, POINTS_10, false, function(p, v) return v > 0 end, function(p) return p.matches_total end)
process_metric(14, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.goals.head end)
process_metric(15, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.clearances end)
process_metric(16, POINTS_5, false, function(p, v) return p.matches_field >= 20 and p.goals.total > 0 end, function(p) return p.goals.total / p.matches_field end)
process_metric(17, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.clean_sheets end)
end
if year >= Config.eras.assists then
process_metric(19, POINTS_10, false, function(p, v) return v > 0 end, function(p) return p.assists.total end)
process_metric(20, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.goals.heel end)
process_metric(21, POINTS_5, false, function(p, v) return v > 0 end, function(p) return p.goals.free_kick end)
process_metric(22, POINTS_5, false, function(p, v) return p.matches_field >= 20 and p.assists.total > 0 end, function(p) return p.assists.total / p.matches_field end)
local mt_assists_list = {}
for name, p in pairs(players) do
local total_mt = p.assists.hat_trick + p.assists.poker + p.assists.penta
if total_mt > 0 then table.insert(mt_assists_list, { name = name, total = total_mt, penta = p.assists.penta, poker = p.assists.poker }) end
end
table.sort(mt_assists_list, function(a, b)
if a.total ~= b.total then return a.total > b.total end
if a.penta ~= b.penta then return a.penta > b.penta end
if a.poker ~= b.poker then return a.poker > b.poker end
return a.name < b.name
end)
allocate_points(mt_assists_list, POINTS_5, function(item) return item.total .. "_" .. item.penta .. "_" .. item.poker end, add_pts, 23)
end
for _, p in pairs(players) do p.megarating.final_assists = 0; p.megarating.semi_assists = 0 end
for match_id, match in pairs(year_db) do
local st = match.stage or ""
if (st == "Финал" or st == "Полуфинал") and match.goals then
for _, g in ipairs(match.goals) do
if g.assist and players[g.assist] then
if st == "Финал" then players[g.assist].megarating.final_assists = players[g.assist].megarating.final_assists + 1
else players[g.assist].megarating.semi_assists = players[g.assist].megarating.semi_assists + 1 end
end
end
end
if st == "Финал" then
local res = StatEngine.Harvester.evaluate_match(match)
if res then
local win_team = (res[1].pts >= 2) and 1 or ((res[2].pts >= 2) and 2 or 0)
if win_team > 0 then
local p_teams = StatEngine.Harvester.get_all_teams(match)
for p_name, t_id in pairs(p_teams) do
if t_id == win_team and players[p_name] then players[p_name].megarating.is_champion = true end
end
end
end
end
end
for name, p in pairs(players) do
local mr = p.megarating
local p8 = mr.playoff_mvp.r16 * 1 + mr.playoff_mvp.qf * 2 + mr.playoff_mvp.sf * 3 + mr.playoff_mvp.final * 10
if p8 > 0 then add_pts(name, 8, p8) end
local p9 = 0
if mr.is_champion then p9 = p9 + (year >= 2022 and 10 or 5) end
if year >= 2022 then p9 = p9 + mr.playoff_wins.r16 * 1 + mr.playoff_wins.qf * 2 + mr.playoff_wins.sf * 3 end
if p9 > 0 then add_pts(name, 9, p9) end
local p24 = 0
if mr.best_goal_bonus then p24 = p24 + 5 end
p24 = p24 + (mr.final_goals * 2) + (mr.final_gold_goals * 3) + (mr.final_assists * 1) + (mr.final_gold_assists * 2)
p24 = p24 + (mr.semi_goals * 1) + (mr.semi_assists * 0.5)
if p24 > 0 then add_pts(name, 24, p24) end
end
local final_list = {}
for _, r in pairs(results) do
if r.total_points > 0 then table.insert(final_list, r) end
end
return final_list
end
-- =========================================================
-- ОБОЛОЧКА ДЛЯ ОБРАТНОЙ СОВМЕСТИМОСТИ (Для старых страниц)
-- =========================================================
function Megarating.evaluate_year(year, year_db)
local stats = StatEngine.Harvester.run(year_db, {need_players = true})
return Megarating.evaluate_raw_stats(year, year_db, stats.Players)
end
-- А также добавим функцию для извлечения сфер для тайбрейкера, чтобы Значимость могла к ней обратиться
function Megarating.get_spheres_from_db(year_db)
return extract_spheres(year_db)
end
function Megarating.compare_players_public(a, b, year, spheres)
return compare_players(a, b, year, spheres)
end
-- =========================================================
-- БЛОК ИСТОРИЧЕСКОГО КЭШИРОВАНИЯ (1 проход по БД)
-- =========================================================
local _history_cache = nil
local function get_history()
if _history_cache then return _history_cache end
local history = { players = {}, tournaments = {}, yearly_lists = {} }
local latest_year = Config.years[#Config.years]
local is_latest_finished = Config.is_latest_finished
for _, year in ipairs(Config.years) do
local is_finished = (year ~= latest_year) or is_latest_finished
local success, year_db = pcall(require, 'Module:Data/' .. year)
if success and type(year_db) == "table" then
local spheres = extract_spheres(year_db)
local final_list = Megarating.evaluate_year(year, year_db)
if #final_list > 0 then
table.sort(final_list, function(a, b) return compare_players(a, b, year, spheres) end)
local max_pts = final_list[1].total_points
if is_finished then history.tournaments[year] = {} end
local current_rank = 1
for i, p in ipairs(final_list) do
local is_tie = false
if i > 1 then
is_tie = is_perfect_tie(final_list[i-1], p, year, spheres)
if not is_tie then current_rank = i end
else
current_rank = 1
end
p.computed_rank = current_rank
p.is_tie = is_tie
local pct = (p.total_points / max_pts) * 100
if not history.players[p.name] then
history.players[p.name] = {
name = p.name, total_pct = 0, years = {},
places = { [1]=0, [2]=0, [3]=0, [4]=0 },
all_places = {}, top5 = 0, top10 = 0
}
end
local hp = history.players[p.name]
hp.years[year] = { pct = pct, rank = current_rank, pts = p.total_points, is_finished = is_finished }
hp.total_pct = hp.total_pct + pct
if is_finished then
hp.all_places[current_rank] = (hp.all_places[current_rank] or 0) + 1
if current_rank <= 4 then hp.places[current_rank] = hp.places[current_rank] + 1 end
if current_rank <= 5 then hp.top5 = hp.top5 + 1 end
if current_rank <= 10 then hp.top10 = hp.top10 + 1 end
if current_rank <= 4 then
table.insert(history.tournaments[year], { name = p.name, pts = p.total_points })
end
end
end
history.yearly_lists[year] = final_list
end
end
end
_history_cache = history
return _history_cache
end
-- =========================================================
-- ВНУТРЕННИЕ ГЕНЕРАТОРЫ СТРОК ДЛЯ БЛОКОВ (html)
-- =========================================================
local function generate_historical_table(hist)
local list = {}
for _, p in pairs(hist.players) do table.insert(list, p) end
table.sort(list, function(a, b)
if math.abs(a.total_pct - b.total_pct) > 1e-6 then return a.total_pct > b.total_pct end
return a.name < b.name
end)
if #list == 0 then return "Данные не найдены." end
local columns = { "Место", "Игрок" }
for _, y in ipairs(Config.years) do
local short_y = "'" .. tostring(y):sub(3,4)
table.insert(columns, "[[" .. y .. "|" .. short_y .. "]]")
end
table.insert(columns, "ВСЕГО")
local html = Config.builder.start(columns)
html:css('width', '500px')
local current_rank = 1
for i, p in ipairs(list) do
local is_tie = false
if i > 1 and math.abs(list[i-1].total_pct - p.total_pct) <= 1e-6 then is_tie = true else current_rank = i end
local cells = {
is_tie and "" or tostring(current_rank),
"[[" .. p.name .. "]]"
}
for _, y in ipairs(Config.years) do
local y_data = p.years[y]
if y_data then
local bg = y_data.is_finished and get_bg_color(y_data.rank) or ""
table.insert(cells, {text = fmt(y_data.pct), style = Config.styles.center_nowrap .. bg})
else
table.insert(cells, "0")
end
end
table.insert(cells, "'''" .. fmt(p.total_pct) .. "'''")
Config.builder.row(html, cells)
end
return tostring(html)
end
local function generate_tournament_leaders(hist)
local columns = { "ЧТМ", "", "1", "2", "3", "4" }
local html = Config.builder.start(columns)
html:css('width', '500px')
local latest_year = Config.years[#Config.years]
local is_latest_finished = Config.is_latest_finished
for _, year in ipairs(Config.years) do
local is_finished = (year ~= latest_year) or is_latest_finished
if is_finished then
local top = hist.tournaments[year]
if top and #top > 0 then
local cells = {
"[[" .. year .. "]]"
}
local champ_file = top[1].name:gsub("%.$", "")
table.insert(cells, {text = '[[Файл:Кв-' .. champ_file .. '.png|120px]]', style = Config.styles.center_nowrap .. Config.styles.gold})
for i = 1, 4 do
local bg = get_bg_color(i)
local content = top[i] and ("'''[[" .. top[i].name .. "]]''' — '''" .. fmt(top[i].pts) .. "''' ") or ""
table.insert(cells, {text = content, style = Config.styles.center_nowrap .. bg})
end
Config.builder.row(html, cells)
end
end
end
return tostring(html)
end
local function generate_medal_standings(hist)
local list = {}
for _, p in pairs(hist.players) do
local sum_medals = p.places[1] + p.places[2] + p.places[3] + p.places[4]
if sum_medals > 0 then table.insert(list, p) end
end
table.sort(list, function(a, b)
for r = 1, 4 do
if a.places[r] ~= b.places[r] then return a.places[r] > b.places[r] end
end
for r = 5, 100 do
local a_c = a.all_places[r] or 0
local b_c = b.all_places[r] or 0
if a_c ~= b_c then return a_c > b_c end
end
return a.name < b.name
end)
local columns = { "", "Игрок", "1", "2", "3", "4", "Всего" }
local html = Config.builder.start(columns)
html:css('width', '500px')
local current_rank = 1
for i, p in ipairs(list) do
local is_tie = false
if i > 1 then
local prev = list[i-1]
local exact_match = true
for r = 1, 100 do
local a_c = p.all_places[r] or 0
local b_c = prev.all_places[r] or 0
if a_c ~= b_c then exact_match = false; break end
end
if exact_match then is_tie = true else current_rank = i end
else current_rank = 1 end
local cells = {
is_tie and "" or tostring(current_rank),
"[[" .. p.name .. "]]"
}
for r = 1, 4 do
local val = p.places[r]
local bg = (val > 0) and get_bg_color(r) or ""
local text = (val > 0) and tostring(val) or "0"
table.insert(cells, {text = text, style = Config.styles.center_nowrap .. bg})
end
local total = p.places[1] + p.places[2] + p.places[3] + p.places[4]
table.insert(cells, "'''" .. total .. "'''")
Config.builder.row(html, cells)
end
return tostring(html)
end
local function generate_top_list(hist, min_rank)
local counts = {}
for _, p in pairs(hist.players) do
local count = (min_rank == 5) and p.top5 or p.top10
if count > 0 then
if not counts[count] then counts[count] = {} end
table.insert(counts[count], p.name)
end
end
local sorted_counts = {}
for c, _ in pairs(counts) do table.insert(sorted_counts, c) end
table.sort(sorted_counts, function(a, b) return a > b end)
local out = {}
for _, c in ipairs(sorted_counts) do
local names = counts[c]
table.sort(names)
local linked = {}
for _, n in ipairs(names) do table.insert(linked, "[[" .. n .. "]]") end
local ending = "раз"
local last_digit = c % 10
local last_two = c % 100
if last_digit >= 2 and last_digit <= 4 and (last_two < 10 or last_two >= 20) then ending = "раза" end
local last_name = names[#names]
local end_char = (last_name:sub(-1) == ".") and "" or "."
table.insert(out, "* " .. c .. " " .. ending .. ": " .. table.concat(linked, ", ") .. end_char)
end
return table.concat(out, "\n")
end
-- =========================================================
-- ЧИСТЫЙ ГЕНЕРАТОР ТАБЛИЦЫ КОНКРЕТНОГО ГОДА (БЕЗ ОБРАЩЕНИЯ К БД)
-- Использует новый Config.builder.start_collapsible
-- =========================================================
function Megarating.buildYearTable(year, final_list, expanded)
if not final_list or #final_list == 0 then return "" end
local max_pts = final_list[1].total_points
local max_col = 9
if year >= Config.eras.assists then max_col = 23
elseif year >= Config.eras.plus_minus then max_col = 17 end
-- Формируем колонки для шапки
local columns = {"", "Игрок"}
for i = 1, max_col do table.insert(columns, COLUMNS[i]) end
table.insert(columns, COLUMNS[24])
table.insert(columns, '<abbr title="Сумма очков">ВСЕГО</abbr>')
table.insert(columns, '<abbr title="Процент от суммы очков, набранных лидером">%</abbr>')
local title = "[[" .. year .. "]]"
local html = Config.builder.start_collapsible(columns, title, expanded)
-- Наполняем таблицу строками
for _, p in ipairs(final_list) do
local cells = {
p.is_tie and "" or tostring(p.computed_rank),
"[[" .. p.name .. "]]"
}
for col = 1, max_col do table.insert(cells, fmt(p.breakdown[col])) end
table.insert(cells, fmt(p.breakdown[24]))
table.insert(cells, "'''" .. fmt(p.total_points) .. "'''")
local pct = (p.total_points / max_pts) * 100
table.insert(cells, fmt(pct))
Config.builder.row(html, cells)
end
return tostring(html)
end
local function generate_all_yearly_tables(hist)
local out = {}
for _, year in ipairs(Config.years) do
local final_list = hist.yearly_lists[year]
if final_list and #final_list > 0 then
-- Вызываем универсальный генератор с флагом false (свернута)
local tbl_string = Megarating.buildYearTable(year, final_list, false)
table.insert(out, tbl_string)
end
end
return table.concat(out, "\n\n")
end
-- =========================================================
-- ЕДИНЫЙ ПУБЛИЧНЫЙ ВЫЗОВ (1 проход БД на всю страницу!)
-- Вызов: {{#invoke:Megarating|drawAll}}
-- =========================================================
function Megarating.drawAll(frame)
local hist = get_history()
local out = {}
table.insert(out, "== Исторический мегарейтинг ==")
table.insert(out, generate_historical_table(hist))
table.insert(out, "== Лидеры мегарейтинга по турнирам ==")
table.insert(out, generate_tournament_leaders(hist))
table.insert(out, "== Статистика игроков ==")
table.insert(out, "=== Призовые места ===")
table.insert(out, generate_medal_standings(hist))
table.insert(out, "=== Места в пятёрке ===")
table.insert(out, generate_top_list(hist, 5))
table.insert(out, "=== Места в десятке ===")
table.insert(out, generate_top_list(hist, 10))
table.insert(out, "== Мегарейтинг по чемпионатам ==")
table.insert(out, generate_all_yearly_tables(hist))
-- Возвращаем всё с обязательной пред-загрузкой стилей Config
return frame:preprocess(Config.styles.wiki_templates .. "\n" .. table.concat(out, "\n\n"))
end
-- =========================================================
-- ОДИНОЧНЫЙ ВЫЗОВ ДЛЯ СТРАНИЦ КОНКРЕТНЫХ ТУРНИРОВ
-- Вызов: {{#invoke:Megarating|drawTables|years=2038|expanded=yes}}
-- =========================================================
function Megarating.drawTables(frame)
local args = frame.args
local input_years = args.years or tostring(Config.years[#Config.years])
local expanded = args.expanded == "yes" or args.expanded == "true"
local target_years = {}
if input_years == "all" then target_years = Config.years
else for y in string.gmatch(input_years, "%d+") do table.insert(target_years, tonumber(y)) end end
table.sort(target_years)
local output = {}
for _, year in ipairs(target_years) do
local success, year_db = pcall(require, 'Module:Data/' .. year)
if success and type(year_db) == "table" then
local spheres = extract_spheres(year_db)
local final_list = Megarating.evaluate_year(year, year_db)
if #final_list > 0 then
table.sort(final_list, function(a, b) return compare_players(a, b, year, spheres) end)
-- Подсчитываем ранги
local current_rank = 1
for i, p in ipairs(final_list) do
local is_tie = false
if i > 1 then
is_tie = is_perfect_tie(final_list[i-1], p, year, spheres)
if not is_tie then current_rank = i end
else current_rank = 1 end
p.computed_rank = current_rank
p.is_tie = is_tie
end
-- ВЫЗЫВАЕМ НАШУ ЧИСТУЮ ФУНКЦИЮ
local generated_table = Megarating.buildYearTable(year, final_list, expanded)
table.insert(output, generated_table)
end
end
end
if #output == 0 then return "Данные не найдены." end
-- Возвращаем таблицы, подклеив стили в начало
return frame:preprocess(Config.styles.wiki_templates .. "\n" .. table.concat(output, "\n\n"))
end
-- Экспорт кэша для Модуля Значимости
function Megarating.get_public_history()
return get_history()
end
return Megarating