Module:PHL sports overall tally

require('strict')

local p = { TIE_REGEX = '^T([%d]+)%s*$' }

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

local evt = { {'BSKB', 'Basketball' }, {'3X3B', '3x3 basketball' }, {'INVB', 'Volleyball (indoor)' }, {'BCVB', 'Volleyball (beach)' }, {'SWMM', 'Swimming' }, {'CHSS', 'Chess' }, {'TNNS', 'Tennis' }, {'SFTN', 'Soft tennis' }, {'TBTN', 'Table tennis' }, {'BDMT', 'Badminton' }, {'TKWD', 'Taekwondo' }, {'JUDO', 'Judo' }, {'BSBL', 'Baseball' }, {'SFBL', 'Softball' }, {'FTBL', 'Football' }, {'ATHL', 'Athletics' }, {'FENC', 'Fencing' }, {'ESPT', 'Electronic sports' } }

local colors = { { 'gold',	 'Champion'}, { 'silver', 'Runner-up' }, { '#CC9966', 'Third place' }, ['WD'] = { '#FFBBBB', 'Withdrew' }, ['NT'] = { nil, 	 'No team' } }

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].rank == 1 or teams[t].res[r].raw == 'T1') if found or (t == 1) then return found else return findchamp(teams, t - 1, r) end end

local function countties(teams, r)	local tie = {} for kt, vt in pairs(teams) do		local raw = vt.res[r].raw or '' if (raw):match(p.TIE_REGEX) then tie[raw] = (tie[raw] or 0) + 1 end end return tie end

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

local function getevtindex(value) for k, v in pairs(evt) do		if v[1] == value then return tonumber(k) end end return tonumber(99) end

local function getvte(frame, args) local baselink = frame:getParent:getTitle if mw.title.getCurrentTitle.text == baselink then	baselink = '' end local vtetemplate = args['tname'] or (baselink ~= '' and (':' .. baselink)) or ''

if vtetemplate ~= '' then return frame:expandTemplate{ title = 'navbar', args = { mini=1, style='float:left', brackets=1, vtetemplate} } end return nil end

local function getbg(rank, raw) rank = tonumber(tostring(raw):match(p.TIE_REGEX) or rank) or 0 if rank > 0 and rank <= 3 then return colors[rank][1] elseif raw == 'WD' then return colors.WD[1] else return nil end end

local function comptiepts(ptsbyrank, trank, teamsize, ctie) local count = ctie['T'..trank] if count == 0 then return ptsbyrank[trank] end local limit, total = trank + count - 1, 0 if limit > teamsize then error('Invalid number of teams tied on #'..trank) end for i=trank, limit do		total = ptsbyrank[i] + total end return total / count end

local function getpts(rank, raw, ptsbyrank, tsize, ctie) local trank = tonumber(tostring(raw):match(p.TIE_REGEX)) or 0 if	  trank > 0 then return comptiepts(ptsbyrank,trank,tsize,ctie) or ptsbyrank.NT	elseif string.match(raw,'([?|WD])') then return raw else  return ptsbyrank[rank] or ptsbyrank.NT	end end

local function prefillvalues(args,teams,division,ptsbyrank,ovptsonly) 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,division,ptsbyrank) for kr, vr in pairs(vt.res) do			local evtprop = evt_disp[kr] if evtprop.show then local rank = tonumber(tostring(vr.raw):match(p.TIE_REGEX)) or vr.rank vr.pts = tonumber(getpts(rank,vr.raw,ptsbyrank,#teams,evtprop.ties)) or 0 subtotal[vr.div] = (tonumber(subtotal[vr.div]) or 0) + vr.pts if	  rank == 1 then gold = gold + 1 elseif rank == 2 then silver = silver + 1 elseif rank == 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 ovptsonly then overall = tonumber(args['pts_'..vt.code]) or overall 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) return a.overall > b.overall or (a.overall == b.overall and a.rank < b.rank) end) return tally end

local function medaltable(frame,args,tally,division,isfinal) local mMedals = require('Module:Medals table') local leadingLbl = 'Leads the '..(args['overall'] or 'general')..' championship tally' local legendL = isfinal and 'General champions' or leadingLbl args['team'] = 'Team' args['event'] = 'inst' args['legend_position'] = 'b'	args['flag_template'] = args['team_template'] or 'UAAPteam' args['host_note'] = string.format('; %s %s', frame:expandTemplate{title = 'color box', args = {'#E9D66B'}}, legendL) args['notes'] = isfinal and 'Results are final.' or 'Season in progress. Results are not yet final.' for kt, vt in pairs(tally) do		local name = args['name_'..vt.code] if kt == 1 and vt.overall ~= 0 then args['leading_'..vt.code] = 'yes' end if division == 'junior' and isnotempty(args['j_short_'..vt.code]) then args['name_'..vt.code] = frame:expandTemplate{title = args['flag_template'], args = { vt.code, division, inst = args['j_short_'..vt.code] } } elseif division == 'senior' and isnotempty(args['short_'..vt.code]) then args['name_'..vt.code] = frame:expandTemplate{title = args['flag_template'], args = { vt.code, division, inst = args['short_'..vt.code] } } elseif 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 return mMedals.createTable(frame, args) end

local function buildtable(frame,args,teams,division,ptsbyrank,showmedals,ovptsonly,sumsonly,isfinal) local tally = prefillvalues(args,teams,division,ptsbyrank,ovptsonly) if showmedals then return medaltable(frame,args,tally,division,isfinal) end local root = mw.html.create local footer = mw.html.create local abbr = mw.html.create('abbr') root = root:tag('table') :addClass('wikitable') :addClass('plainrowheaders') :css('font-size', (ovptsonly or sumsonly) and '100%' or '95%') :css('text-align', 'center') -- header row (1) local evts = tally[1].res local divs = div[division] local row = root:tag('tr') local celltype = not ovptsonly and 'th' or 'td' local showwg, showc, showhost, hidedivs = false, false, false, true if not ovptsonly then row:tag('th') :attr('scope', 'col') :attr('colspan', '2') :wikitext(getvte(frame,args)) abbr:attr('title', 'Mixed or co-ed'):wikitext(divs[3]) -- column spanning by event local prevspan, prevcell, prevevt = 0, nil, nil for ke, ve in pairs(evts) do			local evtname = evt[getevtindex(ve.evt)][2] if	  ve.div == divs[2] then showwg = true elseif ve.div == divs[3] then showc = true end if not sumsonly 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('', evtname, evtname)) prevevt = ve.evt end end end hidedivs = not showwg and not showc row:tag('th') :attr('scope', 'col') :attr('colspan', hidedivs and 1 or (((not showwg and showc) or (showwg and not showc)) and 4 or 5)) :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 (ovptsonly or sumsonly) then for ke, ve in pairs(evts) do			row:tag('th') :attr('scope', 'col') :attr('width', '22px') :wikitext(ve.div == divs[3] and tostring(abbr) or ve.div) end end if not ovptsonly then for kd, vd in pairs(divs) do			if (hidedivs or				(not showwg and vd == divs[2]) or				(not showc and vd == divs[3])) then break else row:tag('th') :attr('scope', 'col') :attr('width', '22px') :css('border-left-width', (kd == 1) and '3px' or nil) :wikitext(vd == divs[3] and tostring(abbr) or vd) end end end row:tag('th') :attr('scope', 'col') :css('border-left-width', hidedivs and '3px' or nil) :wikitext(ovptsonly and 'Points' or 'Overall') -- row spanning by points local prevpts, prevspan, prevrankcell, prevtotalcell = -1, 0, nil, nil -- team row for ka, va in pairs(tally) do		local teamtext = va.team

if stripwhitespace(args['status_'..va.code] or '') == 'H' then showhost = true teamtext = va.team..' (H)' end row = root:tag('tr') if (prevpts == va.overall) then prevspan = prevspan + 1 prevrankcell :attr('rowspan', prevspan) else prevspan = 1 prevrankcell = row:tag(celltype) :attr('scope', 'row') :css('text-align', 'center') :wikitext(ka) end row:tag('td') :attr('scope', 'row') :css('white-space', 'nowrap') :css('text-align', 'left') :wikitext(teamtext) if not ovptsonly then if not sumsonly then for kr, vr in pairs(va.res) do					row:tag('td') :css('background-color', getbg(vr.rank,vr.raw)) :wikitext(vr.pts ~= 0 and vr.pts or ptsbyrank.NT) end end for kd, vd in pairs(divs) do				if (hidedivs or					(not showwg and vd == divs[2]) or					(not showc and vd == divs[3])) 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 if (prevpts == va.overall) then prevtotalcell :attr('rowspan', prevspan) else prevspan = 1 prevtotalcell = row:tag(celltype) :attr('scope', 'row') :css('font-weight', 'bold') :css('text-align', 'center') :css('border-left-width', hidedivs and '3px' or nil) :wikitext(va.overall) prevpts = va.overall end end local source, legend = args['source'], footer:tag('div'):cssText('font-size: 90%; margin-bottom: 0.5em;') if source then legend:tag(''):wikitext('Source: '.. source ..' ') end if showhost then legend:tag('span') :css('font-weight', 'bold') :wikitext('(H)') :done :wikitext(' Season host') if ovptsonly or sumsonly then legend:wikitext('.') end end if not (ovptsonly or sumsonly) then local firsttag = not showhost for kp, vp in pairs(ptsbyrank) do			if not string.match(kp,p.TIE_REGEX) and (tonumber(kp) or 0) < 4 then 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(kp)) :css('font-size', '95%') :css('border', '1px solid #BBB') :css('line-height', '1.25') :css('text-align', 'center') :wikitext(type(vp) == 'number' and ' ' or vp) :done :wikitext(' = ' .. (colors[kp] or colors.NT)[2]) firsttag = false end end legend:wikitext('.') end legend:wikitext(' Notes: ' .. (isfinal and 'Results are final.' or 'Season in progress. Results are not yet final.'))

return tostring(root)..tostring(footer) end

function p.main(frame) local getArgs = require('Module:Arguments').getArgs local args = getArgs(frame, { parentFirst = true }) local yesno = require('Module:Yesno') local showmedals = yesno(args['show_medals'] or 'n') local ovptsonly = yesno(args['overall_pts_only'] or 'n') local sumsonly = yesno(args['subtotals_only'] or 'n') local isfinal = yesno(args['final'] or 'y') local division = (args['division'] or 'senior'):lower local template = args['team_template'] or 'UAAPteam' local team_list, defaultpts = {}, { 15, 12, 10, 8, 6, 4, 2, 1, NT = '&mdash;' } local ptsbyrank = { NT = defaultpts.NT } 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] if division == 'junior' and isnotempty(args['j_short_' .. t]) then sname = args['j_short_' .. t] end 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 cvd = vd					if (kd == 3) then cvd = division:sub(1,1) end local evt_rank = stripwhitespace(args[cvd:lower..'_'..ve[1]..'_'..t] or '') table.insert(res, { div = vd, evt = ve[1], raw = stripwhitespace(evt_rank), rank = tonumber(evt_rank) or 0 }) end end table.insert(team_list, {rank = i, code = t, name = tname, res = res}) end end if #team_list == 0 then error ('At least one team required') end for r=1,#team_list do		ptsbyrank[r] = tonumber(stripwhitespace(args['pts_'..require('Module:Ordinal')._ordinal(r)] or '')) or defaultpts[r] or 0 end return buildtable(frame,args,team_list,division,ptsbyrank,showmedals,ovptsonly,sumsonly,isfinal) end

return p