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.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a44396e1ec71e88e

Jump to content

Module:Sandbox/Habst

From Wikipedia, the free encyclopedia
-- Generates {{Medal}} template rows for athletes using [[commons:Data:Athletics_medals.tab]]

local p = {}

local DATA_PAGE = 'Athletics medals.tab'

local COMPETITION_ORDER = { 'Q715044', 'Q182653', 'Q725169', 'Q926006' }
local COMPETITIONS = {
	['Q715044'] = '[[Athletics at the Summer Olympics|Olympic Games]]',
	['Q182653'] = '[[World Athletics Championships|World Championships]]',
	['Q725169'] = '[[World Athletics Indoor Championships|World Indoor Championships]]',
	['Q926006'] = '[[World Athletics Relays|World Relays]]',
}

local MEDAL_NAME = { ['1'] = 'Gold', ['2'] = 'Silver', ['3'] = 'Bronze', ['DQ'] = 'DQ' }
local MEDAL_RANK = { Gold = 1, Silver = 2, Bronze = 3, DQ = 4 }

-- Row order within a competition. true: gold → silver → bronze → DQ, each
-- color ordered by edition year (current). false: edition year first, then
-- medal color (the original ordering).
local ORDER_BY_MEDAL = true

local GENDER_WORD = { ['Q6581097'] = "Men's", ['Q6581072'] = "Women's" }

-- ----------------------------------------------------------------------------
-- Data loading
-- ----------------------------------------------------------------------------

-- returns array of { competition, edition, event, medal, country, city, idx } or nil + error message
local function loadRows(athleteQid)
	local dataset = mw.ext.data.get(DATA_PAGE)
	if not dataset or not dataset.data or not dataset.schema then
		return nil, 'could not load [[commons:Data:' .. DATA_PAGE .. ']]'
	end
	local col = {}
	for i, field in ipairs(dataset.schema.fields) do
		col[field.name] = i
	end
	for _, name in ipairs({ 'competition', 'edition', 'event', 'medal', 'athlete', 'country', 'city' }) do
		if not col[name] then
			return nil, 'Data:' .. DATA_PAGE ..
				' is missing the "' .. name .. '" column'
		end
	end
	local rows = {}
	for _, record in ipairs(dataset.data) do
		if record[col.athlete] == athleteQid then
			local medal = MEDAL_NAME[record[col.medal]]
			if medal then
				rows[#rows + 1] = {
					competition = record[col.competition],
					edition = record[col.edition],
					event = record[col.event],
					medal = medal,
					country = record[col.country],
					city = record[col.city],
					idx = #rows + 1,
				}
			end
		end
	end
	return rows
end

-- ----------------------------------------------------------------------------
-- Wikidata helpers
-- ----------------------------------------------------------------------------

local sitelinkCache = {}
local function sitelinkOf(qid)
	if qid == '' then return nil end
	if sitelinkCache[qid] == nil then
		sitelinkCache[qid] = mw.wikibase.getSitelink(qid) or false
	end
	return sitelinkCache[qid] or nil
end

local function titleOf(qid)
	return sitelinkOf(qid) or mw.wikibase.getLabel(qid) or qid
end

local function yearOf(qid)
	return tonumber(titleOf(qid):match('%d%d%d%d')) or 0
end

-- {{Medal|Sport|...}} cell
local function sportCell(qid)
	local link = '[[Sport of athletics|athletics]]'
	local entity = mw.wikibase.getEntity(qid)
	if entity and entity.claims and entity.claims.P21 then
		for _, claim in ipairs(entity.claims.P21) do
			local snak = claim.mainsnak
			local v = snak and snak.datavalue and snak.datavalue.value
			if v and v.id and GENDER_WORD[v.id] then
				return GENDER_WORD[v.id] .. ' ' .. link
			end
		end
	end
	return link
end

-- {{Medal|Country|...}} cell
local function countryCell(code)
	return '{{flagg|cnuie|' .. code .. '}}'
end

-- {{Medal|Competition|...}} cell
local function competitionCell(qid)
	if COMPETITIONS[qid] then return COMPETITIONS[qid] end
	local link = sitelinkOf(qid)
	if link then return '[[' .. link .. ']]' end
	return titleOf(qid)
end

-- display text: metres -> m, steeplechase -> st., kilometres -> km, race walk -> walk
local function eventDisplay(title)
	local frag = title:match('%s–%s(.+)$') or title
	local mixed = false
	if frag:sub(1, 6) == 'Mixed ' then
		mixed = true
		frag = frag:sub(7)
	elseif frag:sub(1, 6) == "Men's " then
		frag = frag:sub(7)
	elseif frag:sub(1, 8) == "Women's " then
		frag = frag:sub(9)
	end
	frag = frag:gsub(' kilometres', ' km'):gsub(' kilometre', ' km')
	frag = frag:gsub(' metres', ' m'):gsub(' metre', ' m')
	frag = frag:gsub('steeplechase', 'st.')
	frag = frag:gsub('race walk', 'walk')
	frag = frag:gsub('^%a', string.upper)
	-- mixed relays: "4 × 400 m relay" -> "4 × 400 m mixed"
	if mixed then
		local n
		frag, n = frag:gsub(' relay$', ' mixed')
		if n == 0 then frag = frag .. ' mixed' end
	end
	return frag
end

-- ----------------------------------------------------------------------------
-- Grouping and rendering
-- ----------------------------------------------------------------------------

local function compOrder(qid)
	for i, cq in ipairs(COMPETITION_ORDER) do
		if cq == qid then return i end
	end
	return 100
end

function p.medals(frame)
	local args = frame.args
	local qid = mw.text.trim(args.qid or '')
	if qid == '' then qid = mw.wikibase.getEntityIdForCurrentPage() or '' end
	if qid == '' then
		return '<span class="error">Athletics medals: no Wikidata item found; pass |qid=</span>'
	end
	qid = qid:upper()

	local rows, err = loadRows(qid)
	if not rows then
		return '<span class="error">Athletics medals: ' .. err .. '</span>'
	end
	if #rows == 0 then
		return '<span class="error">Athletics medals: no rows for ' .. qid .. ' in [[commons:Data:' .. DATA_PAGE .. ']]</span>'
	end

	-- group: country -> competition -> edition -> medal rows
	local countries, countrySeen = {}, {}
	for _, r in ipairs(rows) do
		local cg = countrySeen[r.country]
		if not cg then
			cg = { code = r.country, comps = {}, compSeen = {}, minYear = math.huge }
			countrySeen[r.country] = cg
			countries[#countries + 1] = cg
		end
		local pg = cg.compSeen[r.competition]
		if not pg then
			pg = { qid = r.competition, editions = {}, edSeen = {} }
			cg.compSeen[r.competition] = pg
			cg.comps[#cg.comps + 1] = pg
		end
		local eg = pg.edSeen[r.edition]
		if not eg then
			eg = { qid = r.edition, city = r.city, rows = {} }
			pg.edSeen[r.edition] = eg
			pg.editions[#pg.editions + 1] = eg
		end
		eg.rows[#eg.rows + 1] = r
	end

	-- sort: countries by earliest medal year; within a competition, rows by
	-- medal color then edition year (ORDER_BY_MEDAL), or the reverse
	for _, cg in ipairs(countries) do
		table.sort(cg.comps, function(a, b)
			local oa, ob = compOrder(a.qid), compOrder(b.qid)
			if oa ~= ob then return oa < ob end
			return a.qid < b.qid
		end)
		for _, pg in ipairs(cg.comps) do
			pg.flat = {}
			for _, eg in ipairs(pg.editions) do
				local y = yearOf(eg.qid)
				if y > 0 and y < cg.minYear then cg.minYear = y end
				for _, r in ipairs(eg.rows) do
					r.year = y
					pg.flat[#pg.flat + 1] = r
				end
			end
			table.sort(pg.flat, function(a, b)
				local ra, rb = MEDAL_RANK[a.medal] or 9, MEDAL_RANK[b.medal] or 9
				if ORDER_BY_MEDAL and ra ~= rb then return ra < rb end
				if a.year ~= b.year then return a.year < b.year end
				if a.edition ~= b.edition then return a.edition < b.edition end
				if ra ~= rb then return ra < rb end
				return a.idx < b.idx
			end)
		end
	end
	table.sort(countries, function(a, b)
		if a.minYear ~= b.minYear then return a.minYear < b.minYear end
		return a.code < b.code
	end)

	-- render
	local out = { '{{Medal|Sport|' .. sportCell(qid) .. '}}' }
	local missingEvents, missingSeen = {}, {}
	for _, cg in ipairs(countries) do
		out[#out + 1] = '{{Medal|Country|' .. countryCell(cg.code) .. '}}'
		for _, pg in ipairs(cg.comps) do
			out[#out + 1] = '{{Medal|Competition|' .. competitionCell(pg.qid) .. '}}'
			local linked = {}
			for _, r in ipairs(pg.flat) do
				-- link meet edition on its first occurrence only
				local year = r.year
				local place = (year > 0 and year or '?') .. '&nbsp;' .. r.city
				local edTitle = sitelinkOf(r.edition)
				local edCell
				if edTitle and not linked[r.edition] then
					edCell = '[[' .. edTitle .. '|' .. (year > 0 and year or '?') .. ' ' .. r.city .. ']]'
					linked[r.edition] = true
				else
					edCell = place
				end
				local evTitle = sitelinkOf(r.event)
				if not evTitle then
					if not missingSeen[r.event] then
						missingSeen[r.event] = true
						missingEvents[#missingEvents + 1] =
							r.event ~= '' and ('[[:d:' .. r.event .. ']]') or '(no event QID in the data row)'
					end
				else
					out[#out + 1] = '{{Medal|' .. r.medal .. '|' .. edCell ..
						'|[[' .. evTitle .. '|' .. eventDisplay(evTitle) .. ']]}}'
				end
			end
		end
	end
	if #missingEvents > 0 then
		return '<span class="error">Athletics medals: no English Wikipedia article for event(s) ' ..
			table.concat(missingEvents, ', ') .. '</span>'
	end

	local text = table.concat(out, '\n')
	local source = args.source or args.raw or ''
	if source == 'yes' or source == '1' or source == 'true' then
		return '<pre>' .. text .. '</pre>'
	end
	return frame:preprocess(text)
end

return p