Jump to content

Module:Team appearances list/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Johnuniq (talk | contribs) at 00:58, 22 November 2016 (code to use a default begin_year if defined for a team at a competion). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
-- This module implements [[Template:Team appearances list]].

local p = {}

local data_competitions, data_absences
local function load_data(frame)
	-- Load data module (or its sandbox) and set variables from its exported data.
	if not data_competitions then
		frame = frame or mw.getCurrentFrame()
		local sandbox = frame:getTitle():find('sandbox', 1, true) and '/sandbox' or ''
		local datamod = mw.loadData('Module:Team appearances list/data' .. sandbox)
		data_competitions = datamod.competitions
		data_absences = datamod.absences
	end
end

local function strip_to_nil(text)
	-- If text is a string, return its trimmed content, or nil if empty.
	-- Otherwise return text (which may, for example, be nil).
	if type(text) == 'string' then
		text = text:match('(%S.-)%s*$')
	end
	return text
end

local function extract_range(text)
	-- Return first (if text is a single year), or first, last if a range.
	-- The returned values are numbers.
	-- Return nothing if text is invalid.
	local year = text:match('^(%d+)$')
	if year then
		if #year == 4 then
			return tonumber(year)
		end
		return
	end
	local first, dash, last = text:match('^(%d+)(%D+)(%d+)$')
	if not (first and #first == 4) then
		return
	end
	dash = strip_to_nil(dash)
	if dash == '-' or dash == '–' then
		if #last ~= 4 then
			if #last == 2 then
				last = first:sub(1, 2) .. last
			else
				return
			end
		end
	end
	first = tonumber(first)
	last = tonumber(last)
	if first < last then
		return first, last
	elseif first == last then
		return first
	end
end

local function competition_absences(data)
	-- Return two tables with absent years and absent year ranges.
	-- Parameter data is an array of strings from template parameters, or
	-- numbers or strings from built-in data.
	-- Parameters that are blank or not numbers or strings are ignored.
	local absent_years, absent_ranges = {}, {}
	for _, item in ipairs(data) do
		if type(item) == 'number' then
			absent_years[item] = true
		else
			item = strip_to_nil(item)
			if type(item) == 'string' then
				local first, last = extract_range(item)
				if not first then
					error('Invalid year: "' .. item .. '"')
				end
				if last then
					table.insert(absent_ranges, {first, last})
				else
					absent_years[first] = true
				end
			end
		end
	end
	return absent_years, absent_ranges
end

local function competition_information(args)
	-- Return three tables with information for the specified competition:
	-- * List of competition years.
	-- * Table of absent years.
	-- * List of pairs of years (absent for each year in range, inclusive).
	local absences
	local comp_years = {}
	local begin_year = tonumber(args.begin_year)
	local end_year = tonumber(args.end_year)
	local competitions = data_competitions[args.competition]
	if competitions then
		absences = competitions[args.team] or
			data_absences[args.team .. ' ' .. args.competition]
		begin_year = begin_year or (absences and absences.begin_year) or 0
		end_year = end_year or 9999
		for _, y in ipairs(competitions) do
			if y > end_year then
				break
			elseif y >= begin_year then
				table.insert(comp_years, y)
			end
		end
	else
		if not (begin_year and 1800 <= begin_year and begin_year <= 2100) then
			error('Parameter begin_year is not a valid year: ' .. tostring(args.begin_year))
		end
		local interval = tonumber(args.interval)
		if not interval then
			if not args.interval then
				error('Parameter interval is missing')
			end
			error('Parameter interval is not a number: ' .. tostring(args.interval))
		end
		if not (1 <= interval and interval <= 30) then
			error('Parameter interval is not valid: ' .. interval)
		end
		end_year = end_year or (os.date('!*t').year + interval)
		for y = begin_year, end_year, interval do
			table.insert(comp_years, y)
		end
	end
	return comp_years, competition_absences(absences or args)
end

-- TODO Missing parameters generate an ugly error (say if args.team is nil).
--      Should show error if begin_year > end_year.
function p._main(args)
	load_data()  -- in case this function is called by another module
	local hlist = require('Module:List').horizontal
	local competitions, absent_years, absent_ranges = competition_information(args)
	local is_absent
	if absent_years and absent_ranges then
		is_absent = function (y)
			if absent_years[y] then
				return true
			end
			for _, range in ipairs(absent_ranges) do
				if range[1] <= y and y <= range[2] then
					return true
				end
			end
			return false
		end
	else
		is_absent = function (y)
			return false
		end
	end
	local appearances = {}
	local absent_first, absent_last
	for i = 1, #competitions + 1 do  -- +1 to handle any trailing absences
		local y = competitions[i]
		if y and is_absent(y) then
			if absent_first then
				absent_last = y
			else
				absent_first = y
			end
		else
			if absent_first then
				table.insert(appearances,
					'<span style="color:gray">' ..
					(absent_last and (absent_first .. '–' .. absent_last) or absent_first) ..
					'</span>')
				absent_first, absent_last = nil, nil
			end
			if y then
				table.insert(appearances, string.format(
					'[[%s at the %d %s|%d]]',
					args.team, y, args.competition, y
				))
			end
		end
	end
	return hlist(appearances)
end

function p.main(frame)
	load_data(frame)
	return p._main(frame:getParent().args)
end

return p