Přeskočit na obsah

Modul:Wikidata/Filterers

Tato stránka je zamčena
Z Wikipedie, otevřené encyklopedie

require "Modul:No globals"

local p = {}

local lib = require 'Modul:Wikidata/lib'

local function checkLimit(array, limit)
	local limit = limit and tonumber(limit)
	if limit then
		return #array >= limit
	end
	return true
end

local function applyLimit(array, limit)
	local limit = limit and tonumber(limit)
	while limit and #array > limit do
		table.remove(array)
	end
end

local function IsInLanguage(snak, lang)
	if snak.datatype ~= 'monolingualtext' then
		return error(lib.raiseInvalidDatatype('IsInLanguage', snak.datatype, 'monolingualtext'))
	else
		return lib.IsSnakValue(snak) and snak.datavalue.value.language == lang
	end
end

local function filterBySnaktype(statement)
	return lib.IsSnakValue(statement.mainsnak)
end

local function hasSnakTarget(snak, target)
	local Formatters = require 'Modul:Wikidata/Formatters'
	return Formatters.getRawValue(snak) == target
end

local function filterHasTarget(statement, target)
	return hasSnakTarget(statement.mainsnak, target)
end

local function filterHasQualifier(statement, prop, value)
	if statement.qualifiers then
		prop = prop:upper()
		for _, snak in ipairs(statement.qualifiers[prop] or {}) do
			if not value or hasSnakTarget(snak, value) then
				return true
			end
		end
	end
	return false
end

local function filterHasRanks(statement, ranks)
	for _, rank in ipairs(ranks) do
		if statement.rank == rank then
			return true
		end
	end
	return false
end

local function filterHasReferences(statement, options)
	if statement.references then
		if #p.filterReferences(statement.references, options) > 0 then
			return true
		end
	end
	return false
end

local function filterItemHasLabel(statement)
	local datatype = statement.mainsnak.datatype
	if datatype ~= 'wikibase-item' and datatype ~= 'wikibase-property' then
		return error(lib.raiseInvalidDatatype('filterItemHasLabel', datatype, { 'wikibase-item', 'wikibase-property' }))
	end
	if lib.IsSnakValue(statement.mainsnak) then
		local langs = { 'cs', 'en', 'sk' }
		local Formatters = require 'Modul:Wikidata/Formatters'
		if lib.getLabelInLanguage(Formatters.getRawValue(statement.mainsnak), langs) then
			return true
		end
	end
	return false
end

local function filterItemIsInstance(statement, options)
	local datatype = statement.mainsnak.datatype
	if datatype ~= 'wikibase-item' and datatype ~= 'wikibase-property' then
		return error(lib.raiseInvalidDatatype('filterItemIsInstance', datatype, { 'wikibase-item', 'wikibase-property' }))
	end
	local Module = require 'Modul:Wikidata/Tree'
	if lib.IsSnakValue(statement.mainsnak) then
		local item = lib.getEntityIdFromValue(statement.mainsnak.datavalue.value)
		if Module.IsInstance(item, options) then
			return true
		end
	end
	return false
end

local function filterMainsnakInLanguage(statement, options)
	return IsInLanguage(statement.mainsnak, options.withlang)
end

local function filter(array, callback, ...)
	local i = #array
	while i > 0 do
		if not callback(array[i], ...) then
			table.remove(array, i)
		end
		i = i - 1
	end
end

local function getValuesFromQualifiers(qualifiers)
	local Values = {}
	local Formatters = require 'Modul:Wikidata/Formatters'
	for key, array in pairs(lib.props) do
		for _, prop in ipairs(array) do
			for _, snak in ipairs(qualifiers[prop] or {}) do
				if lib.IsSnakValue(snak) then
					Values[key] = Formatters.getRawValue(snak)
					break
				end
			end
		end
	end
	return Values
end

function p.filterStatementsFromEntity(entity, options)
	if not options.property or options.property == '' then
		return error(lib.formatError('param-not-provided', 'property'))
	end
	if not entity or not entity.claims then
		return {}
	end
	local property = mw.ustring.upper(options.property)
	local statements = mw.clone(entity.claims[property])
	if not statements then
		return {}
	end

	p.filterStatements(statements, options)
	return statements
end

function p.filterStatements(statements, options)
	local options = lib.common.cleanArgs(options)
	-- apply filter by rank
	local rank = options.rank or "valid"
	if rank ~= "all" then
		if rank == "valid" or rank == "best" then
			filter(statements, filterHasRanks, { "normal", "preferred" })
			if rank == "best" and #statements > 0 then
				for _, statement in ipairs(statements) do
					if statement.rank == "preferred" then
						filter(statements, filterHasRanks, { "preferred" })
						break
					end
				end
			end
		else
			filter(statements, filterHasRanks, { rank })
		end
		if #statements == 0 then return end
	end
	-- apply filter by source
	if options.ref then
		filter(statements, filterHasReferences, options)
		if #statements == 0 then return end
	end
	-- apply filter by snak type
	if not lib.IsOptionTrue(options, 'showspecial') then
		filter(statements, filterBySnaktype)
		if #statements == 0 then return end
	end
	-- apply filter by target value
	if options.withtarget then
		filter(statements, filterHasTarget, options.withtarget)
		if #statements == 0 then return end
	end
	-- apply filter by qualifier property
	if options.withqualifier then
		filter(statements, filterHasQualifier, options.withqualifier, options.withqualifiervalue)
		if #statements == 0 then return end
	end
	-- apply filter by language
	if options.withlang then
		filter(statements, filterMainsnakInLanguage, options)
		if #statements == 0 then return end
	end
	-- apply filter by time
	if options.date then
		local date
		local Time = require 'Modul:Time'
		if type(options.date) == 'table' then
			date = options.date
		elseif options.date == '#now' then
			date = Time.new(os.date('!*t'))
		--elseif mw.ustring.find(options.date, '^[Pp][1-9]%d-$') then TODO
		else
			date = Time.newFromIso8601(options.date)
		end
		if not date then
			return error(lib.formatError('invalid-date', tostring(options.date)))
		end

		local Date = require 'Modul:Wikidata/datum'
		local oldStatements = mw.clone(statements)
		while #statements > 0 do table.remove(statements) end
		for _, statement in ipairs(oldStatements) do
			local Values = getValuesFromQualifiers(statement.qualifiers or {})
			if Values.point then
				local strict = false
				if Date.AreDatesSame(date, Values.point, strict) then
					filter(statements, function (st)
						local val = getValuesFromQualifiers(st.qualifiers).point
						if val then
							return Date.AreDatesSame(val, Values.point, strict)
						end
						return true
					end)
					table.insert(statements, statement)
				elseif Date.IsSecondLaterThanFirst(Values.point, date) then
					if #statements == 0 then
						table.insert(statements, statement)
					else
						local same, ins
						for _, st in ipairs(statements) do
							local val = getValuesFromQualifiers(st.qualifiers).point
							if val then
								if Date.AreDatesSame(date, Values.point, strict) then
									same = true
									break
								end
								if (Date.AreDatesSame(val, Values.point, strict) or
									Date.IsSecondLaterThanFirst(val, Values.point)) then
									ins = true
								end
							end
						end
						if ins and not same then
							filter(statements, function (st)
								local val = getValuesFromQualifiers(st.qualifiers).point
								if val then
									return Date.AreDatesSame(val, Values.point, strict)
								end
								return true
							end)
							table.insert(statements, statement)
						end
					end
				end
			else
				if Values.begin then
					if Date.IsSecondLaterThanFirst(Values.begin, date) then
						if not Values.ending then
							table.insert(statements, statement)
						elseif Date.IsSecondLaterThanFirst(date, Values.ending) then
							table.insert(statements, statement)
						end
					end
				elseif Values.ending then
					if Date.IsSecondLaterThanFirst(date, Values.ending) then
						if not Values.begin then
							table.insert(statements, statement)
						elseif Date.IsSecondLaterThanFirst(Values.begin, date) then
							table.insert(statements, statement)
						end
					end
				end
			end
		end
		if #statements == 0 then return end
	end
	if lib.IsOptionTrue(options, 'withlabel') then
		filter(statements, filterItemHasLabel)
		if #statements == 0 then return end
	end
	-- apply filter by class
	if options.instance then
		filter(statements, filterItemIsInstance, options)
		if #statements == 0 then return end
	end
	-- sort statements if needed
	if options.sort then -- patří to sem?
		local Sorters = require 'Modul:Wikidata/Sorters'
		Sorters.sortStatements(statements, options)
	end
	-- apply filter by limit
	applyLimit(statements, options.limit)
end

function p.filterQualifiers(qualifiers, options)
	local options = lib.common.cleanArgs(options)
	local Qualifiers, oldQualifiers = qualifiers, {}
	if options['qualifiers withlang'] then
		oldQualifiers, Qualifiers = Qualifiers, {}
		for _, snak in ipairs(oldQualifiers) do
			if IsInLanguage(snak, options['qualifiers withlang']) then
				table.insert(Qualifiers, snak)
			end
		end
		if #Qualifiers == 0 then return {} end
	end
	if options['qualifiers class'] then
		local datatype = Qualifiers[math.random(#Qualifiers)].datatype
		if datatype == 'wikibase-item' or datatype == 'wikibase-property' then
			oldQualifiers, Qualifiers = Qualifiers, {}
			local Module = require 'Modul:Wikidata/Tree'
			for _, snak in ipairs(oldQualifiers) do
				if lib.IsSnakValue(snak) then
					local item = lib.getEntityIdFromValue(snak.datavalue.value)
					if Module.IsInTree(item, options['qualifiers class'], 'P279', 20, {}) then
						table.insert(Qualifiers, snak)
					end
				end
			end
			if #Qualifiers == 0 then return {} end
		else
			return error(lib.raiseInvalidDatatype('inClass', datatype, { 'wikibase-item', 'wikibase-property' }))
		end
	end
	--[[
	if options['sort qualifiers'] then
		local Sorters = require "Modul:Wikidata/Sorters"
		Sorters.sortQualifiers(Qualifiers, options) --fixme: undefined!
	end ]]--
	applyLimit(Qualifiers, options['qualifiers limit'])
	-- @deprecated
	return Qualifiers
end

function p.filterReferences(references, options)
	local options = lib.common.cleanArgs(options)
	if options.ref == '#any' then
		-- @deprecated
		return references
	end

	local oldReferences, References = references, {}
	if options.ref == 'valid' then
		local map = (require 'Modul:Wikidata/cite').props
		for _, ref in ipairs(oldReferences) do
			for _, props in pairs(map) do
				for _, prop in ipairs(props) do
					if ref.snaks[prop] then
						table.insert(References, ref)
					end
				end
			end
		end
	end

	if options.min_ref and not checkLimit(References, options.min_ref) then
		return {}
	end
	-- @deprecated
	return References
end

return p