Leaguepedia | League of Legends Esports Wiki

Edit the documentation or categories for this module.

To exclude a match from the top schedule, use |no_topschedule=Yes in {{Infobox Tournament}}.


local util_args = require('Module:ArgsUtil')
local util_cargo = require('Module:CargoUtil')
local util_esports = require('Module:EsportsUtil')
local util_form = require('Module:FormUtil')
local util_html = require('Module:HtmlUtil')
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 MIN_EVENTS_COUNT_TO_GROUP = 3
local MIN_EVENTS_COUNT_TO_GROUP_WITH_UNKNOWN_TEAMS = 2
local SECONDS_IN_HOUR = 60 * 60
local MAX_SERIES_TO_INCLUDE = 50

local FORM_INFO = {
	form = 'SpoilerFreeSchedule',
	template = 'SFS'
}

local h = {}

local p = {}
function p.main(frame)
	local args = util_args.merge()
	local result = h.doQuery()
	local processed = h.processResult(result)
	h.formatResults(processed)
	return h.makeOutput(processed)
end

function h.doQuery()
	local query = h.makeQuery(where)
	return util_cargo.queryAndCast(query)
end

function h.makeQuery(where)
	return {
		tables = 'MatchSchedule=MS, Tournaments=T',
		join = { 'MS.OverviewPage=T.OverviewPage' },
		fields = {
			'MS.Team1',
			'MS.Team2',
			'MS.Player1',
			'MS.Player2',
			'MS.DateTime_UTC=DateTime',
			'MS.OverviewPage',
			'MS.HasTime=TimeEntered [boolean]',
			'MS.ShownName',
			'MS.Round',
			'MS.Tab',
			'MS.Stream',
			'T.StandardName',
			'T.Name'
		},
		groupBy = 'MS.MatchId',
		orderBy = 'MS.DateTime_UTC ASC',
		where = h.getWhere(),
		limit = 250,
	}
end

function h.getWhere()
	local ret = {
		'MS.DateTime_UTC > NOW() - INTERVAL 3 HOUR AND WINNER IS NULL',
		'T.SuppressTopSchedule IS NULL OR T.SuppressTopSchedule="0"',
		
		-- suppress all self pages from storing here automatically since they don't write cargo
		'T._pageName IS NOT NULL',
	}
	return util_cargo.concatWhere(ret)
end

function h.processResult(result)
	local eventCount = h.countEvents(result)
	local eventsWrittenToOutput = {}
	local processed = {}
	for _, row in ipairs(result) do
		if not eventsWrittenToOutput[row.groupKey] then
			row.count = eventCount[row.groupKey]
			processed[#processed+1] = row
		end
		eventsWrittenToOutput[row.groupKey] = eventCount[row.groupKey]
	end
	local output = util_table.slice(processed, 1, MAX_SERIES_TO_INCLUDE)
	return output
end

function h.countEvents(result)
	local tracker = {}
	local eventCount = {}
	for _, row in ipairs(result) do
		row.groupKey = row.OverviewPage .. row.DateTime
		h.updateTracker(tracker, row)
		if h.doWeGroup(row, tracker.counter) then
			eventCount[row.groupKey] = tracker.counter
		end
	end
	return eventCount
end

function h.updateTracker(tracker, row)
	if tracker.groupKey == row.groupKey then
		tracker.counter = (tracker.counter or 0) + 1
	else
		tracker.counter = 1
	end
	tracker.groupKey = row.groupKey
end

function h.doWeGroup(row, counter)
	if row.Team1 == 'TBD' and row.Team2 == 'TBD' then
		return counter >= MIN_EVENTS_COUNT_TO_GROUP_WITH_UNKNOWN_TEAMS
	end
	return counter >= MIN_EVENTS_COUNT_TO_GROUP
end

function h.formatResults(result)
	for i, row in ipairs(result) do
		h.addMatchup(row)
		h.addEventLinked(row)
		h.addCountdown(row, i)
		h.addCalendar(row)
	end
end

function h.addMatchup(row)
	if row.count then
		row.Matchup = ('%s Matches'):format(row.count)
	elseif not (row.Team1 or row.Team2) and not (row.Player1 or row.Player2) then
		row.Matchup = row.Round or row.Tab
	elseif row.Player1 or row.Player2 then
		row.Is1v1 = true
		row.Matchup = h.make1v1Matchup(row)
	else
		Team1Formatted = h.formatTeamForMatchup(row.Team1)
		Team2Formatted = h.formatTeamForMatchup(row.Team2)
		row.Matchup = ('%s vs %s'):format(Team1Formatted, Team2Formatted)
	end
end

function h.make1v1Matchup(row)
	return ('%s vs %s'):format(util_esports.playerLinked(row.Player1), util_esports.playerLinked(row.Player2))
end

function h.formatTeamForMatchup(team)
	if team == 'TBD' then
		return 'TBD'
	end
	return m_team.onlyimagelinked(team, {size = 45})
end

function h.addEventLinked(row)
	row.EventLinked = util_text.intLink(row.OverviewPage, row.ShownName or row.StandardName or row.Name or row.OverviewPage)
end

function h.addCountdown(row, i)
	local countdown = util_time.countdown(row.DateTime, {
		data_end = 'toggle',
		i = 'ts-' .. i,
		default = 'LIVE'
	})
	row.Countdown = util_text.extLinkOrText(row.Stream, countdown)
end

function h.addCalendar(row)
	row.Calendar = util_esports.calendarExtLink(util_form.fullURL(FORM_INFO, {row.OverviewPage}))
end

function h.makeOutput(result)
	local output = mw.html.create('div'):addClass('topschedule')
		:attr('id', 'top-schedule')
		:css('display', 'none')
	h.printContent(result, output)
	return output
end

function h.printContent(result, div)
	for i, row in ipairs(result) do
		local innerDiv = h.initializeInnerDiv(div, row, i)
		h.printHeader(innerDiv, row.EventLinked)
		if row.Is1v1 then
			h.printVs1v1(innerDiv, row.Matchup)
		else
			h.printVs(innerDiv, row.Matchup)
		end
		h.printTime(innerDiv, row)
	end
end

function h.initializeInnerDiv(div, row, i)
	local expiration = util_time.unixNumber(row.DateTime) + SECONDS_IN_HOUR
	local innerDiv = div:tag('div')
		:addClass('topschedule-box')
		:attr('data-expiration', expiration)
		:attr('data-i',i)
	return innerDiv
end

function h.printHeader(div, event)
	div:tag('div')
		:addClass('topschedule-header')
		:wikitext(event)
end

function h.printVs1v1(div, matchup)
	local vsOuter = div:tag('div')
		:addClass('topschedule-vs-1v1')
		:attr("data-nosnippet", "true")
		:wikitext(matchup)
end

function h.printVs(div, matchup)
	local vsOuter = div:tag('div')
		:addClass('topschedule-vs')
		:attr("data-nosnippet", "true")
		:wikitext(matchup)
end

function h.printTime(div, row)
	local timeOuter = div:tag('div')
		:addClass('topschedule-time plainlinks')
		:attr("data-nosnippet", "true")
		:wikitext(row.Countdown, ' • ', row.Calendar)
end

return p