Jump to content

Module:PHL sports overall tally

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by McVahl (talk | contribs) at 10:32, 29 March 2020 (+support on teams with custom short name; sort by rank if overall pts are equal). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

require('Module:No globals')

local p = {}

local div = {
	senior = { 'M', 'W', 'C'},
	junior = { 'B', 'G', 'C'}
}

local evt = { 
	['BSKB'] = {  1, 'bball',    'Basketball' },
	['3X3B'] = {  2, '3x3bball', '3x3 basketball' },
	['INVB'] = {  3, 'vball',    'Volleyball (indoor)' },
	['BCVB'] = {  4, 'bvolley',  'Volleyball (beach)' },
	['SWMM'] = {  5, 'swim',	 'Swimming' },
	['CHSS'] = {  6, 'chess',    'Chess' },
	['TNNS'] = {  7, 'tennis',   'Tennis' },
	['TBTN'] = {  8, 'tbltenn',  'Table tennis' },
	['BDMT'] = {  9, 'badmnt',   'Badminton' },
	['TKWD'] = { 10, 'taekwd',   'Taekwondo' },
	['JUDO'] = { 11, 'judo',     'Judo' },
	['BSBL'] = { 12, 'bsball',   'Baseball' },
	['SFBL'] = { 13, 'sfball',   'Softball' },
	['FTBL'] = { 14, 'ftball',   'Football' },
	['ATHL'] = { 15, 'track',    'Athletics' },
	['FENC'] = { 16, 'fencing',  'Fencing' }
}

local colors = {
	['1st'] = '#FFCE09',
	['2nd'] = '#BABBBC',
	['3rd'] = '#D6A461',
	['WD']	= '#FFBBBB'
}

local status = {
	'Champion', 'Runner-up', 'Third place', 'No team',
	H = 'Host'
}

local showstatus, showmedals, ptsonly, totals = false, false, false, false
local args, division, ptsbyrank = nil, nil, nil

local function isnotempty(s)
	return s and s:match('^%s*(.-)%s*$') ~= ''
end

local function stripwhitespace(text)
	return text:match("^%s*(.-)%s*$")
end

local function findchamp(teams, t, r)
	local found = teams[t].res[r].pts == ptsbyrank[1]
	if (found or (t == 1)) then return found else return findchamp(teams, t - 1, r) end
end

local function getevtdisp(teams)
	local r = 1
	local evt_disp = {}
	for ke, ve in pairs(evt) do
		for kd, vd in pairs(div[division]) do
			local found = findchamp(teams, #teams, r)
			table.insert(evt_disp, found)
			r = r + 1
		end
	end
	return evt_disp
end

local function getindex(map, value)
	for k, v in pairs(map) do
		if v == value then return tonumber(k) end
	end
	return tonumber(99)
end

local function getbg(pts, raw)
	if (ptsbyrank[1] == pts) then return colors['1st']
	elseif (ptsbyrank[2] == pts) then return colors['2nd']
	elseif (ptsbyrank[3] == pts) then return colors['3rd']
	elseif (raw == 'WD') then return colors.WD
	else return nil
	end
end

local function getdisptext(pts,raw)
	if (pts ~= 0) then return pts
	elseif (string.match(raw,'([?|WD])')) then return raw
	else return ptsbyrank[4]
	end
end

local function getteamtext(team,code)
	local stat = args['status_'..code] or nil
	if stat ~= nil and status[stat] ~= nil then
		showstatus = true
		return team..'&nbsp;<b>('..stat:upper()..')<b>'
	end
	return team
end

local function prefillvalues(teams)
	local tally = {}
	for kt, vt in pairs(teams) do
		local res, subtotal, overall, gold, silver, bronze = {}, {}, 0, 0, 0, 0
		
		local evt_disp = getevtdisp(teams)
		for kr, vr in pairs(vt.res) do
			if evt_disp[kr] then
				subtotal[vr.div] = (tonumber(subtotal[vr.div]) or 0) + vr.pts
				if (vr.pts == ptsbyrank[1]) then gold = gold + 1
				elseif (vr.pts == ptsbyrank[2]) then silver = silver + 1
				elseif (vr.pts == ptsbyrank[3]) then bronze = bronze + 1 end
				table.insert(res, vr)
			end
		end
		for kd, vd in pairs(div[division]) do overall = overall + tonumber(subtotal[vd] or 0) end

		if ptsonly then
			overall = tonumber(args['pts_'..vt.code]) or overall
		end
		
		table.sort(res, function (a, b) return evt[a.evt][1] < evt[b.evt][1] or (evt[a.evt][1] == evt[b.evt][1] and getindex(div[division],a.div) < getindex(div[division],b.div)) end)
		table.insert(tally, { rank = vt.rank, code = vt.code, team = vt.name, res = res, subtotal = subtotal, overall = overall, medals = { gold, silver, bronze } })
	end
	table.sort(tally, function (a, b)
		if not ptsonly then
			return a.overall > b.overall or (a.overall == b.overall
				and (a.medals[1] > b.medals[1]
				or (a.medals[1] == b.medals[1] and a.medals[2] > b.medals[2])
				or (a.medals[1] == b.medals[1] and a.medals[2] == b.medals[2] and a.medals[3] > b.medals[3])
				or (a.medals[1] == b.medals[1] and a.medals[2] == b.medals[2] and a.medals[3] == b.medals[3] and a.code < b.code)))
		else return a.overall > b.overall or (a.overall == b.overall and a.rank < b.rank) end
		end)
	return tally
end

local function medaltable(frame,tally)
	local mMedals = require('Module:Medals table')
	for kt, vt in pairs(tally) do
		local name = args['name_'..vt.code]
		if not isnotempty(name) or name == nil then
			args['name_'..vt.code] = vt.name
		end
		if stripwhitespace(args['status_'..vt.code] or '') == 'H' then
			args['host_'..vt.code] = 'yes'
			args['host'] = 'Season host'
		end
		args['gold_'..vt.code] = vt.medals[1]
		args['silver_'..vt.code] = vt.medals[2]
		args['bronze_'..vt.code] = vt.medals[3]
	end
	args['team'] = 'Team'
	args['event'] = division
	args['flag_template'] = args['team_template'] or 'UAAPteam'
	return mMedals.createTable(frame, args)
end

local function buildtable(frame,teams)
	local root = mw.html.create()
	local footer = mw.html.create()
	local tally = prefillvalues(teams)
	
	if showmedals then
		return medaltable(frame,tally)
	end
	
	root = root:tag('table')
		:addClass('wikitable')
		:addClass('plainrowheaders')
		:css('font-size', not ptsonly and '95%' or '100%')
		:css('text-align', 'center')
	
	-- header row (1)
	local evts = tally[1].res
	local row = root:tag('tr')
	local celltype = not ptsonly and 'th' or 'td'
	local showc = false
	
	if not ptsonly then
		row:tag('th')
			:attr('scope', 'col')
			:attr('colspan', '2')
		
		-- column spanning by event
		local prevspan, prevcell, prevevt = 0, nil, nil
		for ke, ve in pairs(evts) do
			local evtname = evt[ve.evt][3]
			if (ve.div == 'C') then showc = true end
			if not totals then
				if (prevevt == ve.evt) then
					prevspan = prevspan + 1
					prevcell
						:attr('colspan', prevspan)
				else
					prevspan = 1
					prevcell = row:tag('th')
						:attr('scope', 'col')
						:wikitext(string.format('[[File:%s pictogram.svg|20px|link=|%s]]', evtname, evtname))
					
					prevevt = ve.evt
				end
			end
		end

		row:tag('th')
			:attr('scope', 'col')
			:attr('colspan', showc and 4 or 3)
			:css('border-left-width', '3px')
			:wikitext('Total')
	end
	
	-- header row (2)
	row = root:tag('tr')
	row:tag('th')
			:attr('scope', 'col')
			:attr('width', '50px')
			:wikitext('Rank')
		:tag('th')
			:attr('scope', 'col')
			:attr('width', '90px')
			:wikitext('Team')
	
	if not (ptsonly or totals) then
		for ke, ve in pairs(evts) do
			row:tag('th')
				:attr('scope', 'col')
				:attr('width', '22px')
				:wikitext(ve.div)
		end
	end
	
	if not ptsonly then
		for kd, vd in pairs(div[division]) do
			if (not showc and vd == 'C') then break
			else row:tag('th')
				:attr('scope', 'col')
				:attr('width', '22px')
				:css('border-left-width', (kd == 1) and '3px' or nil)
				:wikitext(vd)
			end
		end
	end
	
	row:tag('th'):attr('scope', 'col'):wikitext(ptsonly and 'Points' or 'Overall')
			
	-- team row
	for ka, va in pairs(tally) do
		local overall = 0

		row = root:tag('tr')
		row:tag(celltype)
				:attr('scope', 'row')
				:css('text-align', 'center')
				:wikitext(ka)
			:tag(celltype)
				:attr('scope', 'row')
				:css('white-space', 'nowrap')
				:css('text-align', 'left')
				:wikitext(getteamtext(va.team,va.code))
		
		if not ptsonly then
			if not totals then
				for kr, vr in pairs(va.res) do
					row:tag('td')
						:css('background-color', getbg(vr.pts,vr.raw))
						:wikitext(getdisptext(vr.pts,vr.raw))
				end
			end
			
			for kd, vd in pairs(div[division]) do
				if (not showc and vd == 'C') then break
				else row:tag('td')
					:css('border-left-width', (kd == 1) and '3px' or nil)
					:wikitext(va.subtotal[vd] or 0)
				end
			end
		end
		
		row:tag(celltype)
			:attr('scope', 'row')
			:css('font-weight', 'bold')
			:css('text-align', 'center')
			:wikitext(va.overall)
	end
	
	local legend = footer:tag('div'):attr('class', 'reflist')
	if showstatus then
		legend:tag('span')
			:css('font-weight', 'bold')
			:wikitext('(H)')
			:done()
		:wikitext(' '..status.H)
		if ptsonly or totals then legend:wikitext('.') end
	end
	if not (ptsonly or totals) then
		local firsttag = not showstatus
		for kp, vp in pairs(ptsbyrank) do
			if firsttag == false then legend:wikitext('; ') end
			legend:tag('span')
				:css('margin', '0')
				:css('white-space', 'nowrap')
				:tag('span')
					:addClass('legend-text')
					:css('border', 'none')
					:css('padding', '1px .3em')
					:css('background-color', getbg(vp))
					:css('font-size', '95%')
					:css('border', '1px solid #BBB')
					:css('line-height', '1.25')
					:css('text-align', 'center')
					:wikitext(vp)
					:done()
				:wikitext(' = ' .. status[kp])
			firsttag = false
		end
		legend:wikitext('.')
	end
	
	return tostring(root)..tostring(footer)
end

function p.main(frame)
	local getArgs = require('Module:Arguments').getArgs
	local yesno = require('Module:Yesno')
	args = getArgs(frame)
	showmedals = yesno(args['showmedals'] or 'n')
	ptsonly = yesno(args['ptsonly'] or 'n')
	totals = yesno(args['totals'] or 'n')
	division = args['division'] or 'senior'
	ptsbyrank = { tonumber(args['pts_1st']) or 15, tonumber(args['pts_2nd']) or 12,	tonumber(args['pts_3rd']) or 10, '&mdash;' }
	
	local team_list = {}
	local tally = {}
	local template = args['team_template'] or 'UAAPteam'
	
	for ka, va in pairs(args) do
		-- Process team args
		local i = tostring(ka):match('^team([%d]+)%s*$') or '0'
		if (tonumber(i) > 0 and isnotempty(va)) then
			local res, t = {}, args['team' .. i]
			local sname = args['short_' .. t]
			local tname = args['name_' .. t] or
				(isnotempty(sname) and
					frame:expandTemplate{title = template, args = { t, division, name = sname } } or 
					frame:expandTemplate{title = template, args = { t, division, 'short' } }
				)
			for ke, ve in pairs(evt) do
				for kd, vd in pairs(div[division]) do
					local evt_pts = stripwhitespace(args[vd:lower()..'_'..ke..'_'..t] or args[t..'-'..vd:lower()..'_'.. ve[2]:lower()] or '')
					table.insert(res, { div = vd, evt = ke, raw = evt_pts, pts = tonumber(evt_pts) or 0 })
				end
			end
			table.insert(team_list, {rank = i, code = t, name = tname, res = res})
		end
	end

	return buildtable(frame,team_list)
end

return p