Модуль:MatchTable

Материал из ЧТМ
Перейти к навигации Перейти к поиску
Документация Документация

Модуль-оболочка для вывода таблиц матчей. Главное назначение — отрисовывать таблицы по указанию мощного модуля TournamentResults, но может генерировать таблички и в чистом виде, если их заполнять вручную, поскольку из-за архитектурных особенностей прямого выхода на базу данных не имеет.

Параметры

Функция group

Рисует таблицы групповых раундов, автоматически подсчитывая количество участвовавших команд и очки.

  • number_of_rounds = Количество кругов, один или два.
  • compact = yes Если параметр задан, то в таблице не отображаются колонки «И», «В», «Н» и «П».
  • team1, team2 и т. д. — трёхбуквенные коды участвовавших команд.
  • score1_2 — счёт матча с участием указанных команд. Принимает также ссылки вида [[Матч|0:0]], корректно учитывая счёт в результатах
  • bold1, bold2 и т. д. — выделение жирным шрифтом нужных команд.
  • color1, color2 и т. д. — цвета ячеек указанных команд.

Функция playoff

Более простая функция, поскольку здесь не нужно ничего подсчитывать.

  • s1_team1 =, s1_team2 = участники первого противостояния.
  • s2_team1 =, s2_team2 = участники второго противостояния.
  • и т. д.
  • s1_score =, s2_score = счёт первого матча, счёт второго матча.
  • s2_bold2 = выделение жирным шрифтом нужной команды на нужной строке.
  • s1_color2 = выделение цветом нужной команды на нужной строке.

Функция _rating

Нужна для отрисовывания сворачиваемых таблиц рейтингов мест в группах, но напрямую не вызывается, поскольку для её составления нужен доступ к БД. Является внутренней функцией модуля TournamentResults.

Примеры использования

{{#invoke:MatchTable|playoff
|s1_team1 = ДОМ |s1_team2 = ЧАД |s1_score = 2:1(ET) |s1_color1 = lightgreen |s1_color2 = lightsalmon
|s2_team1 = АБХ |s2_team2 = АВИ |s2_score = [[Финал ЧТМ-2014|5:1]] |s2_color1 = |s2_color2 = 
}}
{{#invoke:MatchTable|group
| number_of_rounds = 1
| compact = yes
|team1 = БАГ |color1 = lightgreen | bold1 = yes
|team2 = СКН |color2 = lightsalmon
|team3 = АВИ |color3 = lightsalmon
|team4 = ГАЛ |color4 = lightsalmon

|score1_2 = [[Финал ЧТМ-2014|5:1]]
|score1_3 = 4:0
|score1_4 = 3:2
|score2_3 = 5:1
|score2_4 = 4:1
|score3_4 = 0:0
}}

Пожалуйста, добавляйте категории на страницу документации.

-- =======================================
-- Модуль:MatchTable
-- Оболочка для вывода таблиц матчей
-- Версия 1.3
-- =======================================
local p = {}

local teamsData = require('Модуль:Data/Teams')

local function cleanParam(param)
    if type(param) == 'string' then
        local trimmed = mw.text.trim(param)
        if trimmed ~= '' then return trimmed end
    elseif param ~= nil then
        return param
    end
    return nil
end

local function highlight_tie(text)
    return '<span style="color:#FF0000; font-weight:bold;">' .. text .. '</span>'
end

local function parse_score(str)
    if type(str) ~= 'string' then return nil, nil, false, nil, nil, false, str end
    
    local clean_str = string.gsub(str, "'''", "")
    local is_link, is_simple_link = false, false
    local target, display = nil, clean_str
    
    local m_target, m_display = mw.ustring.match(clean_str, "%[%[([^|]+)|([^%]]+)%]%]")
    if m_target then
        is_link, target, display = true, m_target, m_display
    else
        m_target = mw.ustring.match(clean_str, "%[%[([^|%]]+)%]%]")
        if m_target then
            is_link, is_simple_link, target, display = true, true, m_target, m_target
        end
    end
    
    local g1, sep, g2 = mw.ustring.match(display, "(%d+)%s*([:%-—–])%s*(%d+)")
    if g1 and g2 then return tonumber(g1), tonumber(g2), is_link, target, sep, is_simple_link, clean_str end
    return nil, nil, false, nil, nil, false, clean_str
end

local function getArgsFromFrame(frame)
    local args = frame:getParent().args
    if next(args) == nil then args = frame.args end
    return args
end

-- =======================================
-- ПЛЕЙ-ОФФ
-- =======================================
function p._playoff(args)
    local tbl = mw.html.create('table')
        :attr('border', '1')
        :attr('cellspacing', '1')
        :attr('cellpadding', '1')
        :addClass('article-table')
        :css('width', '500px')

    local row_count = tonumber(cleanParam(args.number_of_strings))
    if not row_count then
        row_count = 0
        while cleanParam(args['s' .. (row_count + 1) .. '_team1']) do
            row_count = row_count + 1
        end
    end

    for i = 1, row_count do
        local t1_code = cleanParam(args['s' .. i .. '_team1']) or '?'
        local t2_code = cleanParam(args['s' .. i .. '_team2']) or '?'
        local score = cleanParam(args['s' .. i .. '_score']) or '—'
        local color1 = cleanParam(args['s' .. i .. '_color1'])
        local color2 = cleanParam(args['s' .. i .. '_color2'])
        
        local bold1_arg = cleanParam(args['s' .. i .. '_bold1'])
        local is_bold1 = (bold1_arg == 'yes' or bold1_arg == true)
        
        local bold2_arg = cleanParam(args['s' .. i .. '_bold2'])
        local is_bold2 = (bold2_arg == 'yes' or bold2_arg == true)

        local t1_name = teamsData.getName(t1_code, 'short')
        local t2_name = teamsData.getName(t2_code, 'short')

        local tr = tbl:tag('tr')

        local td_t1_img = tr:tag('td'):attr('align', 'center'):css('white-space', 'nowrap')
            :wikitext(string.format('[[Файл:%s.jpg|50x100px]]', t1_name))
        if color1 then td_t1_img:css('background-color', color1) end

        local t1_name_str = string.format('[[%s]]', t1_name)
        if is_bold1 then t1_name_str = "'''" .. t1_name_str .. "'''" end
        local td_t1_name = tr:tag('td'):attr('align', 'center'):css('white-space', 'nowrap'):wikitext(t1_name_str)
        if color1 then td_t1_name:css('background-color', color1) end

        tr:tag('td'):attr('align', 'center'):wikitext(tostring(score))

        local t2_name_str = string.format('[[%s]]', t2_name)
        if is_bold2 then t2_name_str = "'''" .. t2_name_str .. "'''" end
        local td_t2_name = tr:tag('td'):attr('align', 'center'):css('white-space', 'nowrap'):wikitext(t2_name_str)
        if color2 then td_t2_name:css('background-color', color2) end

        local td_t2_img = tr:tag('td'):attr('align', 'center'):css('white-space', 'nowrap')
            :wikitext(string.format('[[Файл:%s.jpg|50x100px]]', t2_name))
        if color2 then td_t2_img:css('background-color', color2) end
    end

    return tostring(tbl)
end

function p.playoff(frame)
    return p._playoff(getArgsFromFrame(frame))
end

-- =======================================
-- ГРУППОВОЙ ЭТАП
-- =======================================
function p._group(args)
    local rounds = tonumber(cleanParam(args.number_of_rounds)) or 1
    
    local ms_arg = cleanParam(args.manual_sort)
    local is_manual = (ms_arg == 'yes' or ms_arg == true)
    
    local compact_arg = cleanParam(args.compact)
    local is_compact = (compact_arg == 'yes' or compact_arg == true)
    
    local tiebreaker_str = cleanParam(args.tiebreaker)
    local tie_order = {}
    if tiebreaker_str then
        local rank = 1
        for code in string.gmatch(tiebreaker_str, "[^,%s]+") do
            tie_order[code] = rank
            rank = rank + 1
        end
    end

    local teams = {}
    local n = 0
    while cleanParam(args['team' .. (n + 1)]) do
        n = n + 1
        local code = cleanParam(args['team' .. n])
        local name = teamsData.getName(code, 'short')
        local color = cleanParam(args['color' .. n])
        
        local bold_arg = cleanParam(args['bold' .. n])
        local is_bold = (bold_arg == 'yes' or bold_arg == true)
        
        teams[n] = {
            id = n, code = code, name = name, color = color, is_bold = is_bold,
            p = 0, w = 0, d = 0, l = 0, gf = 0, ga = 0, pts = 0, unresolved_tie = false
        }
    end

    if n == 0 then return "Нет команд для отображения." end

    local grid = {}
    for i = 1, n do
        grid[i] = {}
        for j = 1, n do grid[i][j] = {text = "", played = false, processed = false} end
    end

    -- ВИРТУАЛЬНАЯ БАЗА МАТЧЕЙ ДЛЯ КАЛЬКУЛЯТОРА
    local all_matches = {}

    for i = 1, n do
        for j = 1, n do
            if i ~= j and not grid[i][j].processed then
                local s_ij = cleanParam(args['score' .. i .. '_' .. j])
                local s_ji = cleanParam(args['score' .. j .. '_' .. i])
                
                if s_ij then
                    local g1, g2, is_link, target, sep, is_simple_link, clean_str = parse_score(s_ij)
                    if g1 then
                        table.insert(all_matches, {home = i, away = j, gf = g1, ga = g2})
                        
                        local text_ij = is_link and ("'''" .. clean_str .. "'''") or clean_str
                        grid[i][j] = {text = text_ij, played = true, processed = true}
                        
                        if rounds == 1 then
                            local mirrored_score = g2 .. sep .. g1
                            local text_ji = ""
                            if is_link then
                                if is_simple_link then text_ji = "'''[[" .. mirrored_score .. "]]'''"
                                else text_ji = "'''[[" .. target .. "|" .. mirrored_score .. "]]'''" end
                            else
                                text_ji = mirrored_score
                            end
                            grid[j][i] = {text = text_ji, played = true, processed = true}
                        end
                    else
                        grid[i][j] = {text = s_ij, played = false, processed = true}
                    end
                elseif rounds == 1 and s_ji then
                    local g1, g2, is_link, target, sep, is_simple_link, clean_str = parse_score(s_ji)
                    if g1 then
                        table.insert(all_matches, {home = j, away = i, gf = g1, ga = g2})
                        
                        local mirrored_score = g2 .. sep .. g1
                        local text_ij = ""
                        if is_link then
                            if is_simple_link then text_ij = "'''[[" .. mirrored_score .. "]]'''"
                            else text_ij = "'''[[" .. target .. "|" .. mirrored_score .. "]]'''" end
                        else
                            text_ij = mirrored_score
                        end
                        grid[i][j] = {text = text_ij, played = true, processed = true}
                        
                        local text_ji = is_link and ("'''" .. clean_str .. "'''") or clean_str
                        grid[j][i] = {text = text_ji, played = true, processed = true}
                    end
                end
            end
        end
    end

    -- КАЛЬКУЛЯТОР 
    for _, m in ipairs(all_matches) do
        local h, a, gf, ga = m.home, m.away, m.gf, m.ga
        
        -- Хозяева
        teams[h].p = teams[h].p + 1
        teams[h].gf = teams[h].gf + gf
        teams[h].ga = teams[h].ga + ga
        if gf > ga then
            teams[h].w = teams[h].w + 1
            teams[h].pts = teams[h].pts + 3
        elseif gf == ga then
            teams[h].d = teams[h].d + 1
            teams[h].pts = teams[h].pts + 1
        else
            teams[h].l = teams[h].l + 1
        end

        -- Гости
        teams[a].p = teams[a].p + 1
        teams[a].gf = teams[a].gf + ga
        teams[a].ga = teams[a].ga + gf
        if ga > gf then
            teams[a].w = teams[a].w + 1
            teams[a].pts = teams[a].pts + 3
        elseif gf == ga then
            teams[a].d = teams[a].d + 1
            teams[a].pts = teams[a].pts + 1
        else
            teams[a].l = teams[a].l + 1
        end
    end

    local sorted_teams = {}
    for i = 1, n do table.insert(sorted_teams, teams[i]) end

    if not is_manual then
        local function get_h2h_stats(t_ids)
            local h2h = {}
            local in_group = {}
            for _, id in ipairs(t_ids) do 
                h2h[id] = {pts = 0, gd = 0, gf = 0} 
                in_group[id] = true
            end
            
            for _, m in ipairs(all_matches) do
                local h, a, gf, ga = m.home, m.away, m.gf, m.ga
                if in_group[h] and in_group[a] then
                    h2h[h].gf = h2h[h].gf + gf
                    h2h[h].gd = h2h[h].gd + (gf - ga)
                    if gf > ga then h2h[h].pts = h2h[h].pts + 3
                    elseif gf == ga then h2h[h].pts = h2h[h].pts + 1 end
                    
                    h2h[a].gf = h2h[a].gf + ga
                    h2h[a].gd = h2h[a].gd + (ga - gf)
                    if ga > gf then h2h[a].pts = h2h[a].pts + 3
                    elseif ga == gf then h2h[a].pts = h2h[a].pts + 1 end
                end
            end
            return h2h
        end

        table.sort(sorted_teams, function(a, b)
            if a.pts ~= b.pts then return a.pts > b.pts end
            if (a.gf - a.ga) ~= (b.gf - b.ga) then return (a.gf - a.ga) > (b.gf - b.ga) end
            if a.gf ~= b.gf then return a.gf > b.gf end
            return a.id < b.id
        end)

        local i = 1
        while i <= n do
            local j = i
            while j < n and
                  sorted_teams[j+1].pts == sorted_teams[i].pts and
                  (sorted_teams[j+1].gf - sorted_teams[j+1].ga) == (sorted_teams[i].gf - sorted_teams[i].ga) and
                  sorted_teams[j+1].gf == sorted_teams[i].gf do
                j = j + 1
            end
            
            if j > i then
                local tied_ids = {}
                for k = i, j do table.insert(tied_ids, sorted_teams[k].id) end
                local h2h = get_h2h_stats(tied_ids)
                
                local sub = {}
                for k = i, j do table.insert(sub, sorted_teams[k]) end
                
                table.sort(sub, function(a, b)
                    if h2h[a.id].pts ~= h2h[b.id].pts then return h2h[a.id].pts > h2h[b.id].pts end
                    if h2h[a.id].gd ~= h2h[b.id].gd then return h2h[a.id].gd > h2h[b.id].gd end
                    if h2h[a.id].gf ~= h2h[b.id].gf then return h2h[a.id].gf > h2h[b.id].gf end
                    
                    local order_a = tie_order[a.code]
                    local order_b = tie_order[b.code]
                    if order_a and order_b and order_a ~= order_b then
                        return order_a < order_b
                    end
                    
                    return a.id < b.id
                end)
                
                -- Подсветка неразрешенных споров
                for k = 1, #sub - 1 do
                    local a = sub[k]
                    local b = sub[k+1]
                    if h2h[a.id].pts == h2h[b.id].pts and h2h[a.id].gd == h2h[b.id].gd and h2h[a.id].gf == h2h[b.id].gf then
                        local order_a = tie_order[a.code]
                        local order_b = tie_order[b.code]
                        if not (order_a and order_b and order_a ~= order_b) then
                            a.unresolved_tie = true
                            b.unresolved_tie = true
                        end
                    end
                end
                
                for k = i, j do sorted_teams[k] = sub[k - i + 1] end
            end
            i = j + 1
        end
    end

    local tbl = mw.html.create('table')
        :attr('border', '1')
        :attr('cellspacing', '1')
        :attr('cellpadding', '1')
        :addClass('article-table')
        :css('width', '500px')

    local tr_head = tbl:tag('tr')
    tr_head:tag('th'):attr('scope', 'col'):wikitext('Место')
    tr_head:tag('th'):attr('scope', 'col'):wikitext('')
    tr_head:tag('th'):attr('scope', 'col'):wikitext('Команда')
    for i = 1, n do tr_head:tag('th'):attr('scope', 'col'):wikitext(tostring(i)) end
    
    if not is_compact then
        tr_head:tag('th'):attr('scope', 'col'):attr('title', 'Игры'):wikitext('И')
        tr_head:tag('th'):attr('scope', 'col'):attr('title', 'Выигрыши'):wikitext('В')
        tr_head:tag('th'):attr('scope', 'col'):attr('title', 'Ничьи'):wikitext('Н')
        tr_head:tag('th'):attr('scope', 'col'):attr('title', 'Поражения'):wikitext('П')
    end
    
    tr_head:tag('th'):attr('scope', 'col'):attr('title', 'Забитые и пропущенные мячи'):wikitext('М')
    tr_head:tag('th'):attr('scope', 'col'):attr('title', 'Очки'):wikitext('О')

    for rank, t in ipairs(sorted_teams) do
        local r = tbl:tag('tr')
        
        local rank_str = tostring(rank)
        if t.is_bold then rank_str = "'''" .. rank_str .. "'''" end
        local td_rank = r:tag('td'):attr('align', 'center'):css('white-space', 'nowrap'):wikitext(rank_str)
        if t.color then td_rank:css('background-color', t.color) end
        
        local td_flag = r:tag('td'):attr('align', 'center'):css('white-space', 'nowrap')
            :wikitext(string.format('[[Файл:%s.jpg|50x100px]]', t.name))
        if t.color then td_flag:css('background-color', t.color) end
        
        local name_str = string.format('[[%s]]', t.name)
        if t.is_bold then name_str = "'''" .. name_str .. "'''" end
        if t.unresolved_tie then name_str = highlight_tie(name_str) end
        
        local td_name = r:tag('td'):attr('align', 'center'):css('white-space', 'nowrap'):wikitext(name_str)
        if t.color then td_name:css('background-color', t.color) end
        
        for j = 1, n do
            local opp = sorted_teams[j]
            local td_match = r:tag('td'):attr('align', 'center')
            if t.id == opp.id then
                td_match:css('background-color', 'grey')
            else
                local m = grid[t.id][opp.id]
                if m.played or m.text ~= "" then td_match:wikitext(m.text) end
            end
        end
        
        if not is_compact then
            r:tag('td'):attr('align', 'center'):wikitext(tostring(t.p))
            r:tag('td'):attr('align', 'center'):wikitext(tostring(t.w))
            r:tag('td'):attr('align', 'center'):wikitext(tostring(t.d))
            r:tag('td'):attr('align', 'center'):wikitext(tostring(t.l))
        end
        
        local goals_str = string.format('%d-%d', t.gf, t.ga)
        if t.unresolved_tie then goals_str = highlight_tie(goals_str) end
        r:tag('td'):attr('align', 'center'):css('white-space', 'nowrap'):wikitext(goals_str)
        
        local td_pts = r:tag('td'):attr('align', 'center')
        if t.unresolved_tie then
            td_pts:wikitext(highlight_tie(tostring(t.pts)))
        else
            td_pts:tag('b'):wikitext(tostring(t.pts))
        end
    end

    return tostring(tbl)
end

function p.group(frame)
    return p._group(getArgsFromFrame(frame))
end

-- =======================================
-- СВОДНЫЙ РЕЙТИНГ
-- =======================================
function p._rating(args)
    local title = cleanParam(args.title) or 'Рейтинг команд'

    local tbl = mw.html.create('table')
        :addClass('wikitable mw-collapsible mw-collapsed')

    -- Заголовок таблицы
    tbl:tag('tr')
        :tag('th'):attr('colspan', '4'):wikitext(title)

    -- Заголовки столбцов
    local tr_head = tbl:tag('tr')
    tr_head:tag('td'):css('white-space', 'nowrap'):css('text-align', 'center'):wikitext("'''Место'''")
    tr_head:tag('td'):css('white-space', 'nowrap'):css('text-align', 'center'):wikitext("'''Команда'''")
    tr_head:tag('td'):css('white-space', 'nowrap'):css('text-align', 'center'):wikitext("'''О'''")
    tr_head:tag('td'):css('white-space', 'nowrap'):css('text-align', 'center'):wikitext("'''М'''")

    local n = 1
    while cleanParam(args['team' .. n]) do
        local code = cleanParam(args['team' .. n])
        local name = teamsData.getName(code, 'short')
        local pts = cleanParam(args['pts' .. n]) or '0'
        local gf = cleanParam(args['gf' .. n]) or '0'
        local ga = cleanParam(args['ga' .. n]) or '0'
        local color = cleanParam(args['color' .. n])
        
        local is_tied = (cleanParam(args['err' .. n]) == "yes")

        local r = tbl:tag('tr')

        -- Функция-помощник для ячеек
        local function addCell(content)
            local td = r:tag('td'):css('white-space', 'nowrap'):css('text-align', 'center')
            if color then td:css('background-color', color) end
            td:wikitext(content)
        end
        
        local name_str = string.format('[[%s]]', name)
        if is_tied then name_str = highlight_tie(name_str) end
        
        local pts_str = tostring(pts)
        if is_tied then pts_str = highlight_tie(pts_str) end
        
        local goals_str = string.format('%s-%s', gf, ga)
        if is_tied then goals_str = highlight_tie(goals_str) end

        addCell(tostring(n))
        addCell(name_str)
        addCell(pts_str)
        addCell(goals_str)

        n = n + 1
    end

    return tostring(tbl)
end

function p.rating(frame)
    return p._rating(getArgsFromFrame(frame))
end

return p