Naar inhoud springen

Module:Wikidata Boek

Uit Wikisource

Deze module kan door een andere module worden gebruikt om wikidata over een werk te verkrijgen. Zie de comments bij de functie vraagWikidata.


local p = {}

--[=[
	intern gebruikt voor auteurs & vertalers & ~nu ook uitgevers & plaatsen~ het is eigenlijk gewoon voor wikidata-items ^^

	functiesignatuur:
	formatteerAuteur(auteur, -- het item zelf
					 genereerLink=true, -- Wel of niet links genereren.
					 gebruikOorspronkelijkeSchrijfwijze=true, --[[
								Gebruik de tekst van de qualifier "Oorspronkelijke schrijfwijze",
								als die bestaat. Heeft altijd de hoogste prioriteit als het aanstaat.
						]]
					 gebruikPaginaTitel=<volgt standaard genereerLink>, --[[
								Gebruik lokale paginatitel eerder dan een eigenschap of label van Wikidata.
								Indien niet los gegeven, staat het aan als geneerLinks aanstaat en anders uit.
								Prioriteit net onder oorspronkelijke schrijfwijze.
						]]
					 prefereerEigenschap=nil --[[
								Gebruik een bepaalde eigenschap van de Wikidatapagina van de auteur,
								zoals "naam in moedertaal". De eigenschap moet een tekenreeks zijn,
								niet een ander Wikidata-item.
								Prioriteit hoger dan het label op Wikidata, maar lager dan
								oorspronkelijke schrijfwijze en de lokale paginatitel.
						]]
					)
]=]
local function formatteerAuteur(t)
	setmetatable(t, {__index = {
		genereerLink = true,
		gebruikOorspronkelijkeSchrijfwijze = true,
		gebruikPaginaTitel = nil,
		prefereerEigenschap = nil
	}})
	local auteur, genereerLink, gebruikOorspronkelijkeSchrijfwijze, gebruikPaginaTitel, prefereerEigenschap =
		t[1] or t.auteur,
		t[2] or t.genereerLink,
		t[3] or t.gebruikOorspronkelijkeSchrijfwijze,
		t[4] or t.gebruikPaginaTitel,
		t[5] or t.prefereerEigenschap
		

	local auteur_id -- wikidata ID
	local auteurspagina -- pagina
	local auteurstekst -- zichtbare tekst die teruggegeven wordt
	
	if gebruikOorspronkelijkeSchrijfwijze and auteur.qualifiers and auteur.qualifiers['P1932'] then
		-- er is een oorspronkelijke schrijfwijze en we willen hem gebruiken!
		auteurstekst = mw.wikibase.formatValue(auteur.qualifiers['P1932'][1])
	end
	
	if auteurstekst and not genereerLink then
		-- Als we de tekst al hebben en geen link hoeven, dan zijn we klaar.
		return auteurstekst
	end
	

	if auteur.mainsnak.snaktype == 'value' then
		-- Tijd om de wikidata van de auteur te vinden.
		auteur_id = auteur.mainsnak.datavalue.value.id
		
		-- Nu de paginalink, als we die nodig hebben:
		if genereerLink or gebruikPaginaTitel then
			auteurspagina = mw.wikibase.getSitelink(auteur_id)
			
			if not auteurstekst
				and (gebruikPaginaTitel
					 or (gebruikPaginaTitel ~= false and genereerLink)) then

				if auteurspagina then
					auteurstekst = mw.title.new(auteurspagina).text
				end
			end
		end
		
		-- als we de auteurstekst nog steeds niet gevonden hebben
		if not auteurstekst then
			if prefereerEigenschap then
				local eigenschap =  mw.wikibase.getBestStatements(auteur_id, prefereerEigenschap)[1]
				if eigenschap and eigenschap.mainsnak.snaktype == 'value' then
					assert(eigenschap.mainsnak.datavalue.type == 'string', "Geprefereerde eigenschap was geen string.")
					auteurstekst = eigenschap.mainsnak.datavalue.value
				end
			end
			
			-- nog steeds niet? dan gebruiken we het label
			if not auteurstekst then
				auteurstekst = mw.wikibase.getLabel(auteur_id)
			end
		end
	end
	
	if genereerLink and auteurspagina then
		return '[[:' .. auteurspagina .. '|' .. auteurstekst .. ']]'
	else
		return auteurstekst
	end
end

--[[ 
	Gegeven een eerder opgehaalde wikidata-entiteit, geeft deze functie een
	lijst met de namen van alle auteurs. Er wordt gekeken naar zowel eigenschap
	P50 (auteur) als P2093 (auteur als tekenreeks – voor auteurs zonder
	wikidata-pagina).
	
	Auteurs die een Wikisource-pagina hebben worden een link naar die pagina.
	
	Als de auteur in het werk de qualifier P1932 (oorspronkelijke schrijfwijze)
	heeft, dan wordt dát de tekst die geretourneert wordt.
]]
local function getAuteurs(entiteit)
	-- LET OP auteurs kunnen een volgnummer (P1545) hebben, om ze te sorteren; daar wordt nog niet naar geluisterd.

	local geformatteerdeAuteurs = {}
	
	-- eerst auteurs met een Wikidata-item
	local auteurs = entiteit:getBestStatements('P50')
	for i, auteur in ipairs(auteurs) do
		table.insert(geformatteerdeAuteurs, formatteerAuteur{auteur})
	end
	
	-- dan is er nog een los attribuut voor auteurs zonder wikidata-item
	local auteurs = entiteit:getBestStatements('P2093')
	for i, auteur in ipairs(auteurs) do
		table.insert(geformatteerdeAuteurs, mw.wikibase.formatValue(auteur.mainsnak))
	end
	
	return geformatteerdeAuteurs
end

--[[
	Hetzelfde te gebruiken als getAuteurs. Verschil in de werking is dat er
	op dit moment geen eigenschap “vertaler (als tekenreeks)” lijkt te bestaan,
	dus hier wordt niet naar gezocht.
]]
local function getVertalers(entiteit)
	local geformatteerdeVertalers = {}

	local vertalers = entiteit:getBestStatements('P655')
	for i, vertaler in ipairs(vertalers) do
		table.insert(geformatteerdeVertalers, formatteerAuteur{vertaler})
	end

	return geformatteerdeVertalers
end

--[[ 
	Gegeven een eerder opgehaalde wikidata-entiteit, geeft deze functie een
	tabel met een "titel" en mogelijk een "ondertitel".
]]
local function getTitel(entiteit)
	local titelObject = {}
	
	local siteLink = entiteit:getSitelink()

	local titel = entiteit:getBestStatements('P1476')
	if titel[1] then
		local titelSnak = titel[1].mainsnak -- we pakken de eerste, als er meerdere waardes zijn.
		local titelTekst = mw.wikibase.formatValue(titelSnak)
		
		if siteLink then
			titelObject.titel = '[[:' .. siteLink .. '|' .. titelTekst .. ']]'
		else
			titelObject.titel = titelTekst
		end
	end
	
	local ondertitel = entiteit:getBestStatements('P1680')
	if ondertitel[1] then
		local ondertitelSnak = ondertitel[1].mainsnak -- opnieuw de eerste
		titelObject.ondertitel = mw.wikibase.formatValue(ondertitelSnak)
	end

	return titelObject
end

--[[
	Deze functie geeft informatie over de druk, in het volgende formaat:
	{
		"jaar":     "…", (als string omdat het ook iets als “19e eeuw” of “jaren 1920” kan zijn)
		"uitgever": "…",
		"plaats":   "…",
		"druk": {
			"nummer": "…",
			"schrijfwijze": "…"
		}
	}
	
	De velden die niet beschikbaar zijn, worden weggelaten.
]]
local function getDruk(entiteit)
	local drukInfo = {}

	-- datum van uitgave
	local datums = entiteit:getBestStatements('P577') -- ‘data’ is een mooier woord, maar ‘datums’ is duidelijker
	if datums[1] then -- als er tenminste één datum is
		-- kiezen de datum met de hoogste precisie:
		local maxPrecisie = -1 -- 0 is de laagste precisie die het systeem ondersteunt
		local datum
		for k, v in ipairs(datums) do
			if v.mainsnak.snaktype == 'value' then
				local datum_element = v.mainsnak.datavalue.value
				if datum_element.precision > maxPrecisie then
					maxPrecisie = datum_element.precision
					datum = datum_element['time']
				end
			end
		end
		
		if maxPrecisie >= 7 then -- minstens precisie ‘eeuw’
			-- we geven (voor nu) alleen om het jaar.
			string.gsub(datum, '^([+-])(%d+)', function(sign, jaar)
				--[[
				het jaar wordt altijd naar minstens 4 getallen gepad, maar in de
				toekomst kan “de 19e eeuw” als “+18” worden weergegeven. Dus we
				voegen nullen aan het _eind_ toe als het jaar minder dan vier
				tekens lang is.
				]]
				jaar = string.gsub(jaar, '^(%d%d%d)$', '%10')
				jaar = string.gsub(jaar, '^(%d%d)$', '%100')
				jaar = string.gsub(jaar, '^(%d)$', '%1000') -- lelijke manier maar who cares, ik ken lua nog maar net


				if maxPrecisie == 7 then -- eeuw
					local eeuw = string.sub(jaar, 1, 2)
					eeuw = tonumber(eeuw) + 1
					eeuw = tostring(eeuw)
					jaar = eeuw .. 'e eeuw' -- bijv. 18e eeuw
				elseif maxPrecisie == 8 then -- decennium
					local decennium = string.sub(jaar, 1, 3) .. '0'
					jaar = 'jaren ' .. decennium
				elseif maxPrecisie >= 9 then -- jaar of beter
					-- niks, jaar is al goed
				else
					error('Daar gaat iets goed fout') -- hier kan je niet komen ^^
				end
				
				if sign == '-' then -- vóór christus
					jaar = jaar .. ' v. Chr.'
				end
				
				drukInfo['jaar'] = jaar
			end)
		end
	end
	
	
	local uitgevers = entiteit:getBestStatements('P123')
	if uitgevers[1] then
		drukInfo['uitgever'] = formatteerAuteur{uitgevers[1], genereerLink=false}
	end
	
	local plaatsen = entiteit:getBestStatements('P291')
	if plaatsen[1] then
		drukInfo['plaats'] = formatteerAuteur{plaatsen[1], genereerLink=false}
	end
	
	local druk = entiteit:getBestStatements('P393')
	if druk[1] and druk[1].mainsnak.snaktype == 'value' then
		drukInfo.druk = {}
		drukInfo.druk.nummer = druk[1].mainsnak.datavalue.value
		
		if druk[1].qualifiers and druk[1].qualifiers['P1932'] then
			local schrijfwijze = mw.wikibase.formatValue(druk[1].qualifiers['P1932'][1])
			drukInfo.druk.schrijfwijze = schrijfwijze
		end
	end
	
	local taal = entiteit:getBestStatements('P407')
	if taal[1] and taal[1].mainsnak.snaktype == 'value' and taal[1].mainsnak.datavalue.value.id ~= 'Q7411' then -- deze druk is niet Nederlands!
		local geformatteerdeTalen = {}
		for k, v in ipairs(taal) do
			table.insert(geformatteerdeTalen, formatteerAuteur{v, genereerLink=false})
		end
		drukInfo.taal = table.concat(geformatteerdeTalen, ', ')
	end
	
	return drukInfo
end

local function vindEersteDruk(entiteit, drukInfo)
	--[[
		als dit de eerste druk is, zijn we klaar. we doen twee checks:
		 - is het editienummer "1"?
		 - is dit werk een "eerste druk"/Q10898227?
	]]
	local dit_is_eerste_druk = false
	local dit_is_vertaling = false

	if drukInfo.druk and drukInfo.druk.nummer == "1" then
		dit_is_eerste_druk = true
	else
		local entiteit_type = entiteit:getBestStatements('P31')
		if entiteit_type[1] then
			local entiteit_type = entiteit_type[1]
			if entiteit_type.mainsnak.snaktype == 'value'
				and entiteit_type.mainsnak.datavalue.value.id == 'Q10898227' -- eerste druk
				then

				dit_is_eerste_druk = true
			end
		end
	end
	
	-- om zeker te weten dat er geen eerste druk is waar we naar willen refereren,
	-- moeten we ook vaststellen dat dit werk in de oorspronkelijke taal is.

	local werk = entiteit:getBestStatements('P629')
	if not werk[1] or werk[1].mainsnak.snaktype ~= 'value' then
		-- geen werk gevonden waar dit aan verbonden is
		return nil
	end
	werk = werk[1] -- het werk waar deze editie aan verbonden is
	local werk_id = werk.mainsnak.datavalue.value.id -- het wikidata ID van het werk zelf
	
	-- vind alle talen waar het oorspronkelijke werk in is geschreven
	local werk_talen_statements = mw.wikibase.getBestStatements(werk_id, 'P407')
	local werk_talen = {}
	local taal_gevonden = false
	for k, v in ipairs(werk_talen_statements) do
		if v.mainsnak.snaktype == 'value' then
			local taal_id = v.mainsnak.datavalue.value.id
			werk_talen[taal_id] = true
			taal_gevonden = true
		end
	end
	if not taal_gevonden then
		-- alle waardes voor werk_talen zijn dan true, aangezien we het niet weten
		setmetatable(werk_talen, {__index = function() return true end})
	end
	
	-- voor de taaldetectie doen we: als de eerste taal van deze editie niet in
	-- de lijst talen van het werk zit, is het een vertaling.
	local editie_talen_statements = entiteit:getBestStatements('P407')
	local editie_taal = editie_talen_statements[1] and editie_talen_statements[1].mainsnak.snaktype == 'value' and editie_talen_statements[1].mainsnak.datavalue.value.id
	if not werk_talen[editie_taal] then
		dit_is_vertaling = true
	end
	
	
	if dit_is_eerste_druk and not dit_is_vertaling then
		-- klaar
		return nil
	end

	-- hierna returnen we “werk_id” als we geen eerste druk vinden, in de hoop
	-- in ieder geval het oorspronkelijke jaar van uitgave te achterhalen.

	-- eerst het jaar vinden
	local werk_jaar_statements = mw.wikibase.getBestStatements(werk_id, 'P577')
	local werk_jaar = nil
	for k, v in ipairs(werk_jaar_statements) do
		if v.mainsnak.snaktype == 'value' then
			local waarde = v.mainsnak.datavalue.value
			if waarde.precision >= 9 then -- minstens op het jaar
				werk_jaar = waarde['time']
				werk_jaar = string.gsub(werk_jaar, '^([+-]%d+).*$', '%1')
				break
			end
		end
	end

	
	-- nu de edities vinden:
	local edities = mw.wikibase.getAllStatements(werk_id, 'P747')
	local mogelijke_eerste_drukken = {}
	
	for k, v in ipairs(edities) do
		--[[
			om te kwalificeren moeten ze (qua qualifiers) minstens
				- een editienummer van 1 hebben; of
				- van hetzelfde jaar zijn als het werk
			en _niet_:
				- in een andere taal zijn dan het werk
				- een ander editienummer dan 1 hebben
				- van een ander jaar zijn dan het werk
		]]--
		if not v.qualifiers or v.mainsnak.snaktype ~= 'value' then
			-- als er niet genoeg informatie is (voor selectie danwel gebruik resp.) selecteren we het niet als eerste druk
		else
			-- positieve selectie onthouden voor als er meerdere kandidaten zijn
			-- (negatieve selectie worden het sowieso niet, dus geen extra informatie nodig)
			local positieve_selectie = { jaar_gelijk = false, editienummer_een = false }
			local negatieve_selectie = false

			-- Taal checken: (we doen 'als de eerste taal in de qualifiers buiten de groep talen van het werk valt, is het geen eerste druk')
			if v.qualifiers['P407'] and v.qualifiers['P407'][1].snaktype == 'value' then
				local taal = v.qualifiers['P407'][1].datavalue.value.id
				if not werk_talen[taal] then
					negatieve_selectie = true
				end
			end
			
			if not negatieve_selectie then -- we zijn nog in de running

				-- Jaar checken:
				if v.qualifiers['P577'] and v.qualifiers['P577'][1].snaktype == 'value' then
					local jaar = v.qualifiers['P577'][1].datavalue.value['time']
					local jaar = string.gsub(jaar, '([+-]%d+).*$', '%1')
					if jaar == werk_jaar then
						positieve_selectie.jaar_gelijk = true
					else
						negatieve_selectie = true
					end
				end
				
				if not negatieve_selectie then -- ik zou best een 'continue' statement willen
					-- Editienummer checken
					if v.qualifiers['P393'] and v.qualifiers['P393'][1].snaktype == 'value' then
						local editienummer = v.qualifiers['P393'][1].datavalue.value
						if editienummer == '1' then
							positieve_selectie.editienummer_een = true
						else
							negatieve_selectie = true
						end
					end
				end
			end
			
			if (positieve_selectie.jaar_gelijk or positieve_selectie.editienummer_een) and not negatieve_selectie then
				-- deze wordt toegevoegd aan kandidaten
				-- van positieve selectie maken we een string als key
				local key = ''
				if positieve_selectie.jaar_gelijk then
					key = key .. 'jaar'
				end
				if positieve_selectie.editienummer_een then
					key = key .. 'editie1'
				end

				mogelijke_eerste_drukken[key] = v.mainsnak.datavalue.value.id
			end
		end
	end
	
	-- jaar gelijk en editienummer goed verdient de voorkeur, daarna editienummer goed en geen jaar, en daarna geen editienummer maar jaar goed
	if mogelijke_eerste_drukken['jaareditie1'] then
		return mogelijke_eerste_drukken['jaareditie1']
	elseif mogelijke_eerste_drukken['editie1'] then
		return mogelijke_eerste_drukken['editie1']
	elseif mogelijke_eerste_drukken['jaar'] then
		return mogelijke_eerste_drukken['jaar']
	else
		return werk_id
	end
end

--[=[
	Volledige informatie, in de volgende vorm, maar van geen van de velden
	mag worden aangenomen dat deze beschikbaar zal zijn:

	{
		"auteurs": {
			1: "[[Auteur:Naam|Naam]]",
			2: "Auteur Zonder Wikisourcepagina"
		},
		"vertalers": {
			1: "[[Auteur:Naam|Naam]]"
		}
		"titel": {
			"titel": "Hoofdtitel",
			"ondertitel": "Ondertitel",
		},
		"drukInfo": {
			"druk": {
				"nummer": "3",
				"schrijfwijze": "Derde druk"
			},
			"jaar": "1921",
			"plaats": "Amsterdam",
			"uitgever": "Uitgever"
		},
		"eersteDrukInfo": <zelfde opmaak als drukInfo>
	}
]=]
function p.vraagWikidata(wikidata_id)
	local return_data = {}

	local entiteit = mw.wikibase.getEntity(wikidata_id)
	
	return_data.auteurs   = getAuteurs(entiteit)
	return_data.vertalers = getVertalers(entiteit)
	return_data.titel     = getTitel(entiteit)
	return_data.drukInfo  = getDruk(entiteit)
	
	-- info over eerste druk
	local eerste_druk_id = vindEersteDruk(entiteit, return_data.drukInfo)
	if eerste_druk_id then
		local eerste_druk_entiteit = mw.wikibase.getEntity(eerste_druk_id)
		return_data.eersteDrukInfo = getDruk(eerste_druk_entiteit)
	else
		return_data.eersteDrukInfo = {}
	end
	
	return return_data
end

return p