Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus


--[=[ SimpleDataAccess 2022-07-03
Data Management Module for simplified Access from Within Other Modules:
All functions return first values only.

Author: Vollbracht
* qualifyingValue(statement, propQual)		wikiData first qualifier value
* statementQualOrMainSnak(qualifier, property, propQual)	first value
* MainSnakValue(qualifier, property)		wikiData first main snack value
* indirectMSValue(qualifier, properties)	wikiData first main snack value
* qualifiersLineup(qualifier, property)		sequence of wikiData qualifiers
* 
]=]
	
--Module globals

local _, GeneralTime = pcall(require, "Modul:Wikidata/Time")
local Time = GeneralTime.service

local service = {}

--------------------------- local functions -----------------------------

--[[
	statements(object, property, all)
	Have calling functions work with all of:
		params to call an mw.wikibase statement fetch function,
		wikibase entity and param to call its statement fetch method
		allready fetched statement table.
		single statement and property to access qualifiers
	Functions that are known to have one properties set type only should use the
	applying statement fetch function or method directly instead!
]]
local statements = function(object, property, all)
	if type(object) == 'string' then
		if not property then return nil end
		if all then return mw.wikibase.getAllStatements(object, property) end
		return mw.wikibase.getBestStatements(object, property)
	end
	if not object then return nil end
	if object.getBestStatements then
		if not property then return nil end
		if all then return object:getAllStatements(property) end
		return object:getBestStatements(property)
	end
	if object[property] then object = object[property] end
	if object[1] then return object end
	return nil
end

--[[
	unwrap(<source>)
	object value of a given source: this function simplifies by ignoring a lot
	like language e.g.
	this function drops all object values if table and not
		* time
		* text
		* id
	enhance this function at will:
		process additional table object values as follows:
				return <return object>, 'f'
				have <return object>:format(fmtString)
			or
				return <return object>, <new type>
				enhance service.SnakValue(...) and getList(...) then as well
	parameters:
		source	mainsnak, mainsnak.datavalue, mainsnak.datavalue.value or
				qualifier item
	returns:	(value text, value time, value id, simple type value or nil) and
				type of value:
					f	if formatable
					id	if Q123... - string
					result of format(value) in all other cases
]]
local unwrap = function(source)
	if not source then return nil end
	if source.mainsnak then
		if source.mainsnak.datavalue then
		source = source.mainsnak.datavalue.value
		else mw.logObject(source) end
	elseif source.datavalue then source = source.datavalue.value
	elseif source.value then source = source.value end
	if type(source) == 'table' then
		if source.text then return source.text, 'string' end
		if source.time then return Time:new(source), 'f' end
		if source.id then return source.id, 'id' end
		return nil
	end
	return source, type(source)
end

local function get1stNamedAs(statement, fallback)
	local q = statement.qualifiers
	if not q then return fallback end
	local n = q.P1932
	if not n then n = q.P1810 end
	if not n then return fallback end
	return n[1]["datavalue"]["value"]
end

--[[
	SnakValue
	text value of a given value
	parameters:
		value		as of mainsnak.datavalue.value
		t			as of value, t = unwrap(value) if already unwrapped
		additional	(optional, variant) for use with t = 'f' or 'id'
	returns:	simple data value:
				value (if no table), value.text, or label of value.id
]]
service.SnakValue = function(value, t, additional)
	if not t then value, t = unwrap(value) end
	if not value then return '' end
	if t == 'f' then return value:format(additional) end
	if t == 'id' then
		local label = mw.wikibase.getLabel(value)
		if label then return label end
		if additional then return get1stNamedAs(additional, value) end
		return value
	end
	return value
end

local get1stStatement = function(qualifier, property, test)
	if qualifier == nil or qualifier == "" then
		mw.log('no qualifier given for statement retrival')
		return nil
	end
	local statementList = statements(qualifier, property)
	if test then
		if not statementList or #statementList == 0 then
			mw.logObject(statementList, qualifier .. '.' .. property)
			local all = nil
			if type(qualifier) == "table" then
				all = qualifier:getAllStatements(property)
				mw.logObject(all, 'all of this')
			else
				all = mw.wikibase.getAllStatements(qualifier, property)
				mw.logObject(all, 'all of ' .. qualifier)
			end
			if not all or #all == 0 then
				all = mw.wikibase.getEntity(qualifier)
				mw.logObject(all, 'all of entity: ' .. qualifier)
			end
		else mw.logObject(statementList) end
	end
	if statementList and type(statementList) == "table" then
		local result = statementList[1]
		if type(result) == "table" then
			return result
		end
	end
	if test then mw.log('no data: (' .. qualifier .. '.' .. property .. '):') end
	return nil
end

-- deprecated; use Modul:Wikidata/Time or Modul:Time instead -->
service.time = { MATCHING = Time.MATCHING, PRECISIONLEVEL = Time.PRECISIONLEVEL,
	DEFAULTFORMAT = Time.DEFAULTFORMAT, lessthan = Time.lessthan,
	format = Time.format, timify = Time.timify
}
function service.time:new(source)
	mw.log('indirect (avoid!)')
	return Time:new(source)
end
-- <--deprecated--

--[[
	getSnaks(object, <P...>, <all>)
	snak values table:
		either all main snaks of a wikidata object defined by property
		or all snaks of a statement qualifying its mainsnak by property
	parameters:
		object:		wikidata source:
						qualifier string <Q...> of a wikidata object or
						statement table as of mw.wikibase.getBestStatements or
						single statement beeing source for qualifying snaks
		property:	defining source statement qualifier string <P...> for
						getBestStatements or
						filtering qualifying snaks
					ignored if source object is table {[1]=..., [2]= ..., ...}
		all:		use getAllStatements instead of getBestStatements
					ignored if source object is table
]]
service.getSnaks = function(object, property, all)
	object = statements(object, property, all)
	if not object then return nil end
	if not object[1] then return nil end
	local result = {}
	for _, v in ipairs(object) do
		local r = unwrap(v)
		if r then table.insert(result, r) end
	end
	return result
end

--[[
	SnakList(<object>, <property>, <all>, <value type>, <separator string>)
	statement mainsnak values comma or separator string separated
	parameters:
		object:		wikidata source:
						qualifier string <Q...> of a wikidata object or
						statement table as of mw.wikibase.getBestStatements or
						single statement beeing source for qualifying snaks
		property:	defining source statement qualifier string <P...> for
						getBestStatements or
						filtering qualifying snaks
					ignored if source object is table {[1]=..., [2]= ..., ...}
		all:		use getAllStatements instead of getBestStatements
					ignored if source object is table
					optional; default: false
		vType:		additional process information
					mandantory for objects without toString() method
					optional otherwise; defaults to 'string'
					recomended 'id' for IDs ('Q123...') to process as labels
		sep:		separator string
					optional; default: ', '
	returns:	string
]]
service.SnakList = function(object, property, all, vType, fmtString, sep)
	if not sep then sep = ', ' end
	if not vType then
		local snaks = service.getSnaks(object, property, all)
		if snaks then return table.concat(snaks, sep) end
		return ''
	end
	local result = {}
	local source = statements(object, property, all)
	if not source then return '' end
	if vType == 'id' then
		for _, v in ipairs(source) do
			local label = service.SnakValue(v, nil, v)
			if label then table.insert(result,label) end
		end
	elseif vType == 'f' then
		for _, v in ipairs(source) do
			local text = service.SnakValue(v, nil, fmtString)
			if text then table.insert(result,text) end
		end
	else
		for _, v in ipairs(source) do
			local text, t = unwrap(v)
			if text then table.insert(result,text) end
		end
	end
	return table.concat(result, sep)
end

--[[
	namedAsList(<object>, <property>, <all>, <separator string>)
	Variant to SnakList handling IDs only:
		here "named as" is preferred and "label" (or ID) are fallback
	parameters:
		object:		wikidata source:
						qualifier string <Q...> of a wikidata object or
						statement table as of mw.wikibase.getBestStatements or
						single statement beeing source for qualifying snaks
		property:	defining source statement qualifier string <P...> for
						getBestStatements or
						filtering qualifying snaks
					ignored if source object is table {[1]=..., [2]= ..., ...}
		all:		use getAllStatements instead of getBestStatements
					ignored if source object is table
					optional; default: false
		vType		optional; return result as (comma) separated list instead of
					table
		sep:		separator string
					optional; default: ', '
	returns:	string
]]
service.namedAsList = function(object, property, all, vType, fmtString, sep)
	local result = {}
	local source = statements(object, property, all)
	if not source then if sep then return '' else return {} end end
	if not vType then
		for _, v in ipairs(source) do
			local value = get1stNamedAs(v)
			if not value then value = service.SnakValue(v) end
			if value then table.insert(result, value) end
		end
	elseif vType == 'id' then
		for _, v in ipairs(source) do
			local id, t = unwrap(v)
			local link = mw.wikibase.getSitelink(id)
			local name = get1stNamedAs(v)
			if not name then name = service.SnakValue(id, t) end
			if name then
				if link then
					if link == name then
						table.insert(result, '[[' .. name .. ']]')
					else
						table.insert(result, '[[' .. link .. '|' .. name .. ']]')
					end
				else
					table.insert(result, name)
				end
			else
				if link then table.insert(result, '[[' .. link .. ']]') end
			end
		end
	else
		mw.log('no namedAsList handling defined for ' .. vType)
	end
	if sep then return table.concat(result, sep) end
	return result
end

--[[
	WD:qualifiedSnaks(Object, property, {{}})
	limit a list of statements
	parameters:
		target	either a table containing a list of statements
				or an object name (Q1234, e.g.)
		claim	name of a property to get a statement list if target is name
		qualificators	struct of elements by which list of statements may be
						qualified
						currently knowing qualificators.time only:
						if given only those statements are returned that share
						the same time (not before start = P580 e.g.)
]]
service.qualifiedSnaks = function(this, target, claim, qualificators)
	condInclude = function(statement)
		if not statement then return nil, false end
		if not statement.qualifiers then
			return statement.datavalue.value, false
		end
	end
	target = statements(target, claim)
	if type(target) ~= 'table' then return nil end
	if qualificators then
		if qualificators.time then
			return Time.filtered(target, qualificators.time)
		end
	end
	return target
end

--[[
	qualifyingValue(statement, propQual)
	simplified view on data
	Parameters:
		statement:	statement given for an entity
		propQual:	qualifier for a statement value, defaults to MainSnack
	returns: a string value of MainSnack or property qualifier if available
]]
service.qualifyingValue = function(	statement, propQual, firstOnly, vType,
									fmtString, sep)
	if not statement then return "" end
	if not propQual then return "" end
	if not statement.qualifiers then return "" end
	local snaks = statement.qualifiers[propQual]
	if firstOnly then return service.SnakValue(snaks[1]) end
	return service.SnakList(snaks, nil, nil, vType, fmtString, sep)
end

--[[
	statementQualOrMainSnak
	simple value of a statement with priority on qualifying value and fall back
	to main snak value
	parameters:
		qualifier:	string of Q[0-9]+ type for an entity with statements
		property:	string of P[0-9]+ type for usage of first statement
		propQual:	string of P[0-9]+ type for qualifying this statement
	returns:	simple value (no table)
]]
service.statementQualOrMainSnak = function(qualifier, property, propQual, test)
	mw.log('Avoid deprecated statementQualOrMainSnak')
	local statement = get1stStatement(qualifier, property, test)
	if not statement then return "" end
	local result = ''
	local q = statement["qualifiers"]
	if q and type(q[propQual]) == "table" then
		result = service.SnakValue(q[propQual][1]["datavalue"]["value"])
	end
	if result == '' then
		return service.SnakValue(statement["mainsnak"]["datavalue"]["value"])
	end
	return result
end
service.statementQualOrMainSnack = service.statementQualOrMainSnak

--[[
	MainSnakValue(qualifier, property)
	simplified view on data
	Parameters:
		qualifier:	case 1: wikiData qualifier of an element with a property
					case 2: wikiData element with a property
		property:	Property of the element having result as value
	returns:	a wikiData qualifier or a string value of MainSnack if available
				limited: no regard of anything but first statement
				limited: only label if id
				limited: no regard of text language if text
]]
service.MainSnakValue = function(qualifier, property, fmtString)
	local s = get1stStatement(qualifier, property)
	if not s then return "" end
	local ms = s.mainsnak
	if not ms then return '' end
	local sdv, t = unwrap(ms)
	if t == 'f' then
		if not fmtString then return sdv
		else return service.SnakValue(sdv, 'f', fmtString) end
	elseif t == 'id' then return service.SnakValue(sdv, 'id', s) end
	return service.SnakValue(sdv, t)
end
service.MainSnackValue = service.MainSnakValue

--[[
	indirectMSValue(qualifier, properties)
	Parameters:
		qualifier:	case 1: wikiData qualifier of an element with a property
					case 2: wikiData element with a property
		properties:	sequence of properties in a string, optionally separated
	returns:
		first MainSnackValue of last property of element given in seccond last
		property ... of element given in first property of given element
]]
service.indirectMSValue = function(qualifier, properties, fmtString)
	if not properties then return '' end
	if qualifier == nil or qualifier == "" then return "" end
	local props = {}
	for prop in properties:gmatch('[pP]%d+') do
		table.insert(props, prop)
	end
	if #props == 0 then return "" end
	local statementList = statements(qualifier, props[1])
	local t = ''
	local qual = qualifier
	local result = ""
	local i = 1
	-- process all but last properties
	while i < #props do
		if not statementList then return '' end
		if not statementList[1] then return '' end
		result, t = unwrap(statementList[1].mainsnak)
		if t ~= 'id' then return '' end
		qual = result
		-- mw.log('next for ' .. mw.wikibase.getLabel(qual))
		i = i + 1
		statementList = mw.wikibase.getBestStatements(qual, props[i])
	end
	-- process last property
	if not statementList then return '' end
	if not statementList[1] then return "" end
	local ms = statementList[1].mainsnak
	local sdv, t = unwrap(ms)
	if t == 'f' then return sdv:format(fmtString) end
	if t == 'id' then
		local label = mw.wikibase.getLabel(sdv)
		if label then return label end
		return get1stNamedAs(statementList[1], sdv)
	end
	return sdv
end

--[[
	qualifiersLineup(qualifier, property)
	sequence of wikiData qualifiers
	Parameters:
		qualifier:	case 1: wikiData qualifier of an element with a property
					case 2: wikiData element with a property
		property:	Property of the element having result qualifiers as values
	returns:
		best statement's values as long as they are wikiData qualifiers lined up
		in a table with respect on series ordinals if available
	constraint:
		It's in the responsibility of wikiData to provide correct data. In case
		of corruption this function might return an empty table or one that
		apears empty by starting with index ~= 1.
]]
service.qualifiersLineup = function(qualifier, property)
	local statementList = statements(qualifier, property)
	if not statementList then return {} end
	local result = {}
	for i, elm in ipairs(statementList) do
		local eQual, t = unwrap(elm.mainsnak)
		if t ~= 'id' then return result end
		local iQual = elm.qualifiers
		if not iQual then
			mw.log('kein qualifier in ' .. eQual)
			table.insert(result, eQual)
		else
			iQual = iQual.P1545
			if not iQual then
				mw.log('keine Ornungsnummer in ' .. eQual)
				table.insert(result, eQual)
			else
				local eNum = iQual[1]["datavalue"]["value"]
				table.insert(result, eNum, eQual)
			end
		end
	end
	return result
end

service.test = function(frame)
	return service.MainSnackValue('Q115122668', 'P577', 2)
end

return service