Module:Sandbox/TheThomanski/Esc

From English Wikipedia @ Freddythechick
----- Cell colors -----
local COL_FST = 'gold'			-- 1st place
local COL_SND = 'silver'		-- 2nd place
local COL_TRD = '#C96'			-- 3rd place
local COL_LST = '#FE8080'		-- Last place
local COL_SFQ = 'navajowhite'	-- Semi-Final qualifier
local COL_BJQ = 'paleturquoise'	-- Backup jury qualifier
local COL_DSQ = '#A4EAA9'		-- Disqualifed / withdrawn entry

----- Text -----
local AQ         = 'Automatic qualifier'
local NQ         = 'Failed to qualify'
local NOSEMI     = 'No semi-finals'
local NA         = 'N/A'
local MILLSTREET = 'Kvalifikacija za Millstreet'
local ESC1996Q   = 'Eurovision Song Contest 1996#Qualifying round'

----------------------------------------------------------------------------------------------------

local getArgs = require('Module:Arguments').getArgs
local p = {}

local amountOfEntries
local usedLanguages = {}

function p.entry(f)		return main(f, 'entry')		end
function p.year(f)		return main(f, 'year')		end
function p.country(f)	return main(f, 'country')	end
function p.list(f)		return main(f, 'list')		end

function main(f, reqOutput)
	local args = getArgs(f)
	local html = ''
	
	local contest = getContest(args['cont'])
	local id      = args['id']
	local country = args['cnty']
	local year    = args['y']
	local show    = args['s']
	local entryNo = args['no']
	local sortMethod = ''
	
	if not show    then show = 'gf' end
	if not entryNo then entryNo = 1 end
	if show == 'gf' then sortMethod = 'draw' end
	
	local entries
	if reqOutput == 'year' or reqOutput == 'list' then	-- Get all entries from country
		entries = getAllEntriesFrom{contest=contest.id, from='year', year=year, show=show, sortMethod=sortMethod}
	elseif reqOutput == 'country' then					-- Get all entries from year
		entries = getAllEntriesFrom{contest=contest.id, from='country', country=country}
	else end
	amountOfEntries = tableLen(entries)
	
	local function getEntryNo(entry)
		local entryNo = 1
		if entry.cntye then
			entryNo = entry.cntye
		end
		return entryNo
	end
	
	if reqOutput == 'entry' then
		if country and year then
			html = table.concat(createEntryRow{reqOutput=reqOutput, contest=contest, entryNo=entryNo, country=country, year=year})
		elseif id then
			entryNo = getEntryNo(getEntry{contest=contest, id=id})
			html = table.concat(createEntryRow{reqOutput=reqOutput, contest=contest, entryNo=entryNo, id=id})
		end
	else
		for _, entry in ipairs(entries) do
			entryNo = getEntryNo(entry)
			html = html .. table.concat(createEntryRow{reqOutput=reqOutput, contest=contest, show=show, entryNo=entryNo, country=entry.cnty, year=entry.y})
		end
	end
	return html
end

--------------------------------------------------

local row = 0
local spannedRows = {}
local spannedCols = {}
function createEntryRow(args)
	local html = {}
	local f    = mw.getCurrentFrame()
	
	local contest   = args.contest
	local year      = args.year
	local country   = args.country
	if country and country['name'] then
		for i, name in ipairs(country['name']) do
			if name[2] then
				if contest.dates[tostring(year)] < name[2] then
					country = name[1]
					break
				end
			end
			country = name[1]
		end
	end
	local id        = args.id
	local entryNo   = args.entryNo
	local reqOutput = args.reqOutput
	local show      = args.show
	
	local entry
	if country and year then
		entry = getEntry{contest=contest, country=country, year=year, entryNo=entryNo}
	elseif id then
		entry = getEntry{contest=contest, id=id}
	end
	if entry then
		row = row + 1
		spannedCols = {}
		
		local id = entry.id
		local year = f:expandTemplate{title = 'Escyr', args = {entry.y, entry.c.id}}
		local draw
		local sf_draw = insertZeroBeforeSingleDigit(entry.sf_d)
		local gf_draw = insertZeroBeforeSingleDigit(entry.gf_d)
		if not sf_draw then sf_draw = entry.sf_h end
		if not gf_draw then gf_draw = entry.gf_h end
		local pl, pt
		local sf_pl = entry.sf_pl
		local gf_pl = entry.gf_pl
		local sf_pt = entry.sf_pt
		local gf_pt = entry.gf_pt
		if show then
			if show == 'gf' then
				draw = gf_draw
				pl   = gf_pl
				pt   = gf_pt
			else
				draw = sf_draw
				pl   = sf_pl
				pt   = sf_pt
			end
		else
			if sf_draw then
				if gf_draw then
					-- Took part in both semi-final and grand-final
					pl = gf_pl
					pt = gf_pt
					draw = gf_draw
				else
					-- Only took part in semi-final
					pl = sf_pl
					pt = sf_pt
					draw = sf_draw
				end
			elseif gf_draw then
				-- Only took part in grand final
				pl = gf_pl
				pt = gf_pt
				draw = gf_draw
			end
		end
		local flag
		if entry.cnty['flag'] then
			for _, version in ipairs(entry.cnty['flag']) do
				if contest.dates[tostring(entry.y)] < version[2] then
					flag = version[1]
					break
				end
			end
		end
		country = f:expandTemplate{title = 'Esc', args = {country, flag, y = entry.y}}
		local country_entry = getEntryNoFromCountry(contest, id)
		local artist = entry.art
		local song   = entry.song
		
		local lang = {}
		local function createLangLink(songLang)
			if songLang[2] then
				songLang = '[[' .. songLang[1] .. '|' .. songLang[2] .. ']]'
			else
				songLang = '[[' .. songLang[1] .. ']]'
			end
			return songLang
		end
		for _, songLang in ipairs(entry.lang) do
			if usedLanguages[1] then
				local found = false
				for _, usedLang in ipairs(usedLanguages) do
					if songLang[1] == usedLang then
						if songLang[2] then
							table.insert(lang, songLang[2])
						else
							table.insert(lang, songLang[1])
						end
						found = true
					end
				end
				if not found then
					table.insert(lang, createLangLink(songLang))
					table.insert(usedLanguages, songLang[1])
				end
			else
				table.insert(lang, createLangLink(songLang))
				table.insert(usedLanguages, songLang[1])
			end
		end
		
		if reqOutput == 'entry' then
			entryData	  = {artist, song}
			entryDataText = {'artist', 'song'}
			replaceNilValues(entryData, entryData)
		elseif reqOutput == 'year' then
			entryData	  = {draw, country, artist, song, lang, pl, pt}
			entryDataText = {'draw', 'country', 'artist', 'song', 'lang', 'pl', 'pt'}
			replaceNilValues(entryData, entryData)
		elseif reqOutput == 'country' then
			entryData	  = {year, artist, song, lang, gf_pl, gf_pt, sf_pl, sf_pt}
			entryDataText = {'year', 'artist', 'song', 'lang', 'gf_pl', 'gf_pt', 'sf_pl', 'sf_pt'}
			replaceNilValues(entryData, entryDataText)
		elseif reqOutput == 'list' then
			entryData	  = {id, draw, country, country_entry, artist, song, lang, pl}
			entryDataText = {'id', 'draw', 'country', 'country_entry', 'artist', 'song', 'lang', 'pl'}
			replaceNilValues(entryData, entryData)
		end
		
		local function isNil(row)
			if entryData[row] == 'nil_' .. row then
				return true
			else
				return false
			end
		end
		
		local tr = '<tr>\n' -- Default no row styling
		
		local rowStyling = ''
		if reqOutput == 'year' then
			if (show == 'gf') and gf_pl == 1 then
				rowStyling = 'font-weight:bold; background:' .. COL_FST .. ';'
			elseif (show == 'sf' or show == 'sf1' or show == 'sf2') and (gf_draw or entry.q) then
				if entry.jury then
					rowStyling = 'font-weight:bold; background:' .. COL_BJQ .. ';'
				else
					rowStyling = 'font-weight:bold; background:' .. COL_SFQ .. ';'
				end
			end
		end
		tr = '<tr style="' .. rowStyling .. '">\n' 
		
		table.insert(html, tr)
		
		if entryData then
			for i, data in ipairs(entryData) do
				if not getIndex(spannedRows, row) and not getIndex(spannedCols, entryDataText[i]) then
					local cellStyling = ''
					if type(data) == 'string' and string.sub(data, 1, 4) == 'nil_' then -- If the value is nil
						data = nil
						if reqOutput == 'country' then
							local gf_pl = getIndex(entryDataText, 'gf_pl')
							local gf_pt = getIndex(entryDataText, 'gf_pt')
							local sf_pl = getIndex(entryDataText, 'sf_pl')
							local sf_pt = getIndex(entryDataText, 'sf_pt')
							local colspan = 1
							if not entry.id and entry.dq then
								if entryDataText[i] == 'gf_pl' then
									if entry.y == 2020 then
										data = 'Contest cancelled'
									else
										data = entry.dq
									end
									data = data .. ' <b>X</b>'
									colspan = 4
								else break end
							else
								if gf_pt and isNil(gf_pt) then -- If no gf points
									if not isNil(sf_pl) or not isNil(sf_pt) then
										if entryDataText[i] == 'gf_pl' and gf_pl and isNil(gf_pl) then -- If no gf points and no gf placement
											data = NQ
											if contest.id == 'esc' then
												if entry.y == 1993 then data = '[[' .. MILLSTREET .. '|' .. NQ .. ']] <b>X</b>' end
												if entry.y == 1996 then data = '[[' .. ESC1996Q   .. '|' .. NQ .. ']] <b>X</b>' end
											end
											colspan = 2
										end
									else
										if entryDataText[i] == 'gf_pt' and not isNil(gf_pl) then  -- If no gf points but there is placement (e.g. ESC 1956)
											data = NA
										end
									end
								elseif not gf_pt then
									if entryDataText[i] == 'gf_pl' and gf_pl and isNil(gf_pl) then -- If no gf points and no gf placement
										data = NQ
									end
								end
								if entryDataText[i] == 'sf_pl' and sf_pl then
									if isNil(sf_pl) then -- if no semi results
										if contest.id == 'esc' and entry.y == 1993 then
											data = f:expandTemplate{title = 'Nowrap', args = {'[[' .. MILLSTREET .. ']]'}}
											colspan = 2
										elseif contest.id == 'esc' and entry.y >= 2004 then
											if (not isNil(gf_pl) and not isNil(gf_pt)) or entry.q then
												data = AQ
												colspan = 2
											end
										else
											data = NOSEMI
											colspan = 2
										end
									end
								end
							end
							if data then
								cellStyling = f:expandTemplate{title = 'N/a'}
							else
								data = ''
							end
							if colspan > 1 then
								cellStyling = cellStyling .. '; colspan="' .. colspan .. '"'
								if data == rowspanData then
									cellStyling = cellStyling .. '; rowspan="' .. spannedRows .. '"'
									--cellStyling = cellStyling .. '; colspan="2"'
									table.insert(spannedRows, row + 1)
								else
									spannedRows = 1
								end
								table.insert(spannedCols, entryDataText[i + 1])
							end
						end
						if not data then data = '' end
					elseif entryDataText[i] == 'id' or entryDataText[i] == 'country_entry' then
						cellStyling = 'text-align:right'
						if show == 'gf' and sf_draw then
							data = '<small><i>' .. data .. '</i></small>'
						end
					elseif entryDataText[i] == 'draw' then
						if (show == 'gf' and not entry.gf_d and entry.gf_h) or (show == 'sf1' or show == 'sf2' or show == 'sf' and not entry.gf_d and entry.gf_h) then
							if data == 1 then data = '<b>1st half</b>' end
							if data == 2 then data = '<b>2nd half</b>' end
						end
						cellStyling = 'text-align:center'
					elseif entryDataText[i] == 'year' then -- Year column
						cellStyling = 'text-align:center'
					elseif entryDataText[i] == 'song' then
						local title = song
						local og_title = ''
						if entry.tlang then -- If it's a non-English song title
							if entry.og then -- If it's a transliterated title
								title = f:expandTemplate{title = 'Transl', args = {entry.tlang, title, italics = 'no'}}
								og_title = '<small>(' .. f:expandTemplate{title = 'Lang', args = {entry.tlang, entry.og, italics = 'no'}} .. ')</small>'
							else
								title = f:expandTemplate{title = 'Lang', args = {entry.tlang, title, italics = 'no'}}
							end
						end
						data = '"' .. title .. '" ' .. og_title
					elseif entryDataText[i] == 'lang' then
						if type(data) == 'table' then
							data = processTable(data)
						end
						if entry.langm then -- If there are 'minor' languages
							local function createNote(lang)
								lang = createLangLink(lang)
								data = data .. f:expandTemplate{title = 'Efn', args = {'Contains words or phrases in ' .. lang, name=lang}}
							end
							if type(entry.langm) == 'table' then
								for _, v in ipairs(entry.langm) do
									createNote(v)
								end
							else
								createNote(langm)
							end
						end
					elseif entryDataText[i] == 'pl'	or entryDataText[i] == 'pt'	or entryDataText[i] == 'gf_pl' or entryDataText[i] == 'gf_pt' or entryDataText[i] == 'sf_pl' or entryDataText[i] == 'sf_pt'	then
						cellStyling = 'text-align:right;'
						if reqOutput == 'list' or reqOutput == 'country' then
							if reqOutput == 'country' then
								if entryDataText[i] == 'gf_pl' or entryDataText[i] == 'gf_pt' then
									show = 'gf'
								elseif entryDataText[i] == 'sf_pl' or entryDataText[i] == 'sf_pt' then
									semi = getSemi(contest, entry.id)
									if semi then
										show = semi
									end
								end
							end
							if show then
								calcAmountOfEntries(contest.id, entry.y, show)
							end
							if ((entryDataText[i] == 'sf_pl' or entryDataText[i] == 'sf_pt') and sf_pl == amountOfEntries) or
							   ((entryDataText[i] == 'gf_pl' or entryDataText[i] == 'gf_pt') and gf_pl == amountOfEntries) or
							   ((entryDataText[i] == 'pl' or entryDataText[i] == 'pt') and pl == amountOfEntries) then
								cellStyling = cellStyling .. ' background:' .. COL_LST .. '"'
								--data = tostring(data) .. ' ◁'
							else
								if show == 'gf' or reqOutput == 'country' then
									local pl = gf_pl
									if entryDataText[i] == 'sf_pl' or entryDataText[i] == 'sf_pt' then pl = sf_pl end
									if pl == 1 then cellStyling = cellStyling .. ' background:' .. COL_FST .. '"' end
									if pl == 2 then cellStyling = cellStyling .. ' background:' .. COL_SND .. '"' end
									if pl == 3 then cellStyling = cellStyling .. ' background:' .. COL_TRD .. '"' end
								elseif (show == 'sf' or show == 'sf1' or show == 'sf2') and (gf_pl or gf_pt) then
									if entry.jury then
										cellStyling = cellStyling .. ' background:' .. COL_BJQ ..  '"'
										--data = tostring(data) .. ' ‡'
									else
										cellStyling = cellStyling .. ' background:' .. COL_SFQ ..  '"'
										--data = tostring(data) .. ' †'
									end
								end
							end
						end
						if data == 0 then data = '<b>' .. data .. '</b>' end -- Make digit bold if nul points
					end
					if reqOutput == 'country' and entryDataText[i] ~= 'year' and entryDataText[i] ~= 'sf_pl' and entryDataText[i] ~= 'sf_pt' then
						calcAmountOfEntries(contest.id, entry.y, 'gf')
						local x = amountOfEntries
						if gf_pl == 1 then cellStyling = cellStyling .. ' background:' .. COL_FST end
						if gf_pl == 2 then cellStyling = cellStyling .. ' background:' .. COL_SND end
						if gf_pl == 3 then cellStyling = cellStyling .. ' background:' .. COL_TRD end
						if gf_pl == x then cellStyling = cellStyling .. ' background:' .. COL_LST end
						if entry.dq or entry.nq then cellStyling = cellStyling .. ' background:' .. COL_DSQ end
					end
					if i == 1 then
						cell      = '<th style="' .. rowStyling .. cellStyling .. '" scope="row">\n'
						cellClose = '</th>\n'
					else
						cell      = '<td style="' .. rowStyling .. cellStyling .. '">\n'
						cellClose = '</td>\n'
					end
					table.insert(html, cell .. data .. '</td>\n')
				end
			end
			table.insert(html, '</tr>\n')
		end
	end
	return html
end

function getContest(x)
	local contests = mw.loadData('Module:Sandbox/TheThomanski/Esc/data').contests
	
	local reqContest = 'esc' -- Default ESC
	if x then
		reqContest = string.lower(x)
	end
	
	local result
	for _, contest in ipairs(contests) do -- For every contest do
		if contest.id == reqContest then
			result = contest
		end
	end
	return result
end

function getEntry(args)
	local result
	local contest = args.contest
	local country = args.country -- Table, macedonia
	local year    = args.year
	local id      = args.id
	local entryNo = args.entryNo -- For multiple entries per country in a year (e.g. ESC 1956)
	if type(contest) == 'string' then
		contest = getContest(contest)
	end
	for _, entry in ipairs(contest.values) do -- For every entry in the contest do
		result = nil
		if country and year then
			if (hasValue(entry.cnty, country)) then -- If the entry is from the requested country
				if (entry.y == year) then -- If the entry is from the requested year
					result = entry
				end
			end
		elseif id then
			if (entry.id == tonumber(id)) then -- If the entry is the requested entry then
				result = entry
			end
		end
		
		if result then
			if entryNo and entryNo > 1 then
				entryNo = entryNo - 1
			else
				return result
			end
		end
	end
	return result
end

function getAllEntriesFrom(args)
	local contest = getContest(args.contest)
	local entries = {}
	
	local draw = 1
	local q_d = 0
	local firstEntry = nil
	for i, entry in ipairs(contest.values) do -- For every entry in the contest do
		if args.from == 'year' and entry.y == tonumber(args.year) then -- Get all entries from year
			if args.show then
				if	(args.show == 'sf' and entry.sf_d and not entry.sf) or -- Get all entries from Semi-Final
					(args.show == 'sf1' and entry.sf == 1) or -- Get all entries from Semi-Final 1
					(args.show == 'sf2' and entry.sf == 2) or -- Get all entries from Semi-Final 2
					(args.show == 'gf' and (entry.gf_d or entry.q)) -- Get all entries from Grand Final
				then
					if args.show == 'gf' and args.sortMethod == 'draw' and entry.id then
						local found = false
						local oldEntry = entry.id
						local newEntry = entry.id
						if firstEntry == nil then
							firstEntry = entry
						end
						local gf_d = entry.gf_d
						while not found do
							if entry.q then
								q_d = q_d + 1
								gf_d = q_d
							end
							if gf_d == draw then
								table.insert(entries, entry)
								draw = draw + 1
								found = true
							else
								newEntry = newEntry + 1
								entry = getEntry{contest=contest, id=newEntry}
								if not entry or entry.y ~= firstEntry.y then
									entry = firstEntry
									newEntry = entry.id
								end
								gf_d = entry.gf_d
							end
						end
					else
						table.insert(entries, entry)
					end
				end
			else
				table.insert(entries, entry)
			end
		elseif args.from == 'country' and hasValue(entry.cnty, args.country) then -- Get all entries from country
			table.insert(entries, entry)
		end
	end
	return entries
end

function getEntryNoFromCountry(contest, id)
	if id then
		local entry = getEntry{contest=contest, id=id}
		local country = entry.cnty
		if country['name'] then country = country['name'][1][1] end
		local entries = getAllEntriesFrom{contest=contest.id, from='country', country=country}
		local extraEntries = {} -- For more than one entry in a year (e.g. ESC 1956)
		for i, v in ipairs(entries) do
			if extraEntries[v.y] then
				extraEntries[v.y] = extraEntries[v.y] + 1
			else
				extraEntries[v.y] = 0
			end
			if v.id == entry.id then
				local removal = 0
				for _, v in pairs(extraEntries) do
					removal = removal + v
				end
				local result = i - removal
				if entry.cntye then
					result = result .. ' <small>(' .. entry.cntye .. ')</small>'
				end
				return result
			end
		end
	end
end

function getSemi(contest, id)
	local entry = getEntry{contest=contest, id=id}
	if entry then
		if entry.sf then
			return 'sf' .. tostring(entry.sf)	
		elseif entry.sf_d then
			return 'sf'
		end
	end
	return nil
end

function replaceNilValues(entryData, entryDataText)
	local length = tableLen(entryDataText)
	for i = 1, length do
		if not entryData[i] then
			entryData[i] = 'nil_' .. i
		end
	end
end

function calcAmountOfEntries(contest, year, show)
	amountOfEntries = tableLen(getAllEntriesFrom{contest=contest, from='year', year=year, show=show})
end

--------------------------------------------------

function insertZeroBeforeSingleDigit(x)
	if x then
		if x < 10 then
			x = '0' .. x
		end
	end
	return x
end

function getIndex(tab, reqV)
	if type(tab) == 'table' then
		for i, v in ipairs(tab) do
			if v == reqV then
				return i
			end
		end
	end
	return nil
end

function processTable(tab)
	local str = ''
	if type(tab) == 'table' then
		for i, v in ipairs(tab) do
			str = str .. v
			if i ~= tableLen(tab) then
				str = str .. ', '
			end
		end
		return str
	else return tab end
end

function tableLen(tab)
  local x = 0
  if tab then
	  for _ in pairs(tab) do
	  	x = x + 1
	  end
  end
  return x
end

function hasValue(tab, val)
	if tab then
		if type(tab) == 'table' then
		    for _, v in pairs(tab) do
		        if v == val or (type(v) == 'table' and hasValue(v, val)) then
		            return val
		        end
		    end
		elseif type(tab) == 'string' then
    		if tab == val then
    			return val
    		end
    	end
	end
    return false
end

return p