Модуль:CargoConverter: различия между версиями

Материал из ЧТМ
Перейти к навигации Перейти к поиску
Новая страница: «local p = {} -- Вспомогательная функция для безопасного вывода local function safe(val) if val == nil then return "" end if type(val) == "boolean" then return val and "1" or "0" end return tostring(val) end -- ========================================================= -- 1. КОНВЕРТЕР ПОДРОБНЫХ МАТЧЕЙ (из Модуль:Data/Год) -- ==============================================...»
 
Нет описания правки
Строка 6: Строка 6:
     if type(val) == "boolean" then return val and "1" or "0" end
     if type(val) == "boolean" then return val and "1" or "0" end
     return tostring(val)
     return tostring(val)
end
-- Функция умной сортировки Match ID (с учётом чисел)
local function get_sorted_match_ids(data)
    local keys = {}
    for k in pairs(data) do
        table.insert(keys, k)
    end
   
    table.sort(keys, function(a, b)
        -- Извлекаем год и номер матча (например, "2046" и "3" из "2046-03")
        local ya, na = string.match(a, "^(%d+)-(%d+)")
        local yb, nb = string.match(b, "^(%d+)-(%d+)")
       
        if ya and yb and na and nb then
            local tya, tna = tonumber(ya), tonumber(na)
            local tyb, tnb = tonumber(yb), tonumber(nb)
            if tya ~= tyb then
                return tya < tyb
            else
                return tna < tnb
            end
        end
        return a < b -- если формат другой, сортируем просто по алфавиту
    end)
   
    return keys
end
end


Строка 15: Строка 42:
     local data = mw.loadData('Модуль:Data/' .. year)
     local data = mw.loadData('Модуль:Data/' .. year)
     local out = {}
     local out = {}
 
   
     local tourn_id = "ЧТМ_" .. year .. "_Final"
     local tourn_id = "ЧТМ_" .. year .. "_Final"
 
   
     for match_id, m in pairs(data) do
    -- Получаем отсортированный список ключей
    local sorted_keys = get_sorted_match_ids(data)
   
     for _, match_id in ipairs(sorted_keys) do
        local m = data[match_id]
       
         -- 1. Базовый матч
         -- 1. Базовый матч
         table.insert(out, string.format(
         table.insert(out, string.format(
Строка 28: Строка 60:
             safe(m.video), safe(m.vk_video), safe(m.review_url), safe(m.vk_review_url), safe(m.review_time), safe(m.wikilink), safe(m.comment)
             safe(m.video), safe(m.vk_video), safe(m.review_url), safe(m.vk_review_url), safe(m.review_time), safe(m.wikilink), safe(m.comment)
         ))
         ))
 
       
         -- 2. Составы (TWC_Lineups)
         -- 2. Составы (TWC_Lineups)
         local function parse_squad(sq, t_idx)
         local function parse_squad(sq, t_idx)
Строка 48: Строка 80:
         parse_squad(m.squad2, 2)
         parse_squad(m.squad2, 2)
         parse_squad(m.neutral_gk, 0)
         parse_squad(m.neutral_gk, 0)
 
       
         -- 3. Голы (TWC_Goals)
         -- 3. Голы (TWC_Goals)
         if m.goals then
         if m.goals then
Строка 56: Строка 88:
             end
             end
         end
         end
 
       
         -- 4. Замены (TWC_Subs)
         -- 4. Замены (TWC_Subs)
         if m.subs then
         if m.subs then
Строка 64: Строка 96:
             end
             end
         end
         end
 
       
         -- 5. Карточки (TWC_Cards)
         -- 5. Карточки (TWC_Cards)
         if m.cards then
         if m.cards then
Строка 72: Строка 104:
             end
             end
         end
         end
 
       
         -- 6. Промахи пенальти и Выносы
         -- 6. Промахи пенальти и Выносы
         if m.missed_pens then
         if m.missed_pens then
Строка 84: Строка 116:
             end
             end
         end
         end
 
       
         -- 7. Награды и MVP
         -- 7. Награды и MVP
         if m.mvp and m.mvp.player then
         if m.mvp and m.mvp.player then
             table.insert(out, string.format("{{TWC_Awards|ContextID=%s|AwardType=MVP|Player=%s|TeamIndex=%s|Role=%s}}", match_id, safe(m.mvp.player), safe(m.mvp.team), safe(m.mvp.role)))
             table.insert(out, string.format("{{TWC_Awards|ContextID=%s|AwardType=MVP|Player=%s|TeamIndex=%s|Role=%s}}", match_id, safe(m.mvp.player), safe(m.mvp.team), safe(m.mvp.role)))
         end
         end
 
       
         -- Призы турнира (если финал, записываются в последний матч или можно отдельно, но оставим тут)
         -- Призы турнира
         local awards_map = {golden_sphere="GoldenSphere", silver_sphere="SilverSphere", bronze_sphere="BronzeSphere", wooden_sphere="WoodenSphere", elnur_award="ElnurAward", best_goal="BestGoal", golden_shoe="GoldenShoe", silver_shoe="SilverShoe", bronze_shoe="BronzeShoe", wooden_shoe="WoodenShoe", golden_assistant="GoldenAssistant", silver_assistant="SilverAssistant", bronze_assistant="BronzeAssistant", wooden_assistant="WoodenAssistant", superchampions="Superchampion"}
         local awards_map = {golden_sphere="GoldenSphere", silver_sphere="SilverSphere", bronze_sphere="BronzeSphere", wooden_sphere="WoodenSphere", elnur_award="ElnurAward", best_goal="BestGoal", golden_shoe="GoldenShoe", silver_shoe="SilverShoe", bronze_shoe="BronzeShoe", wooden_shoe="WoodenShoe", golden_assistant="GoldenAssistant", silver_assistant="SilverAssistant", bronze_assistant="BronzeAssistant", wooden_assistant="WoodenAssistant", superchampions="Superchampion"}
 
       
         for k, v in pairs(awards_map) do
         for k, v in pairs(awards_map) do
             if m[k] then
             if m[k] then
Строка 104: Строка 136:
             end
             end
         end
         end
 
       
         table.insert(out, "") -- пустая строка для разделения матчей
         table.insert(out, "") -- пустая строка для разделения матчей
     end
     end
 
   
     return "<pre><nowiki>\n" .. table.concat(out, "\n") .. "\n</nowiki></pre>"
     return "<pre><nowiki>\n" .. table.concat(out, "\n") .. "\n</nowiki></pre>"
end
end
Строка 118: Строка 150:
     local data = mw.loadData('Модуль:Data/Tournaments/' .. year)
     local data = mw.loadData('Модуль:Data/Tournaments/' .. year)
     local out = {}
     local out = {}
 
   
     for tourn_name, stages in pairs(data) do
    -- Сортируем названия турниров по алфавиту
    local sorted_tournaments = {}
     for t_name in pairs(data) do
        table.insert(sorted_tournaments, t_name)
    end
    table.sort(sorted_tournaments)
   
    for _, tourn_name in ipairs(sorted_tournaments) do
        local stages = data[tourn_name]
         table.insert(out, string.format("=== %s ===", tourn_name))
         table.insert(out, string.format("=== %s ===", tourn_name))
 
       
         for stage_name, stage_data in pairs(stages) do
        -- Сортируем стадии турнира по алфавиту
        local sorted_stages = {}
         for s_name in pairs(stages) do
            table.insert(sorted_stages, s_name)
        end
        table.sort(sorted_stages)
       
        for _, stage_name in ipairs(sorted_stages) do
            local stage_data = stages[stage_name]
           
             -- 1. Итоговые таблицы групп
             -- 1. Итоговые таблицы групп
             if stage_data.standings then
             if stage_data.standings then
Строка 130: Строка 179:
                 end
                 end
             end
             end
 
           
             -- 2. Матчи турнира
             -- 2. Матчи турнира
             if stage_data.matches then
             if stage_data.matches then
                 for i, m in ipairs(stage_data.matches) do
                 for i, m in ipairs(stage_data.matches) do
                    -- Генерируем искусственный ID: ТУРНИР_СТАДИЯ_МАТЧ(НОМЕР)
                     local base_match_id = tourn_name .. "_" .. stage_name .. "_M" .. i
                     local base_match_id = tourn_name .. "_" .. stage_name .. "_M" .. i
 
                   
                     if stage_data.type == "group" then
                     if stage_data.type == "group" then
                        -- Группа: t1, t2, s1, s2, field
                         table.insert(out, string.format("{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|FieldAdvantage=%s}}",
                         table.insert(out, string.format("{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|FieldAdvantage=%s}}",
                         base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4]), safe(m[5])))
                         base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4]), safe(m[5])))
 
                       
                     elseif stage_data.type == "knockout" then
                     elseif stage_data.type == "knockout" then
                         if stage_data.number_of_rounds == 1 then
                         if stage_data.number_of_rounds == 1 then
                            -- Плей-офф 1 матч: t1, t2, s1, s2, aet_flag, pen1, pen2, color1, color2, field
                             local aet = (m[5] == "aet")
                             local aet = (m[5] == "aet")
                             table.insert(out, string.format("{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|ShootoutScore1=%s|ShootoutScore2=%s|Color1=%s|Color2=%s|FieldAdvantage=%s}}",
                             table.insert(out, string.format("{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|ShootoutScore1=%s|ShootoutScore2=%s|Color1=%s|Color2=%s|FieldAdvantage=%s}}",
                             base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4]), safe(aet), safe(m[6]), safe(m[7]), safe(m[8]), safe(m[9]), safe(m[10])))
                             base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4]), safe(aet), safe(m[6]), safe(m[7]), safe(m[8]), safe(m[9]), safe(m[10])))
                         else
                         else
                             -- Плей-офф 2 матча! Разделяем на ДВА матча
                             -- Первый матч
                            -- m = {t1, t2, s1_1, s2_1, s1_2, s2_2, aet, pen1, pen2, color1, color2}
 
                            -- Матч 1 (на поле Team1)
                             table.insert(out, string.format("{{TWC_Match|MatchID=%s_L1|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|FieldAdvantage=1}}",
                             table.insert(out, string.format("{{TWC_Match|MatchID=%s_L1|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|FieldAdvantage=1}}",
                             base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4])))
                             base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4])))
 
                           
                             -- Матч 2 (на поле Team2, здесь же пенальти и AET)
                             -- Второй матч
                             local aet = (m[7] == "aet")
                             local aet = (m[7] == "aet")
                             table.insert(out, string.format("{{TWC_Match|MatchID=%s_L2|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|ShootoutScore1=%s|ShootoutScore2=%s|Color1=%s|Color2=%s|FieldAdvantage=2}}",
                             table.insert(out, string.format("{{TWC_Match|MatchID=%s_L2|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|ShootoutScore1=%s|ShootoutScore2=%s|Color1=%s|Color2=%s|FieldAdvantage=2}}",
                             base_match_id, safe(tourn_name), safe(stage_name), safe(m[2]), safe(m[1]), safe(m[6]), safe(m[5]), safe(aet), safe(m[9]), safe(m[8]), safe(m[11]), safe(m[10])))  
                             base_match_id, safe(tourn_name), safe(stage_name), safe(m[2]), safe(m[1]), safe(m[6]), safe(m[5]), safe(aet), safe(m[9]), safe(m[8]), safe(m[11]), safe(m[10])))  
                            -- Обратите внимание: Score1/Score2 и Pen1/Pen2 перевёрнуты для второго матча!
                         end
                         end
                     end
                     end
Строка 168: Строка 210:
         end
         end
     end
     end
 
   
     return "<pre><nowiki>\n" .. table.concat(out, "\n") .. "\n</nowiki></pre>"
     return "<pre><nowiki>\n" .. table.concat(out, "\n") .. "\n</nowiki></pre>"
end
end


return p
return p

Версия от 12:29, 27 мая 2026

Инструкция

Этот скрипт не перезаписывает базу сам, он выступает в роли "переводчика". Он читает старый Lua и выдаёт вам готовый текст, который Cargo поймёт.

Действие 1: Конвертация финальных матчей ЧТМ (Подробных)

  1. Создайте любую черновую страницу (например, Песочница_Cargo).
  2. Напишите там следующий код и нажмите «Предварительный просмотр»: {{#invoke:CargoConverter|runDetailed|2046}} (Вместо 2046 ставьте нужный год).
  3. Прямо в окне предпросмотра вы увидите огромный блок текста, начинающийся с <pre>. Внутри будут тысячи готовых строчек вида {{TWC_Match|...}}, {{TWC_Lineups|...}}.
  4. Выделите этот текст, скопируйте его.
  5. Создайте страницу, где эти данные будут жить вечно (например, Данные:2046_Финал) и вставьте туда скопированный текст. Сохраните. База Cargo проглотила матчи!

Действие 2: Конвертация сеток турниров

  1. Возвращаетесь в Песочницу.
  2. Теперь вызываете вторую функцию: {{#invoke:CargoConverter|runTournaments|2046}}
  3. Снова "Предварительный просмотр". Теперь скрипт прочитает Модуль:Data/Tournaments/2046, разобьёт 2-матчевые противостояния на два отдельных матча (с пометками _L1 и _L2), расставит поля и цвета.
  4. Копируете выданный текст.
  5. Создаёте страницу (например, Данные:2046_Турниры) и вставляете текст туда. Сохраняете. Cargo проглотил сетки!

Действие 3: Заполняем Справочник Команд (TWC_Teams)

  1. Зайдите в вашу Песочницу.
  2. Вставьте вызов: {{#invoke:CargoConverter|runTeams}}
  3. Сделайте «Предварительный просмотр». Скрипт выдаст стройную таблицу вызовов {{TWC_Teams|...}} для всех стран из словаря.
  4. Скопируйте этот текст.
  5. Создайте страницу Данные:Команды_БД (или любое удобное вам название), вставьте текст туда и сохраните.
  6. Сделайте на ней «нулевую правку» (сохраните пустой), чтобы запустить запись в Cargo [1.1.6, 1.2.6]. Справочник команд заполнен!

Действие 4: Заполняем Справочник Турниров (TWC_Tournaments)

  1. В Песочнице напишите: {{#invoke:CargoConverter|runTournamentsList}}
  2. Нажмите «Предварительный просмотр». Модуль сам прошерстит файлы за 2006, 2009, 2010... 2048 годы, соберёт все когда-либо существовавшие турниры и сгенерирует красивый список вызовов {{TWC_Tournaments|...}}.
  3. Скопируйте результат.
  4. Создайте страницу Данные:Турниры_БД, вставьте текст туда и сохраните.
  5. Сделайте «нулевую правку» [1.1.6, 1.2.6]. Метаданные турниров записаны!

(Конечно, вы можете вставлять оба результата на одну большую страницу Данные:2046, если захотите — Cargo не против).


local p = {}

-- Вспомогательная функция для безопасного вывода
local function safe(val)
    if val == nil then return "" end
    if type(val) == "boolean" then return val and "1" or "0" end
    return tostring(val)
end

-- Функция умной сортировки Match ID (с учётом чисел)
local function get_sorted_match_ids(data)
    local keys = {}
    for k in pairs(data) do
        table.insert(keys, k)
    end
    
    table.sort(keys, function(a, b)
        -- Извлекаем год и номер матча (например, "2046" и "3" из "2046-03")
        local ya, na = string.match(a, "^(%d+)-(%d+)")
        local yb, nb = string.match(b, "^(%d+)-(%d+)")
        
        if ya and yb and na and nb then
            local tya, tna = tonumber(ya), tonumber(na)
            local tyb, tnb = tonumber(yb), tonumber(nb)
            if tya ~= tyb then
                return tya < tyb
            else
                return tna < tnb
            end
        end
        return a < b -- если формат другой, сортируем просто по алфавиту
    end)
    
    return keys
end

-- =========================================================
-- 1. КОНВЕРТЕР ПОДРОБНЫХ МАТЧЕЙ (из Модуль:Data/Год)
-- =========================================================
function p.runDetailed(frame)
    local year = frame.args[1] or "2046"
    local data = mw.loadData('Модуль:Data/' .. year)
    local out = {}
    
    local tourn_id = "ЧТМ_" .. year .. "_Final"
    
    -- Получаем отсортированный список ключей
    local sorted_keys = get_sorted_match_ids(data)
    
    for _, match_id in ipairs(sorted_keys) do
        local m = data[match_id]
        
        -- 1. Базовый матч
        table.insert(out, string.format(
            "{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|GroupLetter=%s|Tour=%s|RealDate=%s|Matchday=%s|NumHist=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|Stadium=%s|Players1=%s|Players2=%s|Gates=%s|ThrowIns=%s|Halfs=%s|HalfTime=%s|ExtraTime=%s|ET_Halfs=%s|ET_HalfTime=%s|SubsRules=%s|Video=%s|VkVideo=%s|ReviewUrl=%s|VkReviewUrl=%s|ReviewTime=%s|WikiLink=%s|Comment=%s}}",
            safe(match_id), safe(tourn_id), safe(m.stage), safe(m.letter), safe(m.tour), safe(m.date), safe(m.matchday), safe(m.num_hist),
            safe(m.team1), safe(m.team2), safe(m.score1), safe(m.score2), safe(m.aet), safe(m.stadium),
            safe(m.players1), safe(m.players2), safe(m.gates), safe(m.throw_ins), safe(m.halfs), safe(m.half_time),
            safe(m.extra_time), safe(m.et_halfs), safe(m.et_half_time), safe(m.subs_rules),
            safe(m.video), safe(m.vk_video), safe(m.review_url), safe(m.vk_review_url), safe(m.review_time), safe(m.wikilink), safe(m.comment)
        ))
        
        -- 2. Составы (TWC_Lineups)
        local function parse_squad(sq, t_idx)
            if not sq then return end
            if sq.starters then
                for _, pl in ipairs(sq.starters) do
                    local is_cap = (pl == sq.captain)
                    local is_gk = (pl == sq.full_match_goalie)
                    table.insert(out, string.format("{{TWC_Lineups|MatchID=%s|Player=%s|TeamIndex=%s|Role=starter|IsCaptain=%s|IsFullMatchGoalie=%s}}", match_id, pl, t_idx, safe(is_cap), safe(is_gk)))
                end
            end
            if sq.substitutes then
                for _, pl in ipairs(sq.substitutes) do
                    table.insert(out, string.format("{{TWC_Lineups|MatchID=%s|Player=%s|TeamIndex=%s|Role=sub|IsCaptain=0|IsFullMatchGoalie=0}}", match_id, pl, t_idx))
                end
            end
        end
        parse_squad(m.squad1, 1)
        parse_squad(m.squad2, 2)
        parse_squad(m.neutral_gk, 0)
        
        -- 3. Голы (TWC_Goals)
        if m.goals then
            for _, g in ipairs(m.goals) do
                table.insert(out, string.format("{{TWC_Goals|MatchID=%s|TeamIndex=%s|Scorer=%s|OwnScorer=%s|Assist=%s|ScoreAfter=%s|Minute=%s|GoalType1=%s|GoalType2=%s|IsET=%s|Fouler=%s}}",
                match_id, safe(g.team), safe(g.scorer), safe(g.own_scorer), safe(g.assist), safe(g.score), safe(g.min), safe(g.goal_type), safe(g.goal_type2), safe(g.et_goal), safe(g.fouler)))
            end
        end
        
        -- 4. Замены (TWC_Subs)
        if m.subs then
            for _, s in ipairs(m.subs) do
                table.insert(out, string.format("{{TWC_Subs|MatchID=%s|TeamIndex=%s|PlayerOut=%s|PlayerIn=%s|ScoreAfter=%s|Minute=%s}}",
                match_id, safe(s.team), safe(s.player_out), safe(s.player_in), safe(s.score), safe(s.min)))
            end
        end
        
        -- 5. Карточки (TWC_Cards)
        if m.cards then
            for _, c in ipairs(m.cards) do
                table.insert(out, string.format("{{TWC_Cards|MatchID=%s|TeamIndex=%s|Player=%s|Color=%s|ScoreAfter=%s|Minute=%s|IsReturned=%s|ReturnScore=%s|ReturnMin=%s|Reason=%s}}",
                match_id, safe(c.team), safe(c.player), safe(c.color), safe(c.score), safe(c.min), safe(c.returned), safe(c.return_score), safe(c.return_min), safe(c.reason)))
            end
        end
        
        -- 6. Промахи пенальти и Выносы
        if m.missed_pens then
            for _, p in ipairs(m.missed_pens) do
                table.insert(out, string.format("{{TWC_MissedPens|MatchID=%s|TeamIndex=%s|Taker=%s|Result=%s|Goalie=%s|Fouler=%s|ScoreAfter=%s|Minute=%s}}", match_id, safe(p.team), safe(p.taker), safe(p.result), safe(p.goalie), safe(p.fouler), safe(p.score), safe(p.min)))
            end
        end
        if m.clearances then
            for _, cl in ipairs(m.clearances) do
                table.insert(out, string.format("{{TWC_Clearances|MatchID=%s|TeamIndex=%s|Player=%s|ScoreAfter=%s|Minute=%s}}", match_id, safe(cl.team), safe(cl.player), safe(cl.score), safe(cl.min)))
            end
        end
        
        -- 7. Награды и MVP
        if m.mvp and m.mvp.player then
            table.insert(out, string.format("{{TWC_Awards|ContextID=%s|AwardType=MVP|Player=%s|TeamIndex=%s|Role=%s}}", match_id, safe(m.mvp.player), safe(m.mvp.team), safe(m.mvp.role)))
        end
        
        -- Призы турнира
        local awards_map = {golden_sphere="GoldenSphere", silver_sphere="SilverSphere", bronze_sphere="BronzeSphere", wooden_sphere="WoodenSphere", elnur_award="ElnurAward", best_goal="BestGoal", golden_shoe="GoldenShoe", silver_shoe="SilverShoe", bronze_shoe="BronzeShoe", wooden_shoe="WoodenShoe", golden_assistant="GoldenAssistant", silver_assistant="SilverAssistant", bronze_assistant="BronzeAssistant", wooden_assistant="WoodenAssistant", superchampions="Superchampion"}
        
        for k, v in pairs(awards_map) do
            if m[k] then
                if type(m[k]) == "table" then
                    for _, p in ipairs(m[k]) do
                        table.insert(out, string.format("{{TWC_Awards|ContextID=%s|AwardType=%s|Player=%s}}", tourn_id, v, p))
                    end
                else
                    table.insert(out, string.format("{{TWC_Awards|ContextID=%s|AwardType=%s|Player=%s}}", tourn_id, v, m[k]))
                end
            end
        end
        
        table.insert(out, "") -- пустая строка для разделения матчей
    end
    
    return "<pre><nowiki>\n" .. table.concat(out, "\n") .. "\n</nowiki></pre>"
end

-- =========================================================
-- 2. КОНВЕРТЕР ТУРНИРОВ (из Модуль:Data/Tournaments/Год)
-- =========================================================
function p.runTournaments(frame)
    local year = frame.args[1] or "2046"
    local data = mw.loadData('Модуль:Data/Tournaments/' .. year)
    local out = {}
    
    -- Сортируем названия турниров по алфавиту
    local sorted_tournaments = {}
    for t_name in pairs(data) do
        table.insert(sorted_tournaments, t_name)
    end
    table.sort(sorted_tournaments)
    
    for _, tourn_name in ipairs(sorted_tournaments) do
        local stages = data[tourn_name]
        table.insert(out, string.format("=== %s ===", tourn_name))
        
        -- Сортируем стадии турнира по алфавиту
        local sorted_stages = {}
        for s_name in pairs(stages) do
            table.insert(sorted_stages, s_name)
        end
        table.sort(sorted_stages)
        
        for _, stage_name in ipairs(sorted_stages) do
            local stage_data = stages[stage_name]
            
            -- 1. Итоговые таблицы групп
            if stage_data.standings then
                for pos, st in ipairs(stage_data.standings) do
                    table.insert(out, string.format("{{TWC_StageTeams|TournamentID=%s|Stage=%s|Team=%s|Position=%s|ColorCode=%s}}",
                    safe(tourn_name), safe(stage_name), safe(st[1]), pos, safe(st[2])))
                end
            end
            
            -- 2. Матчи турнира
            if stage_data.matches then
                for i, m in ipairs(stage_data.matches) do
                    local base_match_id = tourn_name .. "_" .. stage_name .. "_M" .. i
                    
                    if stage_data.type == "group" then
                        table.insert(out, string.format("{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|FieldAdvantage=%s}}",
                        base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4]), safe(m[5])))
                        
                    elseif stage_data.type == "knockout" then
                        if stage_data.number_of_rounds == 1 then
                            local aet = (m[5] == "aet")
                            table.insert(out, string.format("{{TWC_Match|MatchID=%s|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|ShootoutScore1=%s|ShootoutScore2=%s|Color1=%s|Color2=%s|FieldAdvantage=%s}}",
                            base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4]), safe(aet), safe(m[6]), safe(m[7]), safe(m[8]), safe(m[9]), safe(m[10])))
                        else
                            -- Первый матч
                            table.insert(out, string.format("{{TWC_Match|MatchID=%s_L1|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|FieldAdvantage=1}}",
                            base_match_id, safe(tourn_name), safe(stage_name), safe(m[1]), safe(m[2]), safe(m[3]), safe(m[4])))
                            
                            -- Второй матч
                            local aet = (m[7] == "aet")
                            table.insert(out, string.format("{{TWC_Match|MatchID=%s_L2|TournamentID=%s|Stage=%s|Team1=%s|Team2=%s|Score1=%s|Score2=%s|AET=%s|ShootoutScore1=%s|ShootoutScore2=%s|Color1=%s|Color2=%s|FieldAdvantage=2}}",
                            base_match_id, safe(tourn_name), safe(stage_name), safe(m[2]), safe(m[1]), safe(m[6]), safe(m[5]), safe(aet), safe(m[9]), safe(m[8]), safe(m[11]), safe(m[10]))) 
                        end
                    end
                end
            end
            table.insert(out, "")
        end
    end
    
    return "<pre><nowiki>\n" .. table.concat(out, "\n") .. "\n</nowiki></pre>"
end

return p