Module:Team appearances list

This is an old revision of this page, as edited by Johnuniq (talk | contribs) at 08:32, 20 November 2016 (major refactor to handle ranges of absences and automatically merge ranges of absences in the output; use four-digit years in ranges per MOS:DATERANGE). 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]].
-- [SublimeLinter luacheck-globals:mw]

local p = {}

local all_competitions = {
	["All-Africa Games"] = {
		1965, 1973, 1978, 1987, 1991, 1995, 1999, 2003, 2007, 2011, 2015},

	-----Asian Games----
	["Asian Games"] = {
		1951, 1954, 1958, 1962, 1966, 1970, 1974, 1978, 1985, 1986, 1990, 1994,
		1998, 2002, 2006, 2010, 2014},
	["Asian Beach Games"] = {
		2008, 2010, 2012, 2014, 2016},
	["Asian Para Games"] = {
		2010, 2014, 2018},
	["Asian Youth Games"] = {
		2009, 2013, 2021},
	["Asian Winter Games"] = {
		1986, 1990, 1996, 1999, 2003, 2007, 2011, 2017},
	["East Asian Games"] = {
		1993, 1997, 2001, 2005, 2009, 2013},
	["South Asian Games"] = {
		1984, 1985, 1987, 1989, 1991, 1993, 1995, 1999, 2004, 2006, 2010, 2016},
	["Southeast Asian Games"] = {
		1977, 1979, 1981, 1983, 1985, 1987, 1989, 1991, 1993, 1995, 1997, 1999,
		2001, 2003, 2005, 2007, 2009, 2011, 2013, 2015, 2017},
	["Southeast Asian Peninsular Games"] = {
		1959, 1961, 1963, 1965, 1967, 1969, 1971, 1973, 1975},
	["West Asian Games"] = {
		1997, 2002, 2005, 2016},

	-----Commonwealth Games-----
	["Commonwealth Games"] = {
		1930, 1934, 1938, 1950, 1954, 1958, 1962, 1966, 1970, 1974, 1978, 1982,
		1986, 1990, 1994, 1998, 2002, 2006, 2010, 2014},
	["Commonwealth Youth Games"] = {
		2000, 2004, 2008, 2011, 2015, 2017},

	-----European Games-----
	["European Athletics Championships"] = {
		1934, 1938, 1946, 1950, 1954, 1958, 1962, 1966, 1969, 1971, 1974, 1978,
		1982, 1986, 1990, 1994, 1998, 2002, 2006, 2010, 2012, 2014, 2016},
	["European Games"] = {
		2015},
	["Mediterranean Games"] = {
		1951, 1955, 1959, 1963, 1967, 1971, 1975, 1979, 1983, 1987, 1991, 1993,
		1997, 2001, 2005, 2009, 2013, 2017},

	----- Olympics -----
	["Summer Olympics"] = {
		1896, 1900, 1904, 1908, 1912, 1920, 1924, 1928, 1932, 1936, 1948, 1952,
		1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000,
		2004, 2008, 2012, 2016},
	["Summer Paralympics"] = {
		1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000, 2004,
		2008, 2012, 2016},
	["Summer Youth Olympics"] = {
		2010, 2014, 2018},
	["Winter Olympics"] = {
		1924, 1928, 1932, 1936, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976,
		1980, 1984, 1988, 1992, 1994, 1998, 2002, 2006, 2010, 2014},
	["Winter Paralympics"] = {
		1976, 1980, 1984, 1988, 1992, 1994, 1998, 2002, 2006, 2010, 2014},
	["Winter Youth Olympics"] = {
		2012, 2016},

	----- Misc / World -----
	["FEI World Equestrian Games"] = {
		1990, 1994, 1998, 2002, 2006, 2010, 2014},
	["Pan American Games"] = {
		1951, 1955, 1959, 1963, 1967, 1971, 1975, 1979, 1983, 1987, 1991, 1995,
		1999, 2003, 2007, 2011, 2015},
	["Summer Universiade"] = {
		1959, 1961, 1963, 1965, 1967, 1970, 1973, 1975, 1977, 1979, 1981, 1983,
		1985, 1987, 1989, 1991, 1993, 1995, 1997, 1999, 2001, 2003, 2005, 2007,
		2009, 2011, 2013, 2015, 2017},
	["Winter Universiade"] = {
		1960, 1962, 1964, 1966, 1968, 1972, 1978, 1981, 1983, 1985, 1987, 1989,
		1991, 1993, 1995, 1997, 1999, 2001, 2003, 2005, 2007, 2009, 2011, 2013,
		2015, 2017},
	["World Aquatics Championships"] = {
		1973, 1975, 1978, 1982, 1986, 1991, 1994, 1998, 2001, 2003, 2005, 2007,
		2009, 2011, 2013, 2015},
	["World Championships in Athletics"] = {
		1983, 1987, 1991, 1993, 1995, 1997, 1999, 2001, 2003, 2005, 2007, 2009,
		2011, 2013, 2015, 2017},}

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 competition_years(args)
	-- Return list of competition years specified in args.
	local result = {}
	local begin_year = tonumber(args.begin_year)
	local end_year = tonumber(args.end_year)
	local competitions = all_competitions[args.competition]
	if competitions then
		begin_year = 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(result, 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(result, y)
		end
	end
	return result
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

-- 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)
	local hlist = require('Module:List').horizontal
	local absent_years, absent_ranges = {}, {}
	for _, v in ipairs(args) do
		-- Extract absent years from numeric parameters.
		-- Skip blank parameters (and non-strings that may be passed by a module).
		v = strip_to_nil(v)
		if type(v) == 'string' then
			local first, last = extract_range(v)
			if not first then
				error('Invalid year: "' .. v .. '"')
			end
			if last then
				table.insert(absent_ranges, {first, last})
			else
				absent_years[first] = true
			end
		end
	end
	local function is_absent(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
	local appearances = {}
	local absent_first, absent_last
	local competitions = competition_years(args)
	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)
	return p._main(frame:getParent().args)
end

return p