-- This module copies content from Template:2012–13_NHL_Eastern_Conference_standings;
-- see the history of that page for attribution.
-- TODO:
-- add support for annotations:
-- - y = won division
-- - z = best record in conference
-- - p = best record in league
-- add support for sorting by playoff ranking
-- add support for wild card-based tables
local me = { }
local nhlInfo
-- if mw.loadData() not supported, use require() instead
if mw.loadData then
nhlInfo = mw.loadData('Module:Sandbox/isaacl/NHL standings/2013 2014 NHL info')
else
nhlInfo = require('Module:Sandbox/isaacl/NHL standings/2013 2014 NHL info')
end
local Navbar = require('Module:Navbar')
local config = { }
config.inputFormat = '2005-2006-rules'
config.outputFormat = '2013-2014-rules'
config.standingsType = 'league'
config.separatorFormat = 'default'
config.seedInfo = { }
config.tableHeaderText = { }
config.entityForTeam = nil
config.tableTitleForStandingsType = {
conference = 'Conference standings',
division = 'Division',
league = 'League standings',
}
config.scopeForStandingsType = {
conference = 'conference',
division = 'division',
league = 'league',
}
local function initEntityForTeamTable()
config.entityForTeam = { }
for conferenceName, conferenceInfo in pairs(nhlInfo.structure.conference) do
for divisionName, teamList in pairs(conferenceInfo.division) do
for idx, teamName in ipairs(teamList) do
if (config.entityForTeam[teamName] == nil) then
config.entityForTeam[teamName] = { }
end
config.entityForTeam[teamName].conference = conferenceName
config.entityForTeam[teamName].division = divisionName
end -- loop over team names
end -- loop over divisions
end -- loop over conferences
end -- function initTeamsForEntity()
local function filterTeamsByEntity(listOfTeams, entityType, entity)
if (config.entityForTeam == nil) then
initEntityForTeamTable()
end
local filteredTeams = { }
for idx, teamInfo in ipairs(listOfTeams) do
if (config.entityForTeam[teamInfo.name]) then
if (entity == config.entityForTeam[teamInfo.name][entityType]) then
table.insert(filteredTeams, teamInfo)
end
end
end
return filteredTeams
end -- function filterTeamsByEntity
local function compareTeamsByRank(teamA, teamB)
local result;
if (teamA.points ~= teamB.points) then
return teamA.points > teamB.points
elseif (teamA.games ~= teamB.games) then
return teamA.games < teamB.games
elseif (teamA.nonShootoutWins ~= teamB.nonShootoutWins) then
return teamA.nonShootoutWins > teamB.nonShootoutWins
else
-- The next tie-breaker is the head-to-head records of the
-- two teams. As this information is not given to this
-- template, just preserve the original order.
return teamA.originalPosition < teamB.originalPosition
end
end -- function compareTeams()
local readTeamInfo = {
['2005-2006-rules'] = function(args, currentIdx, returnData)
if (config.separatorFormat == 'whitespace') then
if (args[currentIdx] == nil or
args[currentIdx+1] == nil) then
returnData.errorMessage = 'Insufficient number of arguments for format: ' ..
config.separatorFormat
return nil
end
elseif (args[currentIdx] == nil or
args[currentIdx+1] == nil or
args[currentIdx+2] == nil or
args[currentIdx+3] == nil or
args[currentIdx+4] == nil or
args[currentIdx+5] == nil or
args[currentIdx+6] == nil ) then
returnData.errorMessage = 'Insufficient number of arguments for format: ' ..
config.separatorFormat
return nil
end
local teamInfo = {
name = mw.text.trim(args[currentIdx])
}
if (config.separatorFormat == 'whitespace') then
local argList = mw.text.split(args[currentIdx+1], '%s+')
teamInfo.wins = tonumber(mw.text.trim(argList[1]))
teamInfo.losses = tonumber(mw.text.trim(argList[2]))
teamInfo.nonRegulationLosses = tonumber(mw.text.trim(argList[3]))
teamInfo.nonShootoutWins = tonumber(mw.text.trim(argList[4]))
teamInfo.goalsFor = tonumber(mw.text.trim(argList[5]))
teamInfo.goalsAgainst = tonumber(mw.text.trim(argList[6]))
returnData.cIndicesRead = 2
else
teamInfo.wins = tonumber(mw.text.trim(args[currentIdx+1]))
teamInfo.losses = tonumber(mw.text.trim(args[currentIdx+2]))
teamInfo.nonRegulationLosses = tonumber(mw.text.trim(args[currentIdx+3]))
teamInfo.nonShootoutWins = tonumber(mw.text.trim(args[currentIdx+4]))
teamInfo.goalsFor = tonumber(mw.text.trim(args[currentIdx+5]))
teamInfo.goalsAgainst = tonumber(mw.text.trim(args[currentIdx+6]))
returnData.cIndicesRead = 7
end
teamInfo.games = teamInfo.wins + teamInfo.losses + teamInfo.nonRegulationLosses
teamInfo.points = 2 * teamInfo.wins + teamInfo.nonRegulationLosses
return teamInfo
end, -- function readTeamInfo.default()
} -- readTeamInfo object
local function initTableHeaderText(frame)
config.tableHeaderText = {
rank = frame:expandTemplate{ title='abbr', args={'R', 'Rank'} },
division = frame:expandTemplate{ title='abbr', args={'Div', 'Division'} },
games = frame:expandTemplate{ title='abbr', args={'GP', 'Games played'} },
wins = frame:expandTemplate{ title='abbr', args={'W', 'Wins'} },
losses = frame:expandTemplate{ title='abbr', args={'L', 'Regulation losses'} },
nonRegulationLosses = frame:expandTemplate{ title='abbr', args={'OTL', 'Overtime + shootout losses'} },
nonShootoutWins = frame:expandTemplate{ title='abbr', args={'ROW', 'Regulation + overtime wins'} },
goalsFor = frame:expandTemplate{ title='abbr', args={'GF', 'Goals for'} },
goalsAgainst = frame:expandTemplate{ title='abbr', args={'GA', 'Goals against'} },
points = frame:expandTemplate{ title='abbr', args={'Pts', 'Points'} },
}
end
local generateTableHeader = {
['2013-2014-rules'] = function(config)
return
'{| class="wikitable sortable" style="text-align:center;"\
|+ ' .. config.tableTitleForStandingsType[config.standingsType] .. '\
! style="width:5%" | ' .. config.tableHeaderText.rank .. '\
! style="width:25%;" class="unsortable" |\
! style="width:8%;" class="unsortable" | ' .. config.tableHeaderText.games .. '\
! style="width:8%;" | ' .. config.tableHeaderText.wins .. '\
! style="width:8%;" | ' .. config.tableHeaderText.losses .. '\
! style="width:8%;" | ' .. config.tableHeaderText.nonRegulationLosses .. '\
! style="width:8%;" | ' .. config.tableHeaderText.nonShootoutWins .. '\
! style="width:10%;" | ' .. config.tableHeaderText.goalsFor .. '\
! style="width:10%;" | ' .. config.tableHeaderText.goalsAgainst .. '\
! style="width:10%;" | ' .. config.tableHeaderText.points .. '\n'
end, -- function generateTableHeader.'2013-2014-rules'
} -- generateTableHeader object
local generateTeamRow = {
['2013-2014-rules'] = function(config, teamRowInfo, teamInfo)
return
'|-' .. teamRowInfo.rowStyle .. '\
|| ' .. teamRowInfo.rank .. '\
|| ' .. teamRowInfo.seedText .. '[[' .. teamRowInfo.teamSeasonPage .. '|' .. teamInfo.name .. ']]\
|| ' .. teamInfo.games ..'\
|| ' .. teamInfo.wins .. ' || ' .. teamInfo.losses .. '\
|| ' .. teamInfo.nonRegulationLosses .. '\
|| ' .. teamInfo.nonShootoutWins .. '\
|| ' .. teamInfo.goalsFor ..'\
|| ' .. teamInfo.goalsAgainst .. '\
|| ' .. teamInfo.points .. '\n'
end, -- function generateTeamRow.'2013-2014-rules'
} -- generateTeamRow object
local calculateSeeds = {
['2013-2014-rules'] = function()
return '' -- TODO
end, -- function calculateSeeds.'2013-2014-rules'
} -- calculateSeeds object
local function parseHighlightArg(highlightArg, teamsToHighlight)
local teamList = mw.text.split(highlightArg, '%s*,%s*')
if (#teamList == 0) then
return
end
for idx, team in ipairs(teamList) do
teamsToHighlight[mw.text.trim(team)] = true
end
end -- function parseHighlightArg
local function parseTeamLinks(teamLinksArg, linkForTeam)
local teamList = mw.text.split(teamLinksArg, '%s*,%s*')
if (#teamList == 0) then
return
end
for idx, teamLinkInfo in ipairs(teamList) do
local teamData = mw.text.split(teamLinkInfo, '%s*:%s*')
if (#teamData >= 2) then
local team = mw.text.trim(teamData[1])
local teamLink = mw.text.trim(teamData[2])
linkForTeam[team] = teamLink
end
end
end -- function parseTeamLinks
function me.generateStandingsTable(frame)
config.templateName = nil
if (frame.args.template_name ~= nil) then
config.templateName = frame.args.template_name
end
if (frame.args.standingsType ~= nil) then
local standingsTypeArg = mw.text.trim(frame.args.standingsType)
if (standingsTypeArg ~= '') then
config.standingsType = standingsTypeArg
end
end
if (config.standingsType ~= nil) then
if (frame.args.entity ~= nil) then
local entityArg = mw.text.trim(frame.args.entity)
if (entityArg ~= '') then
config.entity = entityArg
end
end
end
if (frame.args.separatorFormat ~= nil) then
local separatorFormatArg = mw.text.trim(frame.args.separatorFormat)
config.separatorFormat = separatorFormatArg
end
config.season = mw.text.trim(frame.args.season or '')
-- For convenience, replace hyphen with en-dash
config.season = mw.ustring.gsub(config.season, '-', '–')
config.teamsToHighlight = {}
if (frame.args.highlight ~= nil) then
parseHighlightArg(frame.args.highlight, config.teamsToHighlight)
end
config.linkForTeam = {}
if (frame.args.team_links ~= nil) then
parseTeamLinks(frame.args.team_links, config.linkForTeam)
end
local listOfTeams = {}
local currentArgIdx = 1
local originalPosition = 1
while (frame.args[currentArgIdx] ~= nil) do
local returnData = { }
local teamInfo = readTeamInfo[config.inputFormat](frame.args, currentArgIdx, returnData);
if (teamInfo == nil) then
if (returnData.errorMessage ~= nil) then
return returnData.errorMessage
end
break
end
if (config.linkForTeam[teamInfo.name] ~= nil) then
teamInfo.teamLink = config.linkForTeam[teamInfo.name]
else
teamInfo.teamLink = teamInfo.name
end
teamInfo.originalPosition = originalPosition
table.insert(listOfTeams, teamInfo)
currentArgIdx = currentArgIdx + returnData.cIndicesRead
originalPosition = originalPosition + 1
end
if (#listOfTeams == 0) then
return 'No teams!'
end
local outputBuffer = { }
initTableHeaderText(frame)
table.insert(outputBuffer,
generateTableHeader[config.outputFormat](config)
)
local teamsToDisplay = listOfTeams
-- filter teams to display, based on scope
if (config.scopeForStandingsType[config.standingsType] == 'division') then
teamsToDisplay = filterTeamsByEntity(listOfTeams, 'division', config.entity)
end
if (config.scopeForStandingsType[config.standingsType] == 'conference') then
teamsToDisplay = filterTeamsByEntity(listOfTeams, 'conference', config.entity)
end
table.sort(teamsToDisplay, compareTeamsByRank)
-- TODO: further filter teams after sorting (for division leaders
-- table)
local rankIdx = 1
for idx, teamInfo in ipairs(teamsToDisplay) do
local teamRowInfo = {
seedText = '',
rowStyle = '',
teamSeasonPage = config.season .. ' ' .. teamInfo.teamLink .. ' season',
rank = rankIdx,
}
if (config.seedInfo[teamInfo.name] ~= nil) then
teamRowInfo.seedText = '<sup>(' .. seedInfo[teamInfo.name] .. ')</sup> '
teamRowInfo.rowStyle = ' style="background:#CCFFCC"'
end
if (config.teamsToHighlight[teamInfo.name]) then
teamRowInfo.rowStyle = ' style="background:#CCFFCC"'
end
table.insert(outputBuffer,
generateTeamRow[config.outputFormat](config, teamRowInfo, teamInfo)
)
rankIdx = rankIdx + 1
end -- end of looping over listOfTeams
table.insert(outputBuffer, '|}\n')
return table.concat(outputBuffer)
end -- function me.generateStandingsTable()
function me.generateStandingsTable_fromTemplate(frame)
return me.generateStandingsTable(frame:getParent())
end -- function me.generateStandingsTable_fromTemplate()
return me