Module:Timeline

local util_args = require('Module:ArgsUtil') local util_cargo = require('Module:CargoUtil') local util_esports = require('Module:EsportsUtil') local util_footnotes = require('Module:FootnoteUtil') local util_html = require('Module:HtmlUtil') local util_math = require('Module:MathUtil') local util_table = require('Module:TableUtil') local util_text = require('Module:TextUtil') local util_title = require('Module:TitleUtil') local util_toggle = require('Module:ToggleUtil') local util_tournament = require('Module:TournamentUtil') local util_vars = require('Module:VarsUtil')

local m_team = require('Module:Team')

local lang = mw.getLanguage('en') local sep = '%s*,%s*'

local TOGGLES = { order = { 'cu', 'wk' }, displays = { wk = 'Weekly', cu = 'Cumulative' }, }

local DISPLAY_TYPE = { record = 'record', recordwithgames = 'record', points = 'points', recordwithpoints = 'recordwithpoints', bo2nopoints = 'record', } --	Table structure:	{		{			name = Tab 1,			team1, team2, team3,			team1 = { this = { w =, l = , p = }, total = {}, place = 1, diff = 0 }		},		{			name = Tab 2,			team1, team2, team3,			team1 = { this = {}, total = {}, place = , diff = }		}	}	first construct all tabs, except for standings & diffs	then determine standings & diffs

local h = {}

function h.getData(page, args, tltype) local teamlist = h.getTeamlist(page, args.teamlist, args.onlygroup) local query = h.makeQuery(page, args) local result = util_cargo.queryAndCast(query) local data = h.parseResult(result, tltype, args.weeks, teamlist, args.finalorder, args.finalplaces) return data end

function h.getTeamlist(page, teamlist, group) if teamlist then return util_args.splitAndMap(teamlist,nil,m_team.teamlinkname) elseif group then return util_tournament.getGroupTeamList(page, group) end return nil end

-- cargo function h.makeQuery(page, args) local query = { tables = 'MatchSchedule', fields = h.makeFields(args), where = h.makeWhere(page, args), orderBy = 'N_Page ASC, N_TabInPage ASC, N_MatchInTab ASC', limit = 999, groupBy = 'UniqueMatch', types = { Winner = 'number', Team1Score = 'number', Team2Score = 'number', Team1Points = 'number', Team2Points = 'number', Team1PointsTB = 'number', Team2PointsTB = 'number', N_MatchInTab = 'number', N_TabInPage = 'number' }	}	return query end

function h.makeFields(args) local tbl = { 'Team1', 'Team2', 'Team1Final', 'Team2Final', 'Winner', 'Team1Score', 'Team2Score', 'Team1Points', 'Team2Points', 'Team1PointsTB', 'Team2PointsTB', 'Tab', 'N_MatchInTab', 'N_TabInPage', 'CONCAT(N_Page,"_",N_TabInPage)=Index' }	if not util_args.castAsBool(args.nofootnotes) then util_table.mergeArrays(tbl, { 'Team1Footnote', 'Team2Footnote' }) end return tbl end

function h.makeWhere(page, args) local tbl = { ('OverviewPage="%s"'):format(page), -- we will actually prune the data later as if we had no where condition here -- because if data is split across pages then we might get too many things here that we don't want -- but certainly this is an upper bound on the amount of data we need -- so i'll leave this condition here because it isn't hurting anything -- and technically it is potentially allowing us to do less computation overall util_cargo.whereFromArg('N_TabInPage <="%s"', args.weeks) }	return util_table.concat(tbl, ' AND ') end

-- data processing function h.parseResult(result, tltype, weeks, teamlist, finalorder, finalplaces) if not teamlist then teamlist = h.getTeamList(result) end local dataByTab = h.splitResultByTab(result) h.normalizeTeams(dataByTab, teamlist) h.addTabTotals(dataByTab, tltype) for _, tab in ipairs(dataByTab) do		h.sortTab(tab) end if weeks then h.pruneExtraWeeks(dataByTab, tonumber(weeks)) end h.sortFinalTab(dataByTab[#dataByTab], finalorder, finalplaces) h.addPlaceAndDiffs(dataByTab) return dataByTab end

function h.getTeamList(result) local tbl = {} for _, row in ipairs(result) do		if not tbl[row.Team1Final] then tbl[row.Team1Final] = true end if not tbl[row.Team2Final] then tbl[row.Team2Final] = true end end local teamlist = {} for team, _ in pairs(tbl) do		teamlist[#teamlist+1] = team end return teamlist end

function h.splitResultByTab(result) local tbl = {} local lastindex = '' local thistab = 0 for _, row in ipairs(result) do		if row.Index ~= lastindex then thistab = thistab + 1 lastindex = row.Index tbl[thistab] = { name = row.Tab } end h.parseRow(tbl[thistab], row) end return tbl end

function h.parseRow(tab, row) local Team1 = row.Team1Final local Team2 = row.Team2Final h.initializeTeam(tab, Team1) h.initializeTeam(tab, Team2) if row.Winner then local this1 = tab[Team1].this local this2 = tab[Team2].this this1.w = this1.w + (row.Winner == 1 and 1 or 0) this1.l = this1.l + (row.Winner == 2 and 1 or 0) this1.p = this1.p + (row.Team1Points or 0) this1.wg = this1.wg + (row.Team1Score or 0) this1.lg = this1.lg + (row.Team2Score or 0) this1.tb = this1.tb + (row.Team1PointsTB or 0) this2.w = this2.w + (row.Winner == 2 and 1 or 0) this2.l = this2.l + (row.Winner == 1 and 1 or 0) this2.p = this2.p + (row.Team2Points or 0) this2.wg = this2.wg + (row.Team2Score or 0) this2.lg = this2.lg + (row.Team1Score or 0) this2.tb = this2.tb + (row.Team2PointsTB or 0) end tab[Team1].team = row.Team1 tab[Team2].team = row.Team2 tab[Team1].footnotes[#tab[Team1].footnotes+1] = row.Team1Footnote tab[Team2].footnotes[#tab[Team2].footnotes+1] = row.Team2Footnote return end

function h.initializeTeam(tab, team) if tab[team] then return end tab[team] = { teamfinal = team, this = { w = 0, l = 0, wg = 0, lg = 0, p = 0, tb = 0 }, total = { w = 0, l = 0, wg = 0, lg = 0, p = 0, tb = 0 }, footnotes = {} }	return end

function h.normalizeTeams(dataByTab, teamlist) for _, tab in ipairs(dataByTab) do		for i, team in ipairs(teamlist) do			tab[i] = team if not tab[team] then h.initializeTeam(tab, team) end end end h.fixRenamedTeams(dataByTab) return end

function h.fixRenamedTeams(dataByTab) -- we only populate teamfinal when we initialize a team -- so now we need to populate actually "team" local week = #dataByTab while week >= 1 do		local tab = dataByTab[week] local nw = week + 1 if dataByTab[nw] then for _, team in ipairs(tab) do				if not tab[team].team then tab[team].team = dataByTab[nw][team].team end end else for _, team in ipairs(tab) do				if not tab[team].team then tab[team].team = tab[team].teamfinal end end end week = week - 1 end end

function h.addTabTotals(dataByTab, tltype) local f_sort = util_tournament.getSortMethod(tltype) for i, tab in ipairs(dataByTab) do		if i == 1 then for _, team in ipairs(tab) do				local total = tab[team].total -- alias for convenience local this = tab[team].this -- also alias total.w = this.w				total.l = this.l				total.wg = this.wg				total.lg = this.lg				total.p = this.p				total.tb = this.tb				f_sort(total) end else for _, team in ipairs(tab) do				local total = tab[team].total -- alias for convenience local this = tab[team].this local last = dataByTab[i-1][team].total total.w = this.w + last.w				total.l = this.l + last.l				total.wg = this.wg + last.wg				total.lg = this.lg + last.lg				total.p = this.p + last.p				total.tb = this.tb + last.tb				f_sort(total) end end end return end

function h.sortTab(tab) table.sort(tab,		function(a,b)			if tab[a].total.sort == tab[b].total.sort then				return lang:lc(a) < lang:lc(b)			else				return tab[a].total.sort > tab[b].total.sort			end		end	) return end

function h.sortFinalTab(tab, finalorder, places) if not finalorder then return end local order_tbl = util_text.split(finalorder,sep) local places_tbl = places and util_text.split(places, sep) or {} for k, v in ipairs(order_tbl) do		local team = m_team.teamlinkname(v) tab[k] = team tab[team].total.sort = places_tbl[k] or k	end return end

function h.addPlaceAndDiffs(dataByTab) for i, tab in ipairs(dataByTab) do		if i == 1 then local place = 0 local lastsort for k, team in ipairs(tab) do				if tab[team].total.sort ~= lastsort then place = k					lastsort = tab[team].total.sort end tab[team].place = place tab[team].diff = 0 end else local place = 0 local lastsort for k, team in ipairs(tab) do				if tab[team].total.sort ~= lastsort then place = k					lastsort = tab[team].total.sort end tab[team].place = place -- it's better to have a lower place number tab[team].diff = dataByTab[i-1][team].place - place end end end return end

-- from args function h.getDataFromArgs(args, tltype) local dataByTab = {} local pointskey = tltype == 'points' and 'p' or 'tb' local w = 1 local t = 1 while args[('w%steam%s'):format(w,t)] do		dataByTab[w] = { name = ('Week %s'):format(w) } while args[('w%steam%s'):format(w,t)] do			local team = m_team.teamlinkname(args[('w%steam%s'):format(w,t)]) dataByTab[w][t] = team dataByTab[w][team] = { team = team, this = { w = tonumber(args[('w%sw%s'):format(w,t)] or '') or 0, l = tonumber(args[('w%sl%s'):format(w,t)] or '') or 0, wg = 0, lg = 0, p = 0, tb = 0, },				total = { w = 0, l = 0, p = 0, tb = 0 }, }			dataByTab[w][team].this[pointskey] = tonumber(args[('w%spt%s'):format(w,t)] or '') or 0 t = t + 1 end t = 1 w = w + 1 end h.addTabTotals(dataByTab, tltype) if util_args.castAsBool(args.isover) then h.sortFinalTabFromArgs(dataByTab[#dataByTab]) end h.addPlaceAndDiffs(dataByTab) return dataByTab end

function h.sortFinalTabFromArgs(tab) for k, v in ipairs(tab) do		tab[v].total.sort = k	end return end

function h.pruneExtraWeeks(data, weeks) for k, v in ipairs(data) do		if k > weeks then data[k] = nil end end end

-- data has been gotten !

function h.countParts(data) return #data, #data[1] end

function h.getColors(args, nTabs, nTeams) local tbl = { init = args.places and util_args.splitAndMap(args.places,',',h.getClassName) or {} }	for i = 1, nTabs do		tbl[i] = {} for j = 1, nTeams do			local arg = args[('w%sbg%s'):format(i,j)] if arg then tbl[i][j] = h.getClassName(arg) end end end return tbl end

function h.getClassName(class) -- prepend every individual "word" in the class with 'standings-' return class:gsub('([^ ]+)','standings-%1') end

function h.makeOutput(data, tltype, colors) util_footnotes.initializeAllFootnotes if not DISPLAY_TYPE[tltype] then error('Invalid orderby') end tltype = DISPLAY_TYPE[tltype] local output = mw.html.create('div'):addClass('timeline-section') h.makeToggler(output) local div = output:tag('div') for i, tab in ipairs(data) do		h.makeTable(div, tab, tltype, colors.init, colors[i]) end util_footnotes.printFootnotes(output) return output end

function h.makeTable(div, tab, tltype, colorsPlace, colorsBg) local tbl = div:tag('table') :addClass('wikitable') :addClass('timeline') h.addHeading(tbl, tab.name) for i, team in ipairs(tab) do		h.addRow(tbl, tab[team], tltype, colorsPlace[i], colorsBg[i]) end end

function h.addHeading(tbl, name) tbl:tag('tr'):tag('th'):attr('colspan','4'):wikitext(name) return end

function h.addRow(tbl, teamdata, tltype, colorPlace, colorBg) local tr = tbl :tag('tr') :addClass(colorBg) util_esports.addTeamHighlighter(tr, teamdata.teamfinal) tr:tag('td') :addClass('timeline-place') :addClass(colorPlace) :wikitext(teamdata.place) h.printDiff(tr, teamdata.diff) tr:tag('td') :addClass('timeline-team') :wikitext(m_team.onlyimage(teamdata.team,{size=45})) h.makeCell(tr, tltype, teamdata) return end

function h.printDiff(tr, diff) local td = tr:tag('td') util_esports.printDiff(td, diff) return end

function h.makeCell(tr, tltype, data) local wk, cu, class if tltype == 'points' then wk = data.this.p		cu = data.total.p		class = 'timeline-points timeline-score' elseif tltype == 'record' then wk = ('%s-%s'):format(data.this.w, data.this.l)		cu = ('%s-%s'):format(data.total.w, data.total.l)		class = 'timeline-record timeline-score' elseif tltype == 'recordwithpoints' then wk = ("%s-%s (%s)"):format(data.this.w, data.this.l, util_math.printWithSign(data.this.tb)) cu = ("%s-%s (%s)"):format(data.total.w, data.total.l, util_math.printWithSign(data.total.tb)) class = 'timeline-recordwithpoints timeline-score' end h.printCell(tr, wk, cu, class, data.footnotes) return end

function h.printCell(tr, wk, cu, class, footnotes) local td_wk = tr:tag('td') :addClass(class) :wikitext(wk) util_toggle.oflCellClasses(td_wk, TOGGLES, 'wk') util_footnotes.tagFootnotes(td_wk, footnotes) local td_cu = tr:tag('td') :addClass(class) :wikitext(cu) util_toggle.oflCellClasses(td_cu, TOGGLES, 'cu') util_footnotes.tagFootnotes(td_cu, footnotes) return end

function h.makeToggler(tbl) local div = tbl:tag('div') :addClass('toggle-button') util_toggle.printOptionFromListTogglers(div, TOGGLES) util_html.clear(tbl) return end

local p = {} function p.fromCargo(frame) local args = util_args.merge(true) local tltype = lang:lc(args.orderby) local overviewPage = util_esports.getOverviewPage(args.page) local data = h.getData(overviewPage, args, tltype) if not next(data) then return '' end return p.main(data, args, tltype) end

function p.fromArgs(frame) local args = util_args.merge(true) local tltype = lang:lc(args.orderby) local data = h.getDataFromArgs(args, tltype) return p.main(data, args, tltype) end

function p.main(data, args, tltype) local nTabs, nTeams = h.countParts(data) local colors = h.getColors(args, nTabs, nTeams) return h.makeOutput(data, tltype, colors) end

return p