Modul:Ewige Gegner-Scorer gegen Kassel
Die Dokumentation für dieses Modul kann unter Modul:Ewige Gegner-Scorer gegen Kassel/Doku erstellt werden
local p = {}
local TEAM_CODES = {
'AEV', 'BCP', 'BDP', 'BHV', 'BIE', 'BTT', 'DEG', 'DGF', 'DOR', 'DRE', 'DUI',
'EBB', 'EBR', 'ECN', 'ESS', 'ESVK', 'ETC', 'EVL', 'FRA', 'FRB', 'HAL', 'HER',
'HHF', 'HNF', 'HSC', 'IEC', 'ING', 'KEC', 'KRE', 'LFX', 'MAN', 'MUB', 'MUC',
'NBG', 'RAT', 'RAV', 'RLO', 'SBR', 'SCM', 'SCR', 'SEL', 'SWW', 'TIM', 'TÖL',
'WEI', 'WOB'
}
local function trim(value)
return mw.text.trim(value or '')
end
local function getArg(frame, name)
local value = trim(frame.args[name])
if value ~= '' then
return value
end
local parent = frame:getParent()
if parent then
return trim(parent.args[name])
end
return ''
end
local function splitList(value)
local result = {}
for item in trim(value):gmatch('[^,]+') do
item = trim(item)
if item ~= '' then
table.insert(result, item)
end
end
return result
end
local function uniqueInsert(list, seen, value)
value = trim(value)
if value ~= '' and not seen[value] then
seen[value] = true
table.insert(list, value)
end
end
local function stripPlayerSuffix(title)
title = trim(title)
title = title:gsub('%s+%(Gastspieler.-%)$', '')
title = title:gsub('%s+%(Spieler.-%)$', '')
title = title:gsub('%s+%(Torhüter.-%)$', '')
title = title:gsub('%s+%(Stürmer.-%)$', '')
title = title:gsub('%s+%(Verteidiger.-%)$', '')
return trim(title)
end
local function germanize(text)
local replacements = {
['Ä'] = 'Ae', ['ä'] = 'ae',
['Ö'] = 'Oe', ['ö'] = 'oe',
['Ü'] = 'Ue', ['ü'] = 'ue',
['ß'] = 'ss'
}
for from, to in pairs(replacements) do
text = mw.ustring.gsub(text, from, to)
end
return text
end
local function latinize(text)
local replacements = {
['Ä'] = 'A', ['ä'] = 'a',
['Ö'] = 'O', ['ö'] = 'o',
['Ü'] = 'U', ['ü'] = 'u',
['ß'] = 'ss',
['Á'] = 'A', ['á'] = 'a',
['À'] = 'A', ['à'] = 'a',
['Â'] = 'A', ['â'] = 'a',
['Č'] = 'C', ['č'] = 'c',
['Ć'] = 'C', ['ć'] = 'c',
['Ď'] = 'D', ['ď'] = 'd',
['É'] = 'E', ['é'] = 'e',
['Ě'] = 'E', ['ě'] = 'e',
['Í'] = 'I', ['í'] = 'i',
['Ĺ'] = 'L', ['ĺ'] = 'l',
['Ľ'] = 'L', ['ľ'] = 'l',
['Ň'] = 'N', ['ň'] = 'n',
['Ó'] = 'O', ['ó'] = 'o',
['Ô'] = 'O', ['ô'] = 'o',
['Ř'] = 'R', ['ř'] = 'r',
['Š'] = 'S', ['š'] = 's',
['Ť'] = 'T', ['ť'] = 't',
['Ú'] = 'U', ['ú'] = 'u',
['Ů'] = 'U', ['ů'] = 'u',
['Ý'] = 'Y', ['ý'] = 'y',
['Ž'] = 'Z', ['ž'] = 'z'
}
for from, to in pairs(replacements) do
text = mw.ustring.gsub(text, from, to)
end
return text
end
local function makeTextVariants(text)
local base = trim((text or ''):gsub('_', ' '))
local variants = {}
local seen = {}
uniqueInsert(variants, seen, base)
uniqueInsert(variants, seen, germanize(base))
uniqueInsert(variants, seen, latinize(base))
return variants
end
local function getRawPagesInCategory(frame, categoryName)
categoryName = trim(categoryName)
if categoryName == '' then
return 0
end
local ok, count = pcall(function()
return mw.site.stats.pagesInCategory(categoryName, 'pages')
end)
if ok and type(count) == 'number' then
return count
end
local raw = frame:preprocess('{{SEITEN_IN_KATEGORIE:' .. categoryName .. '|pages}}')
return tonumber(trim(raw)) or 0
end
local function getCategoryMembers(category)
if not (mw.ext and mw.ext.dpl and type(mw.ext.dpl.getPagenames) == 'function') then
return {}
end
local ok, pages = pcall(mw.ext.dpl.getPagenames, {
category = trim(category):gsub('^Kategorie:%s*', ''),
namespace = 'main',
redirects = 'exclude',
ordermethod = 'sortkey',
order = 'ascending',
count = 5000
})
if not ok then
return {}
end
return pages or {}
end
local function addSourcePlayers(players, seen, sourceCategories)
for _, category in ipairs(splitList(sourceCategories)) do
for _, pageName in ipairs(getCategoryMembers(category)) do
local name = stripPlayerSuffix(pageName)
if name ~= '' and not seen[name] then
seen[name] = { page = pageName, name = name }
table.insert(players, seen[name])
end
end
end
end
local function addExtraPlayers(players, seen, extraPlayers)
for _, name in ipairs(splitList(extraPlayers)) do
if not seen[name] then
seen[name] = { page = name, name = name }
table.insert(players, seen[name])
end
end
end
local function makeCategoryName(season, value, statKey, playerName, competition)
local head
if statKey == 'GP' then
head = trim(season .. ' GP ' .. playerName .. ' VS-Kassel')
else
head = trim(season .. ' ' .. tostring(value) .. ' ' .. statKey .. ' ' .. playerName .. ' VS-Kassel')
end
if trim(competition) ~= '' then
head = head .. ' ' .. trim(competition)
end
return head
end
local function countFirstVariant(frame, names)
for _, name in ipairs(names) do
local count = getRawPagesInCategory(frame, name)
if count > 0 then
return count
end
end
return 0
end
local function countPlayerCategory(frame, season, value, statKey, playerName, competition)
local candidates = {}
local seen = {}
for _, variant in ipairs(makeTextVariants(playerName)) do
uniqueInsert(candidates, seen, makeCategoryName(season, value, statKey, variant, competition))
end
return countFirstVariant(frame, candidates)
end
local function sumStat(frame, seasons, competitions, playerName, statKey, maxValue)
local total = 0
for _, season in ipairs(seasons) do
for _, competition in ipairs(competitions) do
for value = 1, maxValue do
local count = countPlayerCategory(frame, season, value, statKey, playerName, competition)
total = total + (value * count)
end
end
end
return total
end
local function sumGames(frame, seasons, competitions, playerName)
local total = 0
for _, season in ipairs(seasons) do
for _, competition in ipairs(competitions) do
total = total + countPlayerCategory(frame, season, 0, 'GP', playerName, competition)
end
end
return total
end
local function addTeam(target, code)
code = trim(code)
if code == '' then
return
end
for _, existing in ipairs(target.teams) do
if existing == code then
return
end
end
table.insert(target.teams, code)
end
local function addDynamicTeams(frame, row, seasons, competitions, playerName)
for _, season in ipairs(seasons) do
for _, competition in ipairs(competitions) do
for _, code in ipairs(TEAM_CODES) do
local head = trim(season .. ' Team ' .. code .. ' ' .. playerName .. ' VS-Kassel')
if competition ~= '' then
head = head .. ' ' .. competition
end
if getRawPagesInCategory(frame, head) > 0 then
addTeam(row, code)
end
end
end
end
end
local function rowKey(row)
if trim(row.page) ~= '' then
return 'page:' .. mw.ustring.lower(trim(row.page))
end
return 'name:' .. mw.ustring.lower(trim(row.name))
end
local function cloneBaseRow(row)
local cloned = {
page = trim(row.page),
name = trim(row.name),
games = tonumber(row.games) or 0,
gamesUnknown = row.gamesUnknown and true or false,
goals = tonumber(row.goals) or 0,
assists = tonumber(row.assists) or 0,
points = tonumber(row.points) or 0,
teams = {}
}
for _, code in ipairs(row.teams or {}) do
addTeam(cloned, code)
end
return cloned
end
local function mergeRows(baseRows, dynamicRows)
local rows = {}
local byKey = {}
for _, row in ipairs(baseRows) do
local cloned = cloneBaseRow(row)
local key = rowKey(cloned)
byKey[key] = cloned
table.insert(rows, cloned)
end
for _, row in ipairs(dynamicRows) do
local key = rowKey(row)
local target = byKey[key]
if not target then
target = cloneBaseRow(row)
byKey[key] = target
table.insert(rows, target)
else
target.games = target.games + (tonumber(row.games) or 0)
target.goals = target.goals + (tonumber(row.goals) or 0)
target.assists = target.assists + (tonumber(row.assists) or 0)
target.points = target.points + (tonumber(row.points) or 0)
for _, code in ipairs(row.teams or {}) do
addTeam(target, code)
end
end
end
table.sort(rows, function(a, b)
if a.points ~= b.points then return a.points > b.points end
if a.goals ~= b.goals then return a.goals > b.goals end
if a.assists ~= b.assists then return a.assists > b.assists end
if a.games ~= b.games then return a.games > b.games end
return mw.ustring.lower(a.name) < mw.ustring.lower(b.name)
end)
return rows
end
local function collectDynamicRows(frame, baseRows, dynamicSeasons, competitions, sourceCategories, extraPlayers, maxPoints, maxGoals, maxAssists)
local seasons = splitList(dynamicSeasons)
if #seasons == 0 then
return {}
end
local playerEntries = {}
local seen = {}
for _, row in ipairs(baseRows) do
local name = trim(row.name)
if name ~= '' and not seen[name] then
seen[name] = { page = trim(row.page), name = name }
table.insert(playerEntries, seen[name])
end
end
addSourcePlayers(playerEntries, seen, sourceCategories)
addExtraPlayers(playerEntries, seen, extraPlayers)
local rows = {}
for _, player in ipairs(playerEntries) do
local games = sumGames(frame, seasons, competitions, player.name)
local goals = sumStat(frame, seasons, competitions, player.name, 'T', maxGoals)
local assists = sumStat(frame, seasons, competitions, player.name, 'A', maxAssists)
local points = sumStat(frame, seasons, competitions, player.name, 'P', maxPoints)
if games > 0 or goals > 0 or assists > 0 or points > 0 then
local row = {
page = player.page,
name = player.name,
games = games,
goals = goals,
assists = assists,
points = points,
teams = {}
}
addDynamicTeams(frame, row, seasons, competitions, player.name)
table.insert(rows, row)
end
end
return rows
end
local function formatName(row)
if trim(row.page) ~= '' then
return string.format('[[%s|%s]]', row.page, row.name)
end
return row.name
end
local function formatTeams(row)
local out = {}
for _, code in ipairs(row.teams or {}) do
table.insert(out, '[[' .. code .. ']]')
end
return table.concat(out, ' · ')
end
local function formatGames(row)
if row.gamesUnknown then
return '-'
end
return tostring(row.games or 0)
end
local function formatAverage(row)
if row.gamesUnknown or not row.games or row.games <= 0 then
return '-'
end
return string.format('%.2f', (row.points or 0) / row.games)
end
function p.render(frame)
local baseDataModule = getArg(frame, 'baseDataModule')
if baseDataModule == '' then
baseDataModule = 'Ewige Gegner-Scorer gegen Kassel/Daten/Basis'
end
local ok, data = pcall(mw.loadData, 'Modul:' .. baseDataModule)
if not ok or type(data) ~= 'table' then
return '<strong>Fehler:</strong> Basisdaten konnten nicht geladen werden.'
end
local caption = getArg(frame, 'caption')
if caption == '' then
caption = 'Ewige Gegner-Scorer gegen Kassel'
end
local competitions = splitList(getArg(frame, 'competition'))
if #competitions == 0 then
competitions = { 'HR', 'PO' }
end
local maxPoints = tonumber(getArg(frame, 'maxPoints')) or 8
local maxGoals = tonumber(getArg(frame, 'maxGoals')) or 6
local maxAssists = tonumber(getArg(frame, 'maxAssists')) or 6
local sourceCategories = getArg(frame, 'sourceCategories')
if sourceCategories == '' then
sourceCategories = 'Spieler'
end
local dynamicRows = collectDynamicRows(
frame,
data.rows or {},
getArg(frame, 'dynamicSeasons'),
competitions,
sourceCategories,
getArg(frame, 'extraPlayers'),
maxPoints,
maxGoals,
maxAssists
)
local rows = mergeRows(data.rows or {}, dynamicRows)
local lines = {
'{{#invoke:AutosortTable|create',
'|class=wikitable plainrowheaders sortable static-row-numbers',
'|separator=\t--\t',
'|order=6,4,5,3',
'|numeric=3,4,5,6,7',
'|descending=6,4,5,3,7',
'|caption=' .. caption,
'|rowheader=1',
'|header= \t--\t Name \t--\t Teams \t--\t Spiele \t--\t Tore \t--\t Assists \t--\t Punkte \t--\t Ø Pkt.',
'|colstyle = \t--\t \t--\t \t--\t text-align:center; \t--\t text-align:center; \t--\t text-align:center; \t--\t font-weight:bold;text-align:center; \t--\t text-align:center;'
}
for _, row in ipairs(rows) do
table.insert(lines, string.format(
'|\t--\t %s \t--\t %s \t--\t %s \t--\t %d \t--\t %d \t--\t %d \t--\t %s',
formatName(row),
formatTeams(row),
formatGames(row),
row.goals or 0,
row.assists or 0,
row.points or 0,
formatAverage(row)
))
end
table.insert(lines, '}}')
return frame:preprocess(table.concat(lines, '\n'))
end
return p