Module:Sandbox/isaacl/NHL standings

-- 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;"\ ! 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'
 * + ' .. config.tableTitleForStandingsType[config.standingsType] .. '\

end, -- function generateTableHeader.'2013-2014-rules' } -- generateTableHeader object

local generateTeamRow = { ['2013-2014-rules'] = function(config, teamRowInfo, teamInfo) return '|-' .. teamRowInfo.rowStyle .. '\
 * ' .. teamRowInfo.rank .. '\
 * ' .. teamRowInfo.seedText .. '' .. 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 = '(' .. seedInfo[teamInfo.name] .. ') ' 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