Aller au contenu

Module:Country data/Bac à sable

Une page de Wikipédia, l'encyclopédie libre.
Ceci est une version archivée de cette page, en date du 1 avril 2023 à 11:50 et modifiée en dernier par Golmote (discuter | contributions) (Correction de valueAtDate pour gérer à la fois les cas des tableaux de topic et les cas des tableaux de period). Elle peut contenir des erreurs, des inexactitudes ou des contenus vandalisés non présents dans la version actuelle.

 Documentation[voir] [modifier] [historique] [purger]

Ce module contient des informations utilisées par le système country.

--[[
This module is intended to replace the functionality of {{drapeau2}} and related
templates.  It provides several methods, including
]]
local titleSelf = mw.title.new(... or mw.getCurrentFrame():getTitle())
local sandbox = false
if titleSelf.isSubpage then
	sandbox = titleSelf.subpageText:match[[^Bac à sable]] and true
end

local p = {};

local gdata = mw.loadData ("Module:Country data/liste" .. (sandbox and "/Bac à sable" or ""))
local dates = require "Module:date complexe"
local linguistic = require "Module:Linguistique"
local Outils = require "Module:Outils"
local Drapeau = require("Module:Drapeau" .. (sandbox and "/Bac à sable" or ""))

local function _getCompetition(c,aaaa)
	local gdomain = mw.loadData ( "Module:Drapeau/Domaine" ) -- chargé seulement sur les articles où ça sert, ça prend un peu de place
	
	local symbs = {
		['cm'] = "à la Coupe du monde",
		['coupedumonde'] = "à la Coupe du monde",
		['ce'] = "au championnat d'Europe",
		['euro'] = "au championnat d'Europe",
		['chm'] = "au championnat du monde",
		['can'] = "à la Coupe des Confédérations",
		['coupedesconfederations'] = "à la Coupe des Confédérations",
		['en'] = "en",
		['jo'] = "aux Jeux olympiques",
		['jp'] = "aux Jeux paralympiques",
	}

	local str = symbs[string.lower(c or '')] or ""
	
	--edition
	if(aaaa ~= nil and aaaa ~= "") then
		if(c=="jo"  or c=="jp" ) then
			local o=gdomain.jo["_"..aaaa];
			if(o ~= nil) then
				str = str .." "..o
			end
		else
		   str = str .." "..aaaa
		end
	end

	return str
end

local function printFlag(flagfile, alt, displayformat)
	
	displayformat = displayformat or {}
	local size = displayformat.size or '20x15'
	local border = 'border|' 
	
	if displayformat.border == '-' then
		border = ''
	end
	
	if not alt then
		alt = ''
	end
	return '<span class="flagicon">' ..
			'[[Fichier:' .. flagfile .. '|' .. size ..'px|' .. border  .. alt ..'|class=noviewer]]' ..
			'</span>';
end

local function bestfordate(data, period) -- data contient une table dont les clés sont des dates au format ['2010'] = ou ['2010-05-04'] = 
	if type(data) == 'string' then
		return data
	end

	-- très artisanal, à améliorer
	if (not period) or (period == 'default') then
		return data.default
	end
	
	local val = data.default
	local bestdate = '-1000-01-01'
	for i, j in pairs(data) do
		if j ~= 'default' and dates.before(period, i) and dates.before(i, bestdate) then
			val = j
			bestdate = i
		end -- bestdate est la date la plus récente antérieure ou égale à period, val est la donnée à cette date
	end
	return val
end


local function valueAtDate(data, period, topic, forceTopic) -- topic: type de drapeau, genre de l'adjectif, etc.
	if type(data) == 'string' or type(data) == 'nil' then
		return data
	end
	if type(data) ~= 'table' then
		return error('mauvais datatabpe ' .. type(data))
	end
	topic = topic or "default"
	if topic ~= "default" and data[topic] then
		return bestfordate(data[topic], period)
	elseif topic == "default" or forceTopic ~= true then
		if type(data.default) == 'table' and data.default.default then
			return bestfordate(data.default, period)
		else
			return bestfordate(data, period)
		end
	end

	return error()
end

local function getData(datatable, typedata, topic, period, forceTopic) -- récupère la chaîne de caractère la plus appropriée dans la datatable
	-- datatable: la table de sous module par exemple [[Module:Country data/grenade]]
	-- typedata: "flag" / "name" / "adjective"
	-- period: data in ISO format
	-- topic: for instance "navy" for naval ensign
	local val = datatable[typedata]

	if not val then -- error handling ?
		return nil
	end

	local val = valueAtDate(val, period, topic, forceTopic)

	if type(val) ~= 'string' then -- error handling ?
		return error(val)
	end
	return val
end

local function getAdjective(data, gender, number)
	if not gender then
		gender = 'm'
	end
	if not number then
		number = 's'
	end
	if (gender ~= 'm' and gender ~= 'f') then
		return error('gender devrait être m ou f mais est ' .. gender)
	end
	if (number ~= 's' and number ~= 'p') then
		return error('number devrait être s ou p mais est ' .. number)
	end
	return getData(data, 'adjective', (gender .. number))
end

local function getDemonym (data, gender, number)
	if data.demonym == nil then
		return linguistic.ucfirst( getAdjective(data, gender, number) )
	end
	gender = gender or 'm'
	number = number or 's'
	if (gender ~= 'm' and gender ~= 'f') then
		return error('gender devrait être m ou f mais est ' .. gender)
	end
	if (number ~= 's' and number ~= 'p') then
		return error('number devrait être s ou p mais est ' .. number)
	end
	return getData(data, 'demonym', (gender .. number))
end

local function getLabel(data, topic, period, form)
	local label
	if (not form) or form == 'short' then
		label = getData(data, 'shortname', topic, period)
	end
	if not label then
		label = getData(data, 'name')
	end
	if (not label) and data.item then
		label = mw.wikibase.label(getData(data, 'item'))
	end
	return label
end

local function getLink(data, topic, period)
	local link = getData(data, 'link', topic, period)
	if (not link) and data.item then
		link =  mw.wikibase.label(getData(data, 'item'))
	end
	return link
end

local function applyregex(str, areadata)
	local cio = 'code CIO (en attente)' --require('Module:Wikidata')._formatStatements({entity= areadata.item, property= 'P984'}) or '??'

	local label = getData(areadata, 'name')
	local of = linguistic.of(label, areadata.genre)
	
	str = mw.ustring.gsub(str, '$de$label', of)
	str = mw.ustring.gsub(str, '$label', label)
	str = mw.ustring.gsub(str, '$cio', 'cio') 

	if string.find(str, '$gentile') then
		local function get(genre) return getData(areadata, 'adjective', genre) end
		local gentileMS, gentileFS, gentileMP, gentileFP = get('ms'), get('fs'), get('mp'), get('fp')
		str = mw.ustring.gsub(str, '$gentileMS', gentileMS)
		str = mw.ustring.gsub(str, '$gentileFS', gentileFS)
		str = mw.ustring.gsub(str, '$gentileMP', gentileMP)
		str = mw.ustring.gsub(str, '$gentileFP', gentileFP)
	end
	return str
end

local function getDatatable(zone)
	zoneLower = mw.ustring.lower(zone)
	zoneUpper = mw.ustring.upper(zone)
	zone = gdata.wikidata[zoneLower] or gdata.alpha3[zoneUpper] or zoneLower
	return require('Module:Country data/' .. zone)
end


local function flagIcon(data, flagtype, period, displayformat)
	local flagimage = getData(data, 'flag', flagtype, period)
	if flagimage then
		return printFlag(flagimage, '', displayformat)
	end
end

local function getcontents(frame,country,params)
  return frame:expandTemplate({title="Country data "..country;args=params})
end

function p.gettable(frame,country,params)
--Returns the parameters of a country data template as a Lua table
  --If not a valid data template, return empty table
  local bool, s = pcall(getcontents,frame,country,params or {})
  if bool and (string.find(s,"^%{%{ *%{%{%{1") or string.find(s,"^%{%{safesubst: *%{%{%{1"))
  then
    --Replace parameter delimiters with arbitrary control characters
    --to avoid clashes if param values contain equals/pipe signs
    s = string.gsub(s,"|([^|=]-)=","\1\1%1\2")
    s = string.gsub(s,"}}%s*$","\1")
    --Loop over string and add params to table
    local part = {}
    for par in string.gmatch(s,"\1[^\1\2]-\2[^\1\2]-\1") do
      local k = string.match(par,"\1%s*(.-)%s*\2")
      local v = string.match(par,"\2%s*(.-)%s*\1")
      if v and not (v=="" and string.find(k,"^flag alias")) then
        part[k] = v
      end
    end
    return part
  else
  	return {}
  end
end

function p.gettemplate(frame)
--For testing, recreates the country data from the created Lua table
  --Get data table
  local data = p.gettable(frame,frame.args[1])
  --Concatenate fields into a template-like string
  local out = "{{ {{{1}}}"
  for k,v in pairs(data) do
    out = out.."\n| "..k.." = "..v
  end
  return out.."\n}}"
end

local function convertTemplateToModuleFormat(data)
	if not data.alias or not data['flag alias'] then
		return false, nil
	end
	local newData = {
		name = data['shortname alias'] or data.alias,
		link = {default = {default = data.alias}},
		flag = {default = {default = data['flag alias']}},
		--flagsize = {},
		--flagborder = {},
		--flagalt = {},
		--sortkey = {},
	}
	if data.size and data.size ~= '' then
		newData.size = {default = {default = data.size}}
	end

	local keys = {
		link = 'link',
		flag = 'flag',
		['size flag'] = 'flagsize',
		border = 'flagborder',
		['alt attribute'] = 'flagalt',
		sortkey = 'sortkey',
	}
	local topics = {
		naval = 'naval',
		['air force'] = 'airforce',
		army = 'army',
		marines = 'marines',
		['coast guard'] = 'coastguard',
		military = 'military',
	}
	for k, v in pairs(data) do
		local foundKey, foundTopic, foundPeriod
		for key, newKey in pairs(keys) do
			for topic, newTopic in pairs(topics) do
				if k == key .. ' alias-' .. topic then
					foundPeriod = 'default'
				else
					foundPeriod = k:match('^' .. key .. ' alias%-' .. topic .. '%-(%d+)$')
				end
				if foundPeriod then
					foundTopic = newTopic
					break
				end
			end
			if not foundTopic then
				if k == key then
					foundPeriod = 'default'
				else
					foundPeriod = k:match('^' .. key .. ' alias%-(%d+)$')
				end
				if foundPeriod then
					foundTopic = 'default'
				end
			end
			if not foundTopic then
				foundPeriod = 'default'
				foundTopic = k:match('^' .. key .. ' alias%-(.+)')
			end
			if foundTopic then
				foundKey = newKey
				break
			end
		end
		if foundKey and foundTopic and foundPeriod then
			newData[foundKey] = newData[foundKey] or {}
			newData[foundKey][foundTopic] = newData[foundKey][foundTopic] or {}
			newData[foundKey][foundTopic][foundPeriod] = v
		end
	end
	return true, newData
end

-- Retrouver topic et period utilisés par le format modules de données à partir de la variant utilisée par les modèles
local function getTopicAndPeriodFromVariant(variant)
	if variant == nil then
		return nil, nil
	end
	-- TODO déplacer un niveau au-dessus pour éviter la répétition ?
	local topics = {
		naval = 'naval',
		['air force'] = 'airforce',
		army = 'army',
		marines = 'marines',
		['coast guard'] = 'coastguard',
		military = 'military',
	}
	for topic, newTopic in pairs(topics) do
		if variant == topic then
			return newTopic, nil
		end
		local period = variant:match('^' .. topic .. '%-(%d+)$')
		if period then
			return newTopic, period
		end
	end
	if variant:match('^(%d+)$') then
		return nil, variant
	end
	return variant, nil
end

local function getDatatableFromTemplate(country)
	local data = p.gettable(mw.getCurrentFrame(), country)
	if next(data) == nil then
		return false, nil
	end
	return convertTemplateToModuleFormat(data)
end

local function getDatatableFallback(zone)
	local success, data = pcall(getDatatable, zone)
	if not success then
		success, data = getDatatableFromTemplate(zone)
	end
	return success, data
end

local function getAnyDatatable(zone, usedata)
	if usedata == 'template' then
		return getDatatableFromTemplate(zone)
	elseif usedata == 'module' then
		return pcall(getDatatable, zone)
	else
		return getDatatableFallback(zone)
	end
end

function p.standarddisplay(zone, args)
	if not zone then
		return nil
	end
	-- nettoyage des paramètres
	if not args then
		args = {}
	end
	for i, j in pairs(args) do
		args[i] = mw.text.trim(j) -- remove whitespaces
		if args[i] == '' then args[i] = nil end
	end

   -- ajout des valeurs par défaut
	local size = args.flagsize or '20x15'
	local flagtype = args.type

	local align = args.align or 'left'
	local link = args.link
	local period = args.date
	local competition = args.competition
	local edition = args.edition
	local extra = ''
	
	local success, data = pcall(getDatatable, zone)
	if not success then
		success, data = getDatatableFromTemplate(zone)
	end
	if not success then
		if args.strict then
			return error('lieu non reconnu')
		end
		return nil
	end
	
	-- image
	local flag = flagIcon(data, flagtype, period)
	if (args.label == '-') then
		return flag, true
	end

	-- text
	local text
	local label = getLabel(data, flagtype)
	local link = getLink(data, flagtype)

	if competition then 
		competition = _getCompetition(linguistic.toascii(competition),args["édition"])
	end
	if link and competition then
		link = link .. ' ' .. competition
	end

	if link then
		text = '[[' .. link .. '|' .. label .. ']]'
	end

	-- si les données sont extraites d'un formulaire standard comme module:Drapeau/domain, appliquer des regex
	if string.match(text, '%$') then
		text = applyregex(text, data)
	end
	
	local val
	if align == 'right' then
		val = text .. '&nbsp;' .. flag
	else
		val = flag .. '&nbsp;' .. text
	end
	
	return val, true -- true indique le succès
end

-- fonction appelable avec #invoke
function p.standarddisplay2(frame)
	result, success = p.standarddisplay(frame.args['pays'],frame.args)
	return result
end

-- fonction utilitaire pour récupérer les données du drapeau
function p.getFlagData(country, topic, period, usedata)
	local success, datatable = getAnyDatatable(country, usedata)
	if not success then
		return error('Lieu non reconnu') -- FIXME comment faudrait-il gérer l'erreur ?
	end
	
	local name = getData(datatable, 'name', topic, period)
	local sortkey = getData(datatable, 'sortkey', topic, period)
	if not sortkey then
		sortkey = name
	end
	
	local flag = getData(datatable, 'flag', topic, period)
	local border = getData(datatable, 'flagborder', topic, period)
	border = border ~= ''
	local alt = getData(datatable, 'flagalt', topic, period)
	if not alt then
		alt = name
	end
	local size = getData(datatable, 'flagsize', topic, period)
	local link = getData(datatable, 'link', topic, period)
	if not link then
		link = name
	end
	local gender = getData(datatable, 'gender', topic, period)
	return {
		flag = flag,
		name = name,
		sortkey = sortkey,
		border = border,
		alt = alt,
		size = size,
		link = link,
		gender = gender,
	}
end

-- modèle {{pays}}
function p.pays(frame)
	local args = Outils.extractArgs(frame)
	local templatename = args[1]
	local name = Outils.validTextArg(args, 'name', 'nom', 1)
	local variant = Outils.validTextArg(args, 'variant', 2)
	local size = Outils.validTextArg(args, 'size', 'taille')
	if not size then
		size = '20x18'
	end
	size = size .. 'px'
	
	-- FIXME La rétrocompatibilité parfaite avec {{pays}} implique de mettre à jour les sous-modules de données
	-- par exemple ajouter border="" dans le sous-module Module:Country data/népal
	
	local topic, period = getTopicAndPeriodFromVariant(variant)

	-- TODO args.usedata ne devrait pas avoir lieu d'être à terme, mais il est utile pour tester
	local flagData = p.getFlagData(templatename, topic, period, args.usedata)
	
	local alt = flagData.alt
	if alt == nil then
		alt = flagData.name
	end
	
	return Drapeau.displayFlag(flagData.flag, {
		border = flagData.border,
		size = size,
		alt = alt,
		sortkey = flagData.sortkey,
		text = '[[' .. flagData.link .. '|' .. name .. ']]'
	})
end

-- modèle {{pays/lien seul}}
function p.pays_lien_seul(frame)
	local args = Outils.extractArgs(frame)
	local templatename = args[1]
	local variant = Outils.validTextArg(args, 2)
	
	-- TODO args.usedata ne devrait pas avoir lieu d'être à terme, mais il est utile pour tester
	local success, datatable = getAnyDatatable(templatename, args.usedata)
	if not success then
		return error('Lieu non reconnu') -- FIXME comment faudrait-il gérer l'erreur ?
	end
	
	local topic, period = getTopicAndPeriodFromVariant(variant)
	
	local name = getData(datatable, 'name', topic, period)
	local link = getData(datatable, 'link', topic, period)
	if not link then
		link = name
	end
	
	return '[[' .. link .. '|' .. name .. ']]'
end

-- Selon la valeur de topic, implémente les modèles
-- naval : {{marine de guerre}}
-- army : {{armée de terre}}
-- airforce : {{armée de l'air}}
local function topicTemplate(frame, topic, prefix)
	local args = Outils.extractArgs(frame)
	local templatename = args[1]
	local period = Outils.validTextArg(args, 'variant', 2)
	local size = Outils.validTextArg(args, 'size')
	if not size then
		size = '22x20px'
	end
	local name = Outils.validTextArg(args, 'name')
	
	-- TODO args.usedata ne devrait pas avoir lieu d'être à terme, mais il est utile pour tester
	local success, datatable = getAnyDatatable(templatename, args.usedata)
	if not success then
		return error('Lieu non reconnu') -- FIXME comment faudrait-il gérer l'erreur ?
	end
	
	local flag = getData(datatable, 'flag', topic, period)
	local border = getData(datatable, 'flagborder', topic, period)
	border = border ~= ''

	local linkSuccess, link = pcall(getData, datatable, 'link', topic, period, true)
	if not linkSuccess then
		link = prefix .. getData(datatable, 'name', topic, period)
	end
	
	return Drapeau.displayFlag(flag, {
		border = border,
		size = size,
		text = '[[' .. link .. (name and ('|' .. name) or '') .. ']]'
	})
end

-- modèle {{marine de guerre}}
function p.marine_de_guerre(frame)
	return topicTemplate(frame, 'naval', 'Marine de ')
end

-- modèle {{armée de terre}}
function p.armee_de_terre(frame)
	return topicTemplate(frame, 'army', 'Armée de ')
end

-- modèle {{armée de l'air}}
function p.armee_de_l_air(frame)
	return topicTemplate(frame, 'airforce', 'Force aérienne de ')
end

-- modèle {{drapeau}}
function p.drapeau(frame)
	local args = Outils.extractArgs(frame)
	local templatename = args[1]
	local variant = Outils.validTextArg(args, 'variant', 2)
	local size = Outils.validTextArg(args, 'size', 'taille')
	if not size then
		size = '20x18'
	end
	size = size .. 'px'
	
	local topic, period = getTopicAndPeriodFromVariant(variant)
	
	-- TODO args.usedata ne devrait pas avoir lieu d'être à terme, mais il est utile pour tester
	local success, flagData = pcall(p.getFlagData, templatename, topic, period, args.usedata)
	if not success then
		flagData = {}
	end
	
	local alt = flagData.alt
	if alt == nil and flagData.name then
		alt = 'Drapeau : ' .. flagData.name
	end
	
	return Drapeau.displayFlag(flagData.flag, {
		border = flagData.border,
		size = size,
		alt = alt
	})
end

-- modèle {{getalias}}
function p.getalias(frame)
	local args = Outils.extractArgs(frame)
	local templatename = args[1]
	local name = Outils.validTextArg(args, 2)
	local variant = Outils.validTextArg(args, 'variant', 3)
	
	-- TODO args.usedata ne devrait pas avoir lieu d'être à terme, mais il est utile pour tester
	local success, datatable = getAnyDatatable(templatename, args.usedata)
	if not success then
		return error('Lieu non reconnu') -- FIXME comment faudrait-il gérer l'erreur ?
	end
	
	local topic, period = getTopicAndPeriodFromVariant(variant)
	
	-- Seuls "link" (par défaut), "shortname" et "flag" sont documentés.
	if name == 'flag' then
		return getData(datatable, 'flag', topic, period)
	end
	if name == 'shortname' then
		return getData(datatable, 'name', topic, period)
	end
	return getData(datatable, 'link', topic, period)
end

function p.nationality(zone, gender, number, topic, period)
	local success, data = pcall(getDatatable, zone)
	if not success then return
		zone
	end
	
	local str = getAdjective(data, gender, number)
	if not str then
		return zone
	end
	local link = getLink(data, topic, period) 
	if link then
		str = '[[' .. link .. '|' .. str .. ']]'
	end
	return str, true-- true indique le succès
end

function p.getNationality(args) -- pour obtenir la nationalité d'une personne sur Wikidata sous forme d'adjectifs
	if not args then
		return nil
	end
	local wikidata = require "Module:Wikidata"
	local complexdate = require "Module:Date complexe"
	
	if type(args) == 'string' then -- si un seul argument, c'est l'entité à utiliser
		args = {item = args}
	end
	if args.args then -- si vient de frame
		args = args.args
	end
	local item = args.item or args[1]
	
	-- établit la variable gender pour l'élément
	local vals = {
		['Q6581072'] = 'f',
		['Q6581097'] = 'm',
		default	  = '?'
	}
	local gender = args.gender
	if not gender then
		gender = wikidata.formatStatements{entity = item, property = 'P21', displayformat = 'raw'}
		gender = vals[gender] or vals.default
	end
	-- désactivation si date de naissance avant l'Ère contemporaine : trop d'imprécisions et d'anachronismes
	local mindate = args.mindate or '1789'
	if mindate ~= "-" then
		local birthdate = wikidata.formatStatements{entity = item, property = 'P569', displayformat = 'raw', numval = 1}
                local deathdate = wikidata.formatStatements{entity = item, property = 'P570', displayformat = 'raw', numval = 1}
		if ((not birthdate) or complexdate.before(mindate, birthdate)) and ((not deathdate) or complexdate.before(mindate, deathdate)) then
			return nil
		end
	end
	return wikidata.formatStatements{
			property = 'P27',
			showdate = true,
			entity = item,
			conjtype = args.conjtype or 'and',
			removedupes = true,
			linktopic = '-',
			displayformat = 
				function(snak)
					local g = gender -- genre de la personne, pour affichage du gentilé
					if g == '?' then -- si inconnu, au masculin
						g = 'm'
					end
					local val, success = p.nationality(wikidata.getId(snak), g)
					if not success then
						val = wikidata.formatSnak(snak)
					end
					return val
				end
	}
end

return p