Lua kļūda Modulis:Documentation, 144. rinda: message: type error in message cfg.container (string expected, got nil).

reference = {}

wd = wd or require "Module:Wikidata/fr"

local article = require "Module:Wikidata/fr/Atsauces/classes d'articles"
local book = require "Module:Wikidata/fr/Atsauces/classes d'ouvrages"


local linguistic = require "Module:Valodas rīki"
local cite = require "Module:Biblio"
local langcodes = mw.loadData "Module:Wikidata/fr/Valodu kodi"
local tools = require 'Module:Wikidata/fr/Rīki'
local entities = require 'Module:Wikidata/fr/Formatēšana'
local getData = require 'Module:Wikidata/fr/Récup'

local function getStringStatementValues(entity, propertyId)
	local statements = entity:getBestStatements(propertyId)
	for i,statement in pairs(statements) do
		statements[i] = tools.getValue(statement.mainsnak)
	end
	return statements
end

local function getStringStatementValue(entity, propertyId)
	local statements = getStringStatementValues(entity, propertyId)
	if #statements > 0 then
		return statements[1]
	else
		return nil
	end
end

local function getDirectItemLang(workentity, property)
	itemlangs = getData.getClaims{entity = workentity, property = property}
	if itemlangs ~= nil then
		return langcodes[tools.getNumericId(itemlangs[1].mainsnak)]
	end
end

local function hasRole(statement, roleId)
	return statement.qualifiers and (statement.qualifiers['P518'] and tools.getId(statement.qualifiers['P518'][1]) == roleId or statement.qualifiers['P794'] and tools.getId(statement.qualifiers['P794'][1]) == roleId)
end	

function reference.citeitem(entityId, options)
	if not entityId then
		return nil
	end
	local entity = tools.getEntity(entityId)
	if not entity then
		return "invalid entity id"
	end

	--work entity if it exists
	local workEntity = nil
	local workClaims = getData.getClaims{entity = entity, property = 'P629'}
	if workClaims ~= nil then
		workEntity = tools.getEntity(tools.getMainId(workClaims[1]))
	end
	local journalEntity = nil
	local journalClaims = getData.getClaims{entity = entity, property = 'P1433'}
	if journalClaims ~= nil then
		journalEntity = tools.getEntity(tools.getMainId(journalClaims[1]))
	end

	local translator = wd.formatStatements{entity = entity, property = 'P655', defaultlink = '-'}
	local illustrator = wd.formatStatements{entity = entity, property = 'P110', defaultlink = '-'}
	local preface = ''
	local postface = ''
	local photographe = ''
	local authors = {}
	local are_director = {}
	local responsability = {}
	for _,statement in pairs(entity:getBestStatements('P50')) do
		table.insert(authors, wd.formatStatement(statement, {defaultlink = '-'}))
		table.insert(are_director, '')
		--qualifier pour la responsabilité
		if statement.qualifiers and statement.qualifiers['P518'] then
			table.insert(responsability, wd.formatSnak(statement.qualifiers['P518'][1], {defaultlink = '-'}))
		else
			table.insert(responsability, '')
		end
	end
	for _,statement in pairs(entity:getBestStatements('P98')) do
		table.insert(authors, wd.formatStatement(statement, {defaultlink = '-'}))
		table.insert(are_director, 'y')
		table.insert(responsability, '')
	end
	for _,statement in pairs(entity:getBestStatements('P767')) do
		if hasRole(statement, 'Q670787') or hasRole(statement, 'Q19839393') then --préface/préfacier
			preface = wd.formatStatement(statement, {defaultlink = '-'})
		elseif hasRole(statement, 'Q7234272') then --postface
			postface = wd.formatStatement(statement, {defaultlink = '-'})
		elseif hasRole(statement, 'Q33231') or hasRole(statement, 'Q125191') then --photographe/photographie
			photographe = wd.formatStatement(statement, {defaultlink = '-'})
		end
	end
	
	local isbns = getStringStatementValues(entity, 'P212')
	for _,isbn in pairs(getStringStatementValues(entity, 'P957')) do
		table.insert(isbns, isbn)
	end
	local issns = {}
	if journalEntity then
		issns = getStringStatementValues(journalEntity, 'P236')
	end
	if #issns == 0 then
		issns = getStringStatementValues(entity, 'P236')
	end
	local oclc = getStringStatementValue(entity, 'P243')
	local bnf = getStringStatementValue(entity, 'P268')
	local lccn = getStringStatementValue(entity, 'P1144')
	local dnb = getStringStatementValue(entity, 'P1292')
	local doi = getStringStatementValue(entity, 'P356')
	local pmid = getStringStatementValue(entity, 'P698')
	local jstor = getStringStatementValue(entity, 'P888')
	local bibcode = getStringStatementValue(entity, 'P1300')
	local arxiv = getStringStatementValue(entity, 'P818')
	local pmcid = getStringStatementValue(entity, 'P932')
	
	local ednumber = wd.formatStatements{entity = entity, property = 'P393'}
	local publisher = wd.formatStatements{entity = entity, property = 'P123', defaultlink = '-'}
	local journal = wd.formatStatements{entity = entity , property = 'P1433', defaultlink = '-'}
	--TODO fix local journalnumber = wd.formatStatements{entity = entity, property = 'P433'}
	--TODO fix local volume = wd.formatStatements{entity = entity, property = 'P478'}
	local publishdate = wd.formatStatements{entity = entity, property = 'P577', linktopic = '-'}
	local publishplace = wd.formatStatements{entity = entity, property = 'P291', defaultlink = '-'}
	local pagenum = wd.formatStatements{entity = entity, property = 'P1104'}
	local title = wd.formatStatements{entity = entity, property = 'P1476', numval = 1 } or entities.getLabel(entity)
	local titlelink = entities.getLink(entity)
	if not titlelink and workEntity then -- si pas de lien, on peut essayer de voir si c'est l'édition de quelque chose d'autre
		titlelink = entities.getLink(workEntity)
	end
	if title and titlelink then
		title = '[[' .. titlelink .. '|' .. title .. ']]'
	end
	local subtitle = wd.formatStatements{entity = entity, property = 'P1680'}
	local url = wd.formatStatements{entity = entity, property = 'P854', numval = 1} or wd.formatStatements{entity = entity, property = 'P953', numval = 1 }
	local wikisource = entity:getSitelink('frwikisource')
	local language = getDirectItemLang(entity, 'P407')
	if not language and journalEntity then
		language = getDirectItemLang(journalEntity, 'P407')
	end
	local originalLanguage = getDirectItemLang(entity, 'P364') --TODO: be clever? or getDirectItemLang(workEntity, 'P407') or getDirectItemLang(workEntity, 'P364')
	local originalTitle = nil
	if workEntity and originalLanguage then
		originalTitle = wd.formatStatements{entity = workEntity, property = 'P1476', numval = 1 } or workEntity:getLabel(originalLanguage)
	end

	-- choose relevant cite type
	local entitytype = wd.formatStatements{property = 'P31', entity = entity, numval = 1, displayformat = 'raw'}
	local funtype

	if (entitytype == "Q3331189") then --book edition
		funtype = cite.ouvrage
		
		-- TODO : gérer l'édition et l'élément de l'oeuvre. 
		
	elseif book.is_book_class(entitytype) then -- ouvrage sans édition connue ?
		
		-- TODO : gérer le cas ou un ouvrage est cité mais que Wikidata en connait des éditions.
		-- TODO : refuser ce genre d'items ?
		
		funtype = cite.ouvrage
		
	elseif article.is_article_class(entitytype) then -- article
		funtype = cite.article
	else -- default: output a very simple display
		local parameters = authors
		if title then 
			 table.insert(parameters, '<i>' .. title .. '</i>')
		end
		if publisher then
			table.insert(parameters, publisher)
		end
		if publishplace then
			table.insert(parameters, publishplace)
		end
		if publishdate then
			table.insert(parameters, publishdate)
		end
		if options['page'] then
			table.insert(parameters, 'p.' .. options['page'])
		end
		if options['accessdate'] then
			table.insert(parameters, 'consulté le ' .. options['accessdate'])
		end
		return linguistic.conj(parameters, 'comma')
	end
	
	local parameters = {
		['traducteur'] = translator,
		['illustrateur'] = illustrator,
		['préface'] = preface,
		['postface'] = postface,
		['photographe'] = photographe,
		['titre'] = title,
		['sous-titre'] = subtitle,
		--['volume'] = volume,
		['date'] = publishdate,
		['lieu'] = publishplace,
		['périodique'] = journal,
		--['numéro'] = journalnumber,
		["numéro d'édition"] = ednumber,
		['éditeur'] = publisher,
		['pages totales'] = pagenum,
		['lire en ligne'] = url,
		['wikisource'] = wikisource,
		['isbn'] = isbn,
		['isbn2'] = isbn2,
		['issn'] = issn,
		['oclc'] = oclc,
		['bnf'] = bnf,
		['lccn'] = lccn,
		['dnb'] = dnb,
		['doi'] = doi,
		['pmid'] = pmid,
		['jstor'] = jstor,
		['bibcode'] = bibcode,
		['arxiv'] = arxiv,
		['pmcid'] = pmcid,
		['page'] = options['page'],
		['langue'] = language,
		['langue originale'] = originalLanguage,
		['titre original'] = originalTitle,
		['plume'] = options['plume']
	}
	for i,isbn in pairs(isbns) do
		parameters['isbn' .. i] = isbn
	end
	
	isbns = remove_same_isbn(isbns)
	
	for i,issn in pairs(issns) do
		parameters['issn' .. i] = issn
	end
	
	-- try to find if there is an author set in options
	local setAuthors = true
	for i=1,15 do
		if options['auteur' .. i]  or options['directeur' .. i] or options['responsabilité' .. i] then
			authorsAlreadySets = false
		end
	end

	if setAuthors then
		for i,author in pairs(authors) do
			parameters['auteur' .. i] = author
			parameters['directeur' .. i] = are_director[i]
			parameters['responsabilité' .. i] = responsability[i]
		end
	end
	
	for parameter,value in pairs(options) do
		parameters[parameter] = value
	end
	local val = funtype(parameters)

	if val then
		return wd.addRefAnchor(wd.addLinkback(val, entity), tools.EntityId(entity))
	end
	
	return tools.formatError()
end

-- pour tester
function reference.citeItem(frame)
	local args = frame:getParent().args
	return reference.citeitem(args[1], args)
end


function isbn13_reduce(isbn)
	-- isbn: value an normalized 
	-- return: isbn10 if isbn is isbn10 or isbn13 reducable to isbn10 (the key might be wrong)
	
	local string = require("string")
	
	-- consider all non isbn13 as reduced
	if isbn:len() ~= 13 then
		return isbn
	end

	if string.sub(isbn,1,3) == '978' then
	     return string.sub(isbn,4)
	else
		mw.log("not reducable"..string.sub(isbn,1,3))
	    return isbn
	end
end

function remove_isbn_key(isbn)
	-- isbn: isbn normalized
	-- return isbn without key for future comparison
	
	--remove last char see https://www.rosettacode.org/wiki/Substring/Top_and_tail#Lua
	return isbn:sub(1,-2)
    
end 

function remove_same_isbn(isbns)
	-- input a table of isbns
	-- return a table of ibns without duplicate
	-- asked in https://fr.wikipedia.org/wiki/Discussion_mod%C3%A8le:Bibliographie#ISBN-10_et_ISBN-13
	result = {}
	for _,isbn1 in ipairs(isbns) do
		isbn1 = isbn1:gsub( '[-%s]', '' )
		mw.log(isbn1)
		local same = false
		for index_isbn2,isbn2 in ipairs(result) do
   			if  same_isbn(isbn1,isbn2) then
   				if isbn2:len() == 13 then
   					table.remove(result,index_isbn2)
   					table.insert(result,isbn1)
   				end
   				same = true
   				break
   			end
   			
   		end
   		if not same then
   			table.insert(result,isbn1)
   		end
   end
   return result
end
   
   		
   		
function same_isbn(isbn1,isbn2)
	
	-- input: two normalized isbn
    local References = require( 'Module:Biblio/Références' )
    -- first step check if both isbn are valid
	if not ( References.checkisbn(isbn1) and References.checkisbn(isbn2) ) then 
		mw.log("References.checkisbn(isbn1) "..tostring(References.checkisbn(isbn1)) )
		mw.log("References.checkisbn(isbn2) "..tostring(References.checkisbn(isbn2)) )
		return false
	end
	
	isbn1 = isbn1:gsub( '[-%s]', '' )
	isbn2 = isbn2:gsub( '[-%s]', '' )
    isbn1 = remove_isbn_key(isbn13_reduce(isbn1))
    isbn2 = remove_isbn_key(isbn13_reduce(isbn2))
    mw.log("comparing " .. isbn1 .. " and " .. isbn2)
    if isbn1 == isbn2 then
    	return true
    else
    	return false
    end
end 

function unittest_remove_same_isbn(isbns,expected)
	local comparison ="remove_same_isbn("..tostring(isbns)..") == " ..tostring(expected)
	mw.log("asserting "..comparison) 
    if not remove_same_isbn(isbns) == expected  then
    	mw.log("ERROR: ".. comparison)
    end
end

function unittest_same_isbn(isbn1,isbn2,expected)
	local comparison ="same_isbn("..isbn1..","..isbn2..") == " ..tostring(expected)
	mw.log("asserting "..comparison) 
    if not same_isbn(isbn1,isbn2)  == expected  then
    	mw.log("ERROR: ".. comparison)
    end
end

local test_remove_same_isbn = function ()
	local test = unittest_remove_same_isbn
	mw.log("testing same isbn")
	test({"978-0-85199-430-7","0-85199-430-X"},{"0-85199-430-X"})
	test({"0-85199-430-X","978-0-85199-430-7"},{"0-85199-430-X"})
	
	test({"0-85199-430-X","0-85199-430-X"},{"0-85199-430-X"})
	test({"978-0-85199-430-7","978-0-85199-430-7"},{"978-0-85199-430-7"})
	
	mw.log("testing invalid isbn")
	test({"978-0-85199-430-7","0-85199-435-X"},{"978-0-85199-430-7","0-85199-435-X"})
	test({"978-0-85199-430-7","0-85199-430-1"},{"978-0-85199-430-7","0-85199-430-1"})
    test({"978-0-85199-430-5","0-85199-430-1"},{"978-0-85199-430-5","0-85199-430-1"})
    test({"978-0-85199-432-5","0-85199-430-1"},{"978-0-85199-432-5","0-85199-430-1"})
    mw.log("todo: non equal isbn but valid")
    test({"273812379","9782738123275"},{"273812379"})
	
end 

local test_same_isbn = function ()
	local test = unittest_same_isbn
	mw.log("testing same isbn")
	test("978-0-85199-430-7","0-85199-430-X",true)
	test("0-85199-430-X","0-85199-430-X",true)
	test("978-0-85199-430-7","978-0-85199-430-7",true)
	
	mw.log("testing invalid isbn")
	test("978-0-85199-430-7","0-85199-435-X",false)
	test("978-0-85199-430-7","0-85199-430-1",false)
    test("978-0-85199-430-5","0-85199-430-1",false)
    test("978-0-85199-432-5","0-85199-430-1",false)
    mw.log("todo: non equal isbn but valid")
	
end 


--used to test the duplicate isbn functions, use in the console like this
-- p.test()
reference.test = function()
	test_same_isbn()
	test_remove_same_isbn()
end

return reference