Leaguepedia | League of Legends Esports Wiki
Advertisement
Leaguepedia | League of Legends Esports Wiki

Documentation for this module may be created at Module:MatchSchedule/doc

local util_args = require('Module:ArgsUtil')
local util_cargo = require('Module:CargoUtil')
local util_esports = require('Module:EsportsUtil')
local util_game = require("Module:GameUtil")
local util_map = require('Module:MapUtil')
local util_riot = require("Module:RiotUtil")
local util_table = require('Module:TableUtil')
local util_text = require('Module:TextUtil')
local util_time = require('Module:TimeUtil')
local util_vars = require('Module:VarsUtil')
local m_team = require('Module:Team')
local Patch = require('Module:PatchVariablesClass')()

local Phase = require('Module:Phase')

local i18n = require('Module:I18nUtil')
local lang = mw.getLanguage('en')

local MATCH_FIELDS = { 'Round', 'Group', 'Team1', 'Team2', 'Result', 'Points', 'PointsTB', 'BestOf', 'DateTime_UTC', 'DateTime_PST', 'DateTime_CET', 'DateTime_KST', 'PBP', 'Color', 'MVP', 'VodInterview', 'With', 'VodHighlights', 'Reddit', 'QQ', 'WanplusId', }

local GAME_ARGS = { 'Blue', 'Red', 'Winner', 'FF', 'Selection', 'MatchHistory', 'Recap', 'Vod', 'VodPB', 'VodGameStart', 'VodPostgame', 'VodHighlights', 'VodInterview', 'InterviewWith', 'MVP', 'MVPPoints', 'BlueFootnote', 'RedFootnote', 'Summary', 'Reddit', 'RiotPlatformGameId' }

local GAME_FIELDS = { 'Blue', 'Red', 'Winner', 'FF', 'Selection', 'MatchHistory', 'RiotPlatformGameId', 'Vod', 'VodPB', 'VodGameStart', 'VodPostgame', 'VodHighlights', 'VodInterview', 'InterviewWith', 'MVP' }

local ALLOWED_TZ = { PST = true, KST = true, CET = true }

local h = {}
local p = {}
function p.start(frame)
	i18n.init('MatchSchedule')
	local args = util_args.merge()
	local tbl = mw.html.create()
	args.tab = args.tab and Phase(args.tab):name() or 'Matches'
	tbl:tag('tr'):tag('th'):attr('colspan','50'):wikitext(args.tab)
	local tr = tbl:tag('tr')
	for _, v in ipairs(MATCH_FIELDS) do
		tr:tag('th'):wikitext(i18n.print(v))
	end
	for _, v in ipairs(GAME_FIELDS) do
		tr:tag('th'):wikitext(i18n.print(v))
	end
	h.setStartVars(args)
	return '<table class="wikitable">', tbl
end

function p.main(frame)
	i18n.init('MatchSchedule')
	local args = util_args.merge()
	h.validateArgs(args)
	h.castMatchArgs(args)
	
	local match_tbl = h.makeCargoMatchFields(args)
	
	local display_games, games_cargo
	if args.game1 then
		local games_tbl = util_args.numberedArgsToTable(args, 'game')
		util_map.inPlace(games_tbl, util_args.splitArgs, GAME_ARGS)
		util_map.rowsInPlace(games_tbl, h.castGameArgs)
		games_cargo = h.makeCargoGameFields(games_tbl, match_tbl)
		display_games = util_map.arraySafe(games_tbl, h.makeGameDisplay)
		h.addGameTotalScoreToMatchAndValidate(match_tbl, games_cargo, util_args.castAsBool(args.ignorewarnings))
	end
	
	if util_cargo.doWeStoreCargo(args.nocargo, 'Data') then
		util_cargo.store(match_tbl)
		if games_cargo then
			for _, game in ipairs(games_cargo) do
				util_cargo.store(game)
			end
		end
	end
	
	local display_match = h.makeMatchDisplay(match_tbl, args)
	return h.printMatchRow(display_match, display_games)
end

function h.validateArgs(args)
	if args.date and args.date:find('%s%s%-%s%s%-%s%s%s%s') then
		error('Unsupported date type detected, please use yyyy-mm-dd')
	end
end

function h.castMatchArgs(args)
	if lang:lc(args.winner or '') == 'draw' then
		args.winner = '0'
	end
	if args.ff == 'both' then args.ff = 0 end
	if args.ff then args.ff = tonumber(args.ff) end
	args.team1 = m_team.teamlinkname(args.team1)
	args.team2 = m_team.teamlinkname(args.team2)
	args.Winner = args.winner -- for consistency with game field names
	h.castAndValidateWinner(args, 'team1', 'team2')
end

function h.castAndValidateWinner(row, team1, team2)
	-- a lot of people think winner is supposed to be the name of a team instead of 1/2
	-- so let's make that allowed syntax
	if row.Winner then
		local winTeam = m_team.teamlinkname(row.Winner)
		if winTeam == row[team1] then row.Winner = '1' end
		if winTeam == row[team2] then row.Winner = '2' end
		if row.Winner:lower() == 'blue' then row.Winner = '1' end
		if row.Winner:lower() == 'red' then row.Winner = '2' end
	end
	if row.Winner and row.Winner ~= '1' and row.Winner ~= "2" and row.Winner ~= "0" then
		error(i18n.print('error_invalidWinner', row[team1] or 'Unknown', row[team2] or 'Unknown'))
	end
	if row.ff == 1 then row.Winner = 2 end
	if row.ff == 2 then row.Winner = 1 end
end

function p.endTable(frame)
	return '</table>'
end

function h.setStartVars(args)
	util_vars.setGlobalIndex('N_TabInPage')
	util_vars.resetGlobalIndex('N_MatchInTab')
	util_vars.setVar('OverviewPage', util_esports.getOverviewPage())
	util_vars.setVar('TabName', args.tab)
	util_vars.setVarOnlyIf('Bestof', args.bestof)
	util_vars.setVarOnlyIf('MVPPoints', args.mvppoints)
	util_vars.setVarOnlyIf('shownname', args.shownname)
	if args.phase then
		local thisphase = Phase(args.phase):name()
		local lastphase = util_vars.getVar('Phase')
		if lastphase ~= thisphase then
			util_vars.setVar('Phase', thisphase)
			util_vars.setGlobalIndex('Phase_N')
		end
	end
end

function h.getTZData(args)
	if not args.date then
		return {}
	end
	local time = ('%s %s'):format(args.date, args.time or '23:59')
	if not args.timezone or not ALLOWED_TZ[args.timezone] then
		error('Missing or invalid timezone. Allowed values: PST, CET, KST.')
	end
	local tz = args.timezone or 'PST'
	local dst = util_args.norm(args.dst)
	return util_time.getTimezones(time, tz, dst)
end

function h.makeCargoMatchFields(args)
	local tzdata = h.getTZData(args)
	h.validateMatchArgs(args)
	local tbl = {
		_table = 'MatchSchedule',
		Team1 = args.team1, -- was previously cast
		Team2 = args.team2, -- was previously cast
		Team1Points = args.team1points,
		Team2Points = args.team2points,
		Team1PointsTB = args.team1pointstb,
		Team2PointsTB = args.team2pointstb,
		DateTime_UTC = tzdata.UTC,
		HasTime = util_args.isDefined(args.time),
		OverviewPage = util_vars.getVar('OverviewPage'),
		Winner = args.Winner, -- cast version with capital letter
		Team1Score = args.team1score,
		Team2Score = args.team2score,
		FF = args.ff,
		DST = args.dst,
		IsFlexibleStart = util_args.castAsBool(args.flex),
		
		BestOf = args.bestof or util_vars.getVar('Bestof'),
		ShownRound = args.round or util_vars.getVar('Round'),
		Phase = util_vars.getVar('Phase'),
		N_MatchInPage = util_vars.setGlobalIndex('MS_Overall'),
		Tab = util_vars.getVar('TabName'),
		N_MatchInTab = util_vars.setGlobalIndex('N_MatchInTab'),
		N_TabInPage = util_vars.getGlobalIndex('N_TabInPage'),
		N_Page = util_vars.getVar('N_Page'),
		
		Player1 = args.player1,
		Player2 = args.player2,
		
		Patch = Patch:get('patch'),
		DisabledChampions = Patch:get('disabled'),
		Hotfix = Patch:get('hotfix'),
		PatchFootnote = Patch:get('footnote'),
		
		IsTiebreaker = util_args.castAsBool(args.istb),
		OverrideAllowPredictions = util_args.castAsBool(args.predictionoverride),
		
		Stream = args.stream,
		StreamDisplay = args.streamdisplay,
		Venue = args.venue,
		CastersPBP = args.pbp,
		CastersColor = args.color,
		Casters = h.getAllCasters(args.pbp, args.color),
		MVP = args.mvp,
		MVPPoints = args.mvppoints or util_vars.getVar('MVPPoints'),
		VodInterview = args.vodinterview,
		VodHighlights = args.vodhl,
		InterviewWith = args.with,
		Recap = args.recap,
		Reddit = args.reddit,
		QQ = args.qq,
		qq_mh = args.qq and ('https://lpl.qq.com/es/stats.shtml?bmid=%s'):format(args.qq),
		Wanplus = args.wanplus,
		WanplusId = args.wanplus and args.wanplus:match('schedule/(%d+)'),
		
		Team1Poster = args.team1poster,
		Team2Poster = args.team2poster,
		Team1Footnote = args.team1footnote,
		Team2Footnote = args.team2footnote,
		Footnote = args.footnote,
		
		Tags = args.tags,
		UserSignup = args.user_signup,
	}
	tbl.PatchPage = h.getPatchPage(tbl.Patch)
	tbl.InitialN_MatchInTab = args.initialorder or tbl.N_MatchInTab
	tbl.InitialPageAndTab = args.initialtab
	local title = mw.title.getCurrentTitle().text
	tbl.UniqueMatch = title .. '_' .. tbl.Tab .. '_' .. tbl.N_MatchInTab
	tbl.MatchId = ('%s_%s_%s'):format(
		tbl.OverviewPage,
		tbl.Tab,
		tbl.N_MatchInTab
	)
	tbl.ShownName = args.shownname or util_vars.getVar('shownname') or tbl.OverviewPage
	
	tbl.Team1Final = args.team1final and m_team.teamlinkname(args.team1final) or tbl.Team1
	tbl.Team2Final = args.team2final and m_team.teamlinkname(args.team2final) or tbl.Team2
	
	-- TODO: these should be made able to cope with nil Team1, Team2
	tbl.PageAndTeam1 = tbl.OverviewPage .. '_' .. tbl.Team1
	tbl.PageAndTeam2 = tbl.OverviewPage .. '_' .. tbl.Team2
	
	h.getMatchDay(tbl, tzdata.UTC, args.matchday)
	return tbl
end

function h.getPatchPage(patch)
	if not patch then return nil end
	local page = util_game.patchPage(patch)
	-- this is only expensive the first time it's called per unique page
	if mw.title.makeTitle('', page).exists then return page end
	return nil
end

function h.validateMatchArgs(args)
	if args.team1score and not tonumber(args.team1score) then
		error(i18n.print('error_nonIntegerTeamScore', 1, args.team1 or 'TBD', args.team2 or 'TBD'))
	end
	if args.team2score and not tonumber(args.team2score) then
		error(i18n.print('error_nonIntegerTeamScore', 2, args.team1 or 'TBD', args.team2 or 'TBD'))
	end
end

function h.getAllCasters(pbp, color)
	local tbl = {}
	tbl[#tbl+1] = pbp
	tbl[#tbl+1] = color
	return util_table.concat(tbl, ',')
end

function h.getMatchDay(tbl, tzdata, gameday)
	local thistime = tonumber(util_time.unix(tzdata))
	local lasttime = tonumber(util_vars.getVar('MatchTime') or '')
	local lastday = tonumber(util_vars.getVar('MatchDay') or '')
	util_vars.setVar('MatchTime', thistime)
	if tbl.N_MatchInTab == 1 then
		tbl.MatchDay = 1
		util_vars.setVar('MatchDay', 1)
	elseif gameday then
		tbl.MatchDay = gameday
		util_vars.setVar('MatchDay', gameday)
	elseif not lasttime then
		tbl.MatchDay = 1
		util_vars.setVar('MatchDay', 1)
	elseif (thistime - lasttime) > 43200 then
		tbl.MatchDay = lastday + 1
		util_vars.setVar('MatchDay', lastday + 1)
	else
		tbl.MatchDay = lastday
		util_vars.setVar('MatchDay', lastday)
	end
end

function h.castGameArgs(game)
	game.Blue = game.Blue and m_team.teamlinkname(game.Blue)
	game.Red = game.Red and m_team.teamlinkname(game.Red)
	h.castAndValidateWinner(game, 'Blue', 'Red')
end

function h.makeCargoGameFields(args, match)
	local tbl = {}
	for i, game in ipairs(args) do
		tbl[i] = mw.clone(game)
		thisgame = tbl[i] -- alias for convenience
		thisgame._table = 'MatchScheduleGame'
		thisgame.Blue = game.Blue
		thisgame.Red = game.Red
		
		thisgame.MVPPoints = thisgame.MVPPoints or match.MVPPoints
				
		thisgame.OverviewPage = match.OverviewPage
		thisgame.N_GameInMatch = i
		thisgame.UniqueMatch = match.UniqueMatch
		thisgame.N_MatchInTab = match.N_MatchInTab
		thisgame.N_TabInPage = match.N_TabInPage
		thisgame.N_Page = match.N_Page
		thisgame.WrittenSummary = game.Summary
		thisgame.Reddit = game.Reddit
		
		thisgame.MatchHistory = thisgame.MatchHistory or match.qq_mh
		util_riot.setMhIdFields(thisgame, thisgame.MatchHistory)
		
		h.setGameIds(thisgame, match)
		if thisgame.Selection then
			h.sideSelection(thisgame)
		end
		
		local allowed_teams = { [match.Team1] = true, [match.Team2] = true }
		
		if thisgame.Blue and not allowed_teams[thisgame.Blue] then
			error(('%s: Invalid blue team (%s) in match %s game %s'):format(match.Tab, thisgame.Blue, thisgame.N_MatchInTab, i))
		end
		if thisgame.Red and not allowed_teams[thisgame.Red] then
			error(('%s: Invalid red team (%s) in game %s in match %s'):format(match.Tab, thisgame.Red, thisgame.N_MatchInTab, i))
		end
		if thisgame.Blue and thisgame.Blue == thisgame.Red then
			error(('%s: Same team entered for blue & red (%s) in match %s game %s'):format(match.Tab, thisgame.Blue, thisgame.N_MatchInTab, i))
		end
	end
	return tbl
end

function h.setGameIds(thisgame, match)
	thisgame.GameId = ('%s_%s_%s_%s'):format(
		match.OverviewPage,
		match.Tab,
		match.N_MatchInTab,
		thisgame.N_GameInMatch
	)
	thisgame.MatchId = ('%s_%s_%s'):format(
		match.OverviewPage,
		match.Tab,
		match.N_MatchInTab
	)
end

function h.sideSelection(thisgame)
	if thisgame.Selection == '1' or thisgame.Selection == '2' then
		h.selectionFromNumber(thisgame, match)
	else
		thisgame.Selection = m_team.teamlinkname(thisgame.Selection)
	end
end

function h.selectionFromNumber(thisgame)
	if thisgame.Selection == '1' then
		thisgame.Selection = thisgame.Blue
	elseif thisgame.Selection == '2' then
		thisgame.Selection = thisgame.Red
	end
end

function h.makeMatchDisplay(tbl, args)
	local display = mw.clone(tbl)
	local tzdata = h.getTZData(args)
	display.Result = ("%s - %s"):format(
		tbl.Winner == '1' and ("'''%s'''"):format(tbl.Team1Score or '') or tbl.Team1Score or '',
		tbl.Winner == '2' and ("'''%s'''"):format(tbl.Team2Score or '') or tbl.Team2Score or ''
	)
	display.Team1 = m_team.plainlinked(tbl.Team1)
	display.Team2 = m_team.plainlinked(tbl.Team2)
	display.DateTime_UTC = tbl.DateTime_UTC and lang:formatDate('D Y-m-d H:i', tbl.DateTime_UTC)
	display.DateTime_PST = h.getTZDisplay(tzdata.PST)
	display.DateTime_CET = h.getTZDisplay(tzdata.CET)
	display.DateTime_KST = h.getTZDisplay(tzdata.KST)
	display.Points = (tbl.Team1Points or tbl.Team2Points) and ('%s - %s'):format(tbl.Team1Points or '', tbl.Team2Points or '')
	display.PointsTB = (tbl.Team1PointsTB or tbl.Team2PointsTB) and ('%s - %s'):format(tbl.Team1PointsTB or '', tbl.Team2PointsTB or '')
	display.PBP = util_esports.playersLinked(args.pbp)
	display.Color = util_esports.playersLinked(args.color)
	display.MVP = util_esports.playersLinked(args.mvp)
	display.With = util_esports.playersLinked(args.with)
	display.Reddit = args.reddit and ('[%s]'):format(args.reddit)
	display.Recap = args.recap and ('[%s]'):format(args.recap)
	display.VodInterview = args.vodinterview and ('[%s]'):format(args.vodinterview)
	display.VodHighlights = args.vodhl and ('[%s]'):format(args.vodhl)
	return display
end

function h.getTZDisplay(str)
	if not str then return nil end
	return str:match('(%d%d:%d%d)')
end

function h.makeGameDisplay(game)
	local display = mw.clone(game)
	display.Blue = game.Blue and m_team.short(game.Blue) or '[[Category:Data Pages Missing Per-Game Data]]'
	display.Red = game.Red and m_team.short(game.Red) or '[[Category:Data Pages Missing Per-Game Data]]'
	display.Vod = h.printExternalLink(game.Vod)
	display.VodPB = h.printExternalLink(game.VodPB)
	display.VodGameStart = h.printExternalLink(game.VodGameStart)
	display.VodPostgame = h.printExternalLink(game.VodPostgame)
	display.VodHighlights = h.printExternalLink(game.VodHighlights)
	display.Recap = h.printExternalLink(game.recap)
	display.VodInterview = h.printExternalLink(game.VodInterview)
	display.MatchHistory = h.printExternalLink(game.MatchHistory)
	display.MVP = util_esports.playersLinked(game.MVP)
	display.InterviewWith = util_esports.playersLinked(game.InterviewWith)
	display.Summary = game.Summary and 'Y' or ''
	return display
end

function h.printExternalLink(url)
	if not url then
		return nil
	end
	return ('[%s]'):format(url)
end

function h.addGameTotalScoreToMatchAndValidate(match_tbl, games_cargo, ignorewarnings)
	local team1 = match_tbl.Team1
	local team2 = match_tbl.Team2
	local matchScores = {
		[team1] = match_tbl.Team1Score,
		[team2] = match_tbl.Team2Score,
	}
	local gameScores = {
		[team1] = 0,
		[team2] = 0,
		hasTotals = false,
	}
	h.countGameScores(gameScores, games_cargo)
	if match_tbl.Winner then
		h.validateTotalScores(matchScores, gameScores, match_tbl, ignorewarnings)
	elseif gameScores.hasTotals then
		match_tbl.Team1Score = gameScores[team1]
		match_tbl.Team2Score = gameScores[team2]
	end
end

function h.countGameScores(gameScores, games_cargo)
	for _, game in ipairs(games_cargo) do
		local winKey = (game.Winner == '1' and 'Blue') or (game.Winner == '2' and 'Red')
		if winKey then
			gameScores.hasTotals = true
			local winTeam = game[winKey]
			gameScores[winTeam] = (gameScores[winTeam] or 0) + 1
		end
	end
end

function h.validateTotalScores(matchScores, gameScores, match_tbl, ignorewarnings)
	if not gameScores.hasTotals then return end
	if ignorewarnings then return end
	if match_tbl.FF then return end
	for team, v in pairs(matchScores) do
		v = v and tonumber(v)
		if gameScores[team] ~= v then
			error(('Non-matching match (%s) & game (%s) scores for team '):format(
				v,
				gameScores[team]
			) .. team)
		end
	end
end

function h.printMatchRow(display_match, games)
	if not games then games = { {} } end
	local ngames = #games
	local tbl = mw.html.create()
	for i, game in ipairs(games) do
		local tr = tbl:tag('tr')
		if i == 1 then
			for _, v in ipairs(MATCH_FIELDS) do
				tr:tag('td'):attr('rowspan',ngames):wikitext(display_match[v])
			end
		end
		h.printGameRow(tr, game)
	end
	return tbl
end

function h.printGameRow(tr, game)
	for _, v in ipairs(GAME_FIELDS) do
		tr:tag('td'):wikitext(game[v])
	end
	return
end

return p
Advertisement