Module:Kinship

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

This module shall replace Template:Kinship somewhen and allow automatic data retrieval from Wikidata. Some data useful for mapping might be found at Template:Kinship/Wikidata mapping. Some testcases can be found at Module talk:Kinship/testcases, the syntax there is still work in progress.

Currently {{#invoke:Kinship|kinship|item=Q6530}} gives:
kinship of Henryk Hescheles, Marian Hemar and Janina Altman, parent of Tomasz Lem, single person of Barbara Lem, mother of Samuel Lem

Code

--[[

Code by Marsupium is also under CC-0, copy-paste it wherever you like!

Currently no support for:
* proper handling of all kinds of linking
* proper handling of properties with >1 value of "inverse of (P1696)"
* possibility to chose what data to retrieve/show from Wikidata:
  how shall this be done????? always all for one kind of relation? not good
* qualifier "type of kinship (P1039)"
* genders in relation labels
* date qualifiers
* "partner" instead of "unmarried partner (P451)", should be supported?
* …

TODO:
* propose to rename the template to "Template:Relationship" etc.
]]

local wd          = require("Module:Wikidata")
local wdLabel     = require("Module:Wikidata label")
local wdGetClaims = require("Module:Wikidata/GetClaims")
local LangSwitch  = require("Module:LangSwitch")
local linguistic  = require("Module:Linguistic")
local city        = require("Module:City")._city

local p = {}
local subject = {}

-- as of [[Template:Kinship/labels]]:
-- [[Special:PermanentLink/256815792]], 2017-08-30:
local relationDictionary = {
	aunt = "Q76507",
	uncle = "Q76557",
	cousin = "Q23009870",
	brother = "Q10861465",
	brotherinlaw = "Q2914212",
	daughter = "Q308194",
	father = "Q7565",
	grandfather = "Q9238344",
	grandmother = "Q9235758",
	granddaughter = "Q19756330",
	grandson = "Q11921506",
	grandnephew = "Q4113832",
	greatgranddaughter = "Q20747487",
	greatgrandson = "Q19682170",
	halfbrother = "Q15312935",
	halfsister = "Q2920422",
	stepson = "Q4346792",
	stepdaughter = "Q19822354",
	husband = "Q212878",
	mother = "Q7560",
	nephew = "Q15224724",
	niece = "Q3403377",
	partner = "P451", -- no item yet (2017-09-06), same meaning? -> ask at talk!
	sister = "Q595094",
	sisterinlaw = "Q3238556",
	son = "Q177232",
	student = "Q48282",
	teacher = "Q37226",
	wife = "Q188830" --[[, -- more:
	spouse = "Q1196129"
]]
}


function p.oneEntity(query)
	-- get one entity value for a property of an entity
	local result = {id = nil, label = nil}
	if query.entity and query.property then
		local prop = wd.getClaims({entity=query.entity, property=query.property})
		if prop then
			result.id = prop[1]["mainsnak"]["datavalue"]["value"]["id"]
			if query.label == true and query.lang then
				result.label = wdLabel._getLabel(result.id, query.lang)
			end
		end
	end
	return result.id, result.label
end


local function XofY(lang, object, args)
	local xID = object["data"]["outID"]
	local xName = object["data"]["outName"] or ''
	local y = object["value"]
	local link = nil
	if args.link then
		y = '[[:' .. args.link .. '|' .. y .. ']]'
	elseif not args['link type'] then
		-- for backwards compatability, to be considered/discussed
		local page = mw.title.new( y, 'category' )
		if page and page.exists then
			link = 'Category:' .. y
			y = "[[:Category:" .. y .. "|" .. y .. "]]"
		end
	else
		y = city(y, lang) -- TODO: WARNING: misuse of [[Module:City]]
	end
	-- mimics [[Template:Kinship/link]]
	-- TODO: needs change of data model of subject[prop]["template"]["value"]
	--       to work with Wikidata
	-- for future features see also
	-- [[Module talk:City#Wikidata for places not in Module:City/data]]
	-- TODO: 'link type' should overwrite link

	local x = xID and wdLabel._getLabel(xID, lang, "-") or xName ..
		"[[Category:Pages using Kinship template with incorrect parameter]]"
	-- "-" means no link

	local switch = {
		de = x .. " von " .. y,
		en = x .. " of " .. y,
		fa = x .. y,
		fr = x .. " " .. linguistic.of(y, "fr"),
		-- works without raw through [[Module:Delink]], better performance without though
		-- raw could be provided if wd.formatStatements is not used
		-- and if [[Module:Match link]] provides raw link
		hu = y .. x, -- there are no prepositions in Hungarian, only various appendages, so can't added here
		it = x .. " di " .. y,
		mk = x .. " на  " .. y,
		nl = x .. " van  " .. y,
		pl = args.pl and link and x .. " " .. "[[:" .. link .. "|" .. args.pl .. "]]" or
			args.pl and x .. " " .. args.pl or
			x .. " osoby: " .. y,
		pt = x .. " de  " .. y,
		ru = args.ru and x .. " " .. args.ru or x .. " следующего лица: " .. y,
		zh = y .. "的" .. x, -- easier to add the "的" in every case
		uk = x .. " такої особи: " .. y
	}
	return LangSwitch._langSwitch(switch, lang)
end

function p._kinship(args)
	if args.item then subject["entity"] = args.item end
	local lang = args.lang
	subject["relations"] = {}
	subject["props"] = {}

	-- old template input
	if args[1] and args[2] then
		subject["props"]["template"] = {}
		subject["props"]["template"]["data"] = {}
		subject["props"]["template"]["data"]["outName"] = args[1]
		subject["props"]["template"]["data"]["outID"] = relationDictionary[string.gsub(args[1], "-", "")]
		subject["props"]["template"]["value"] = args[2]
	end

	-- Wikidata input
	local itemrelations
	local relSpec = ""
	local PIDs = {"P22", "P25", "P26", "P451", "P40", "P3373", "P1038", "P1066", "P802"}
	local QIDs = {}
	local relNames = {}
	if args["itemrelations"] then
		relSpec = ": " .. relSpec .. args["itemrelations"] .. "; "
		PIDs = {}
		local itemrelationList = mw.text.split(args["itemrelations"], ",")
		for i, relStr in pairs(itemrelationList) do
			local PID = string.match(relStr, "[Pp]%d+")
			local QID = string.match(relStr, "[Qq]%d+")
			local relName = string.match(relStr, "[%a- ]+")
			if PID then
				table.insert(PIDs, PID)
				relSpec = relSpec .. PID
			elseif QID then -- not yet implemented
				table.insert(QIDs, QID)
				relSpec = relSpec .. QID
			elseif relName then -- not yet implemented; TODO: normalize!
				table.insert(relNames, relName)
				relSpec = relSpec .. relName
			end
		end
	end
	
	
	for i, prop in pairs(PIDs) do -- TODO: sort?
		local propContent = wd.formatStatements({item=subject["entity"], property=prop,link='wikipedia',lang=lang})
		--[[ TODO: formatStatements shouldn't be used, it should be formatted later for gender distinction: ->
		-- local propStatement = wd.getClaims({entity=subject["entity"], property=prop})
		-- TO COME!!!! TODO: what does this give?
		]]
		if propContent then
			subject["props"][prop] = {}
			subject["props"][prop]["value"] = propContent
			subject["props"][prop]["data"] = {}
			
			-- symmetric property: "instance of (P31)": "symmetric property (Q18647518)"
			local propInstanceOf = wd.getClaims({entity=prop, property="P31"})
			local isSymmetric = false
			for i,j in pairs(propInstanceOf) do
				if wdGetClaims.hastargetvalue(j, "Q18647518") then
					isSymmetric = true
				end
			end
			if isSymmetric then
				subject["props"][prop]["data"]["outID"] = prop
			else
			
			-- get "opposite of (P461)" of "subject item of this property (P1629)" of the property
				local propSubjItem = p.oneEntity({entity=prop, property="P1629"})
				-- assumes there is just one "subject item of this property (P1629)"
				subject["props"][prop]["data"]["outID"] =
						p.oneEntity({entity=propSubjItem, property="P461", lang=lang})
				-- assumes there is just one "opposite of (P461)"
			
			-- if that didn't work, get "subject item of this property (P1629)" of "inverse of (P1696)" of the property
				if not subject["props"][prop]["data"]["outID"] then
					local propInverseProp = p.oneEntity({entity=prop, property="P1696"})
					-- assumes there is just one "inverse of (P1696)"
					-- FIXME: this is wrong e.g. for "child (P40), but that isn't retrieved anyway
					subject["props"][prop]["data"]["outID"] =
						p.oneEntity({entity=propInverseProp, property="P1629", lang=lang})
				end
			end
		end
	end
	
	-- build strings
	for i, j in pairs(subject["props"]) do
		local i18nStr = XofY(lang, j, args)
		local relation = i18nStr
		table.insert(subject["relations"], relation)
	end
	return table.concat(subject.relations, ", ") --.. relSpec, subject -- TODO: relSpec for testing
end

function p.kinship(frame)
	local args = {}
	for name, value in pairs(frame:getParent().args) do
		local trimmedValue = value:gsub("^%s*(.-)%s*$", "%1")
		if trimmedValue ~= "" then
			args[name] = trimmedValue
		end
	end
	for name, value in pairs(frame.args) do
		local trimmedValue = value:gsub("^%s*(.-)%s*$", "%1")
		if trimmedValue ~= "" then
			args[name] = trimmedValue
		end
	end
	
	 -- if no language is set get user's chosen language
	if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then 
		args.lang = frame:callParserFunction( "int", "lang" )
	end
	
	local results = p._kinship(args)
	return results
end

function p.test()
	local args = {}
	args[1] = "child"
	args[2] = "John Doe"
	args["item"] = "Q981558"
	args["lang"] = "de"
	local results, subject = p._kinship(args)
	return subject
end

return p