Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Module:Sports stat rank/sandbox

From Wikipedia, the free encyclopedia
-- Module to build tables ranking top player in a particular stat in Sports
-- See documentation for details

require('strict')

local p = {}

-- Main function
function p.main(frame)
    -- Declare locals
    local getArgs = require('Module:Arguments').getArgs
	local Args = getArgs(frame, {parentFirst = true})
	assert(Args['order_cat'], 'Error: Please define order_cat')
	local cat_order = mw.text.split(Args['order_cat'], '%s*[;,]%s*')
	local stat_list = {['Totals'] = {}}
	local t = {}

	local templatestyles = frame:extensionTag{
		name = 'templatestyles', args = { src = 'Module:Sports stat rank/styles.css' }
	}

	-- Load modules
	local yesno = require('Module:Yesno')

	local function defaultTrue(val)
		return (yesno(val) == nil) and true or yesno(val)
	end
	local function defaultFalse(val)
		return yesno(val) or false
	end

	-- Set show flags
	local show_totals = defaultTrue(Args['show_totals'])
	local show_num = defaultTrue(Args['show_num'])
	local show_pos = defaultFalse(Args['show_pos'])
	local show_nat = defaultFalse(Args['show_nat'])
	local show_team = defaultFalse(Args['show_team'])
	local show_cat = defaultTrue(Args['show_cat'])

	-- Set threshold checks
	local function checkThresholdRank(next_rank)
		local threshold_rank = tonumber(Args['threshold_rank'])
		if threshold_rank then
			return next_rank <= threshold_rank
		else
			return true	-- Check passes if no threshold rank is specified
		end
	end
	local function checkThresholdStat(next_stat)
		local threshold_stat = tonumber(Args['threshold_stat'])
		if threshold_stat then
			return next_stat >= threshold_stat
		else
			return true	-- Check passes if no threshold rank is specified
		end
	end

    -- Form list of players, stored under stat_list[playertotal][pname]
    for arg, value in pairs(Args) do		
        -- Get player abbreviation
        local pname = string.match(arg, '^name_(.+)')
		if yesno(pname, true) then
	        local pstat = {total = 0}
	        
	        -- Get player stats by category
	        for _, cname in pairs(cat_order) do
	            local stat = tonumber(Args[cname..'_'..pname]) or 0	-- Stat might not be set
	            pstat[cname] = stat
	            pstat.total = pstat.total + stat
	            
	            -- Add stat to appropriate totals category
	            stat_list['Totals'][cname] = (stat_list['Totals'][cname] or 0) + stat
	            stat_list['Totals'].total = (stat_list['Totals'].total or 0) + stat
	        end

			-- Apply stat adjustment to account for matches that count towards multiple competitions
			local adj = tonumber(Args['adj_'..pname]) or 0
			pstat.total = pstat.total - adj
			stat_list['Totals'].total = stat_list['Totals'].total - adj
			
	        -- Add player to stats list
			if not stat_list[pstat.total] then stat_list[pstat.total] = {} end -- Initialise dict for particular stat total
	        stat_list[pstat.total][pname] = pstat
			-- Add player number sort value
			stat_list[pstat.total][pname].num = tonumber(Args['num_'..pname])
			-- Add player name sort value
			local name_stripped_brackets = mw.ustring.gsub(Args['name_'..pname], '[%[%]]', '')
        	local name_clean = mw.ustring.gsub(name_stripped_brackets, '.-|', '') -- strip original page name
			stat_list[pstat.total][pname].sortname = Args['data_sort_value_name_'..pname] or name_clean
		end
    end
    
    -- Sort stat_list, by descending total goals
    local total_array = {}
    for total in pairs(stat_list) do
        table.insert(total_array, total)
    end
    table.sort(total_array, function(a, b)
        -- 'Totals' is placed at the bottom
        if a == 'Totals' then return false end
        if b == 'Totals' then return true end
        
        -- Sort by stat, descending
        return tonumber(a) > tonumber(b)
    end)

	-- Sort sub stat_list for each goals total, by player number or name
    for k, v in pairs(stat_list) do
        if k ~= 'Totals' then -- Skip 'Total' entry
			local player_array = {}
			for pname in pairs(v) do
	        	table.insert(player_array, pname)
	    	end
            table.sort(player_array, function(pnamea, pnameb)
				if Args['sort_by'] == 'NUM' then
					-- Sort by player number, ascending, with no assigned player number placed lower
					if v[pnamea].num and v[pnameb].num then return v[pnamea].num < v[pnameb].num end
					if not v[pnamea].num then return false end
					if not v[pnameb].num then return true end
				end
				-- Sort by player name otherwise, ascending
	            return v[pnamea].sortname < v[pnameb].sortname
            end)
			v.player_array = player_array
        end
    end
    
    -- Create table header
	local style = 'style='..(Args['header_style'] or '')
    table.insert(t, '{| class="wikitable sortable" style="text-align:center"\n')
    table.insert(t, '!width=15 '..style..'|Rank\n')
	if show_num then table.insert(t, '!width=15 '..style..'|<abbr title="Squad number">No.</abbr>\n') end
    if show_pos then table.insert(t, '!width=15 '..style..'|<abbr title="Player position">Pos</abbr>\n') end
    if show_nat then table.insert(t, '!width=15 '..style..'|<abbr title="Nationality">Nat</abbr>\n') end
    table.insert(t, '! '..style..'"|Player\n')
    if show_team then table.insert(t, '! '..style..'|Team\n') end
    if show_cat then
        -- Add category columns
        for _, cname in pairs(cat_order) do
            table.insert(t, '!width=25 data-sort-order="desc" '..style..'|'..(Args['cat_'..cname] or '')..'\n')
        end
    end
    -- Add totals column, named as defined with stat_name, or 'Total' by default
    table.insert(t, '!data-sort-order="desc" '..style..'|'..(Args['stat_name'] or 'Total')..'\n')
    
    -- Create data rows
    local rank = 1 -- Track current ranking
    for _, total in pairs(total_array) do
        local is_totals = total == 'Totals'
        
        if not is_totals then
            -- Player row formatting
			if not (checkThresholdRank(rank) and checkThresholdStat(total)) then
				break	-- Stop creating table rows if threshold checks fail.
			end
			
			local num_players = #stat_list[total].player_array
			local first_player = true -- Track if it is the first player with that stat total, to manage new rows and total cell placement
            
            table.insert(t, '|-\n')
            table.insert(t, '! rowspan="'..num_players..'"|'..rank..'\n')
            for _, pname in pairs(stat_list[total].player_array) do
				local pstat = stat_list[total][pname]
				if not first_player then table.insert(t, '|-\n') end

				if show_num then table.insert(t, '|'..(Args['num_'..pname] or '')..'\n') end
                if show_pos then table.insert(t, '|'..(Args['pos_'..pname] or '')..'\n') end
                if show_nat then table.insert(t, '|'..(Args['nat_'..pname] or '')..'\n') end
				table.insert(t, '| style="text-align:left;" data-sort-value="'..(pstat.sortname or '')..'" |'..(Args['name_'..pname] or '')..'\n')
                if show_team then table.insert(t, '| style="text-align:left;" |'..(Args['team_'..pname] or '')..'\n') end
                if show_cat then
                    -- Add category columns
                    for _, cname in ipairs(cat_order) do
                        table.insert(t, '|'..((pstat[cname] ~= 0) and pstat[cname] or ''))
						if yesno(Args['note_'..cname..'_'..pname], true) then
            				table.insert(t, frame:expandTemplate{title='efn', args={group='lower-alpha', Args['note_'..cname..'_'..pname]}})
						end
						table.insert(t, '\n')
                    end
                end
				if first_player then table.insert(t, '| rowspan="'..num_players..'"|<b>'..total..'</b>\n') end
				first_player = false
            end

			rank = rank + num_players
        elseif show_totals then
            -- Totals row formatting
            local totals_name_colspan = 2 + (show_num and 1 or 0) + (show_pos and 1 or 0) + (show_nat and 1 or 0) + (show_team and 1 or 0) -- Rank and player columns are mandatory
            
            table.insert(t, '|-style="background-color:#d8edf3; font-weight:bold;"\n')
            table.insert(t, '| colspan="'..totals_name_colspan..'" | Totals\n')
            if show_cat then
                -- Add category columns
                for _, cname in ipairs(cat_order) do
                    table.insert(t, '| '..stat_list['Totals'][cname]..'\n')
                end
            end
            table.insert(t, '|'..stat_list['Totals'].total..'\n')
        end
    end
	
	-- Close table
	table.insert(t, '|}')

	-- Create footer
	local note_list = {}
    if yesno(Args['updated'], true) then
        table.insert(note_list, 'Updated to match(es) played on '..Args['updated']..'.')
    end
	
    if yesno(Args['source'], true) then
        table.insert(note_list, 'Source: '..Args['source'])
	end

	local t_footer = '<div class="sports-stat-rank-notes">'..table.concat(note_list, ' ')..'</div>'..frame:expandTemplate{title='notelist', args={group='lower-alpha'}}

	return templatestyles..'\n'..table.concat(t)..t_footer
end

return p