[checked revision] | [checked revision] |
([ST] add to `this` instead of `total` cos we do it before we sort i guess?) |
([ST] jk) |
||
Line 337: | Line 337: | ||
if not tab then return end |
if not tab then return end |
||
local f_sort = util_tournament.getSortMethod(tltype) |
local f_sort = util_tournament.getSortMethod(tltype) |
||
⚫ | |||
for _, team in ipairs(tab) do |
for _, team in ipairs(tab) do |
||
h.copyThisToTotal(tab[team].total, tab[team].this) |
h.copyThisToTotal(tab[team].total, tab[team].this) |
||
f_sort(tab[team].total) |
f_sort(tab[team].total) |
||
end |
end |
||
⚫ | |||
end |
end |
||
Line 359: | Line 359: | ||
} |
} |
||
for _, teamstr in ipairs(weekOne) do |
for _, teamstr in ipairs(weekOne) do |
||
− | h.addAdjustmentsToWeekOne(weekOne[teamstr]. |
+ | h.addAdjustmentsToWeekOne(weekOne[teamstr].total, teamstr, arg_adjust) |
end |
end |
||
end |
end |
Revision as of 11:45, 14 June 2021
Documentation for this module may be created at Module:Timeline/doc
local util_args = require('Module:ArgsUtil')
local util_cargo = require('Module:CargoUtil')
local util_esports = require('Module:EsportsUtil')
local util_footnote = require('Module:FootnoteUtil')
local util_html = require('Module:HtmlUtil')
local util_map = require('Module:MapUtil')
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 TOGGLES = {
order = { 'cu', 'wk' },
displays = {
wk = 'Weekly',
cu = 'Cumulative'
},
}
local DISPLAY_TYPE = {
record = 'record',
recordwithgames = 'record',
points = 'points',
recordwithpoints = 'recordwithpoints',
bo2nopoints = 'recordbo2',
}
--[[
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 = {}
local p = {}
function p.fromCargo(frame)
local args = util_args.merge()
h.verifyParams(args)
local tltype = lang:lc(args.orderby)
local overviewPage = util_esports.getOverviewPage(args.page)
local data = h.getData(overviewPage, args, tltype)
if #data == 0 then return '' end
return p.main(data, args, tltype)
end
function p.fromArgs(frame)
local args = util_args.merge()
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
function h.verifyParams(args)
if not args.orderby then
error('Missing |orderby=')
end
end
function h.getData(page, args, tltype)
local teamlist = h.getTeamlist(page, args.teamlist, args.onlygroup)
local query = h.getQuery(page, args)
local result = util_cargo.queryAndCast(query)
local data = h.parseResult(result, tltype, args, teamlist)
return data
end
function h.getTeamlist(page, teamlist, group)
if teamlist then
return util_map.split(teamlist,nil,m_team.teamlinkname)
elseif group then
return util_tournament.getGroupTeamList(page, group)
end
return nil
end
-- cargo
function h.getQuery(page, args)
local query = {
tables = 'MatchSchedule',
fields = h.getFields(args),
where = h.getWhere(page, args),
orderBy = 'N_Page ASC, N_TabInPage ASC, N_MatchInTab ASC',
groupBy = 'MatchId',
}
return query
end
function h.getFields(args)
local tbl = {
'Team1',
'Team2',
'Team1Final',
'Team2Final',
'Winner [number]',
'Team1Score [number]',
'Team2Score [number]',
'Team1Points [number]',
'Team2Points [number]',
'Team1PointsTB [number]',
'Team2PointsTB [number]',
'Tab',
'N_MatchInTab [number]',
'N_TabInPage [number]',
'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.getWhere(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),
'IsTiebreaker="0"',
}
return util_table.concat(tbl, ' AND ')
end
-- data processing
function h.parseResult(result, tltype, args, teamlist)
if not teamlist then
teamlist = h.getTeamList(result)
end
local gamesByTab = h.splitResultByTab(result)
local weeks = args.weeks or h.countWeeks(gamesByTab)
local starting_week = args.starting_week and tonumber(args.starting_week) or 1
local dataByTab = util_map.arraySafe(gamesByTab, h.parseTab)
h.normalizeTeams(dataByTab, teamlist)
h.addAndAdjustFirstTabTotals(dataByTab[1], tltype, teamlist, args)
h.addRestTabTotals(dataByTab, tltype)
for _, tab in ipairs(dataByTab) do
h.sortTab(tab)
end
h.pruneExtraWeeks(dataByTab, tonumber(weeks), starting_week)
h.sortFinalTab(dataByTab[#dataByTab], args.finalorder, args.finalplaces)
h.addPlaceAndDiffs(dataByTab)
util_map.rowsInPlace(dataByTab, h.addClassesToTab)
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
tbl.TBD = nil -- undefine teams that dont actually exist
local teamlist = {}
for team, _ in pairs(tbl) do
teamlist[#teamlist+1] = team
end
return teamlist
end
function h.splitResultByTab(result)
local gamesByTab = {}
local lastindex = ''
local thistab = 0
for _, row in ipairs(result) do
if row.Index ~= lastindex then
thistab = thistab + 1
lastindex = row.Index
gamesByTab[thistab] = { name = row.Tab }
end
gamesByTab[thistab][#gamesByTab[thistab]+1] = row
end
return gamesByTab
end
function h.countWeeks(gamesByTab)
for k, v in ipairs(gamesByTab) do
if not v[1].Winner then
return math.max(1, k-1)
end
end
return 9999
end
function h.parseTab(tab)
local ret = { name = tab.name }
for _, row in ipairs(tab) do
h.parseRow(ret, row)
end
return ret
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
h.addRowTotals(row, tab[Team1].this, tab[Team2].this)
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,
t = 0,
wg = 0,
lg = 0,
p = 0,
tb = 0,
w_unadj = 0,
t_unadj = 0,
l_unadj = 0,
p_unadj = 0,
tb_unadj = 0,
wg_unadj = 0,
lg_unadj = 0,
hiddenpoints = 0,
},
total = {
w = 0,
l = 0,
t = 0,
wg = 0,
lg = 0,
p = 0,
tb = 0,
w_unadj = 0,
t_unadj = 0,
l_unadj = 0,
p_unadj = 0,
tb_unadj = 0,
wg_unadj = 0,
lg_unadj = 0,
hiddenpoints = 0,
},
footnotes = {}
}
return
end
function h.addRowTotals(row, this1, this2)
this1.w = this1.w + (row.Winner == 1 and 1 or 0)
this1.l = this1.l + (row.Winner == 2 and 1 or 0)
this1.t = this1.t + (row.Winner == 0 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.t = this2.t + (row.Winner == 0 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
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.addAndAdjustFirstTabTotals(tab, tltype, teamlist, args)
if not tab then return end
local f_sort = util_tournament.getSortMethod(tltype)
for _, team in ipairs(tab) do
h.copyThisToTotal(tab[team].total, tab[team].this)
f_sort(tab[team].total)
end
h.addArgsToWeekOneTotals(tab, teamlist, args)
end
function h.addArgsToWeekOneTotals(weekOne, teamlist, args)
-- for GLL format, they had points count from results in the previous split.
-- these have to be manually added by an editor via the "adjustment" parameters, same as Module:Standings
-- here we add them ONLY to the total for week 1, and not to the individual week.
-- this must be done after the total for week 1 is copied from 'this' but before the rest of the weeks are added
if not weekOne then return end
local arg_adjust = {
w = h.getAdjustmentArgData(args.wadjust),
l = h.getAdjustmentArgData(args.ladjust),
t = h.getAdjustmentArgData(args.tadjust),
wg = h.getAdjustmentArgData(args.wgadjust),
lg = h.getAdjustmentArgData(args.lgadjust),
p = h.getAdjustmentArgData(args.pointadjust),
}
for _, teamstr in ipairs(weekOne) do
h.addAdjustmentsToWeekOne(weekOne[teamstr].total, teamstr, arg_adjust)
end
end
function h.getAdjustmentArgData(arg)
if not arg then
return {}
end
local tbl = {}
for val in arg:gmatch('%(%(%((.-)%)%)%)') do
k, v = val:match('(.*)===(.*)')
tbl[m_team.teamlinkname(k)] = v
end
return tbl
end
function h.addAdjustmentsToWeekOne(team, teamstr, arg_adjust)
team.w = team.w + (tonumber(arg_adjust.w[teamstr] or 0))
team.l = team.l + (tonumber(arg_adjust.l[teamstr] or 0))
team.t = team.t + (tonumber(arg_adjust.t[teamstr] or 0))
team.wg = team.wg + (tonumber(arg_adjust.wg[teamstr] or 0))
team.lg = team.lg + (tonumber(arg_adjust.lg[teamstr] or 0))
end
function h.addRestTabTotals(dataByTab, tltype)
local f_sort = util_tournament.getSortMethod(tltype)
for i, tab in ipairs(dataByTab) do
if i == 1 then
-- pass
else
h.addLaterTabTotals(tab, dataByTab[i-1], f_sort)
end
end
end
function h.copyThisToTotal(total, this)
total.w = this.w
total.l = this.l
total.t = this.t
total.wg = this.wg
total.lg = this.lg
total.p = this.p
total.tb = this.tb
end
function h.addLaterTabTotals(tab, last, f_sort)
for _, team in ipairs(tab) do
h.addThisAndLastAsTotal(tab[team].total, tab[team].this, last[team].total)
f_sort(tab[team].total)
end
end
function h.addThisAndLastAsTotal(total, this, last)
total.w = this.w + last.w
total.l = this.l + last.l
total.t = this.t + last.t
total.wg = this.wg + last.wg
total.lg = this.lg + last.lg
total.p = this.p + last.p
total.tb = this.tb + last.tb
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.pruneExtraWeeks(data, weeks, startingWeek)
if not weeks then return end
for k, _ in ipairs(data) do
if k > weeks then
data[k] = nil
end
-- in the event that we have a startingWeek > 1, this pushes the earlier weeks up
-- and then sets the later weeks to nil once it gets to the end
-- if startingWeek == 1 then this does nothing
data[k] = data[k + startingWeek - 1]
end
end
function h.sortFinalTab(tab, finalorder, places)
if not finalorder then return end
local order_tbl = util_text.split(finalorder)
local places_tbl = places and util_text.split(places) 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
h.addFirstTabPlaceAndDiffs(tab)
else
h.addLaterTabPlaceAndDiffs(tab, dataByTab[i-1])
end
end
return
end
function h.addFirstTabPlaceAndDiffs(tab)
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
end
function h.addLaterTabPlaceAndDiffs(tab, lasttab)
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 = lasttab[team].place - place
end
end
function h.addClassesToTab(tab)
for _, team in ipairs(tab) do
h.addClassesToTeam(tab[team])
end
end
function h.addClassesToTeam(team)
if not team.classes then team.classes = {} end
team.classes[#team.classes+1] = h.getTeamPercentageClass(team.total)
end
function h.getTeamPercentageClass(total)
if total.w == total.l then
return 'tl-row-equal'
elseif total.w > total.l then
return 'tl-row-above'
end
return 'tl-row-below'
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,
t = tonumber(args[('w%st%s'):format(w,t)] or '') or 0,
wg = 0,
lg = 0,
p = 0,
tb = 0,
},
total = { w = 0, l = 0, t = 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.addAndAdjustFirstTabTotals(dataByTab[1], tltype, teamlist, args)
h.addRestTabTotals(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
-- 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_map.split(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_footnote.init()
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_footnote.printTexts(output)
return output
end
function h.makeTable(div, tab, tltype, colorsPlace, colorsBg)
local tbl = div:tag('table')
:addClass('wikitable')
:addClass('timeline')
h.printHeading(tbl, tab.name)
for i, team in ipairs(tab) do
h.printRow(tbl, tab[team], tltype, colorsPlace[i], colorsBg[i])
end
end
function h.printHeading(tbl, name)
tbl:tag('tr'):tag('th'):attr('colspan','4'):wikitext(name)
return
end
function h.printRow(tbl, teamdata, tltype, colorPlace, colorBg)
local tr = tbl
:tag('tr')
:addClass(colorBg)
h.printRowClasses(tr, teamdata.classes)
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}))
:attr('title', m_team.teamname(teamdata.team))
h.makeCell(tr, tltype, teamdata)
return
end
function h.printRowClasses(tr, classes)
if not classes then return end
for _, c in ipairs(classes) do
tr:addClass(c)
end
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 == 'recordbo2' then
wk = ('%s-%s-%s'):format(data.this.w, data.this.t, data.this.l)
cu = ('%s-%s-%s'):format(data.total.w, data.total.t, 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_footnote.tag(td_wk, footnotes)
local td_cu = tr:tag('td')
:addClass(class)
:wikitext(cu)
util_toggle.oflCellClasses(td_cu, TOGGLES, 'cu')
util_footnote.tag(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
return p