Moduuli:Cs:Wikidata/Filterers

Wikipediasta
Siirry navigaatioon Siirry hakuun

require "Module:No globals"

local p = {}

local lib = require 'Module:cs:Wikidata/lib'

local function in_array(value, array)
	for _, val in ipairs(array) do
		if val == value then
			return true
		end
	end
	return false
end

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, langs)
	local langs = lib.textToTable(langs)
	if snak.datatype ~= 'monolingualtext' then
		return error(lib.raiseInvalidDatatype('IsInLanguage', snak.datatype, 'monolingualtext'))
	else
		return lib.IsSnakValue(snak) and in_array(snak.datavalue.value.language, langs)
	end
end

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

local function hasSnakTarget(snak, target)
	local Formatters = require 'Module:cs: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)
	return in_array(statement.rank, ranks)
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 'Module:cs: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
	if lib.IsSnakValue(statement.mainsnak) then
		local Module = require 'Module:cs:Wikidata/Tree'
		local Formatters = require 'Module:cs:Wikidata/Formatters'
		local item = Formatters.getRawValue(statement.mainsnak)
		if Module.IsInstance(item, options) then
			return true
		end
	end
	return false
end

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

local function filterMainsnakHasUnit(statement, unit)
	local datatype = statement.mainsnak.datatype
	if datatype ~= 'quantity' then
		return error(lib.raiseInvalidDatatype('filterItemIsInstance', datatype, { 'quantity' }))
	end
	return lib.IsSnakValue(statement.mainsnak) and lib.getItemIdFromURI(statement.mainsnak.datavalue.value.unit) == unit
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 'Module:cs: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.withlang)
		if #statements == 0 then return end
	end
	-- apply filter by unit
	if options.withunit then
		filter(statements, filterMainsnakHasUnit, options.withunit)
		if #statements == 0 then return end
	end
	-- apply filter by time
	if options.date then
		local date
		local Time = require 'Module:cs:Time'
		if type(options.date) == 'table' then
			date = options.date
		elseif options.date == '#now' then
			date = Time.new(os.date('!*t'))
		else
			date = Time.newFromIso8601(options.date)
		end
		if not date then
			return error(lib.formatError('invalid-date', tostring(options.date)))
		end

		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
				if date == Values.point then
					filter(statements, function(st)
						local val = getValuesFromQualifiers(st.qualifiers).point
						if val then
							return val == Values.point
						end
						return true
					end)
					table.insert(statements, statement)
				elseif 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 == Values.point then
									same = true
									break
								end
								if val == Values.point or 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
								return not val or val == Values.point
							end)
							table.insert(statements, statement)
						end
					end
				end
			else
				if Values.begin then
					if Values.begin < date then
						if not Values.ending then
							table.insert(statements, statement)
						elseif date < Values.ending then
							table.insert(statements, statement)
						end
					end
				elseif Values.ending then
					if date < Values.ending then
						if not Values.begin then
							table.insert(statements, statement)
						elseif 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 'Module:cs: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)
	if options['qualifiers withlang'] then
		filter(qualifiers, IsInLanguage, options['qualifiers withlang'])
		if #qualifiers == 0 then return end
	end
	if options['qualifiers class'] then
		--[[ todo
		local datatype = Qualifiers[math.random(#Qualifiers)].datatype
		if datatype == 'wikibase-item' or datatype == 'wikibase-property' then
			oldQualifiers, Qualifiers = Qualifiers, {}
			local Module = require 'Module:cs:Wikidata/Tree'
			local Formatters = require 'Module:cs:Wikidata/Formatters'
			for _, snak in ipairs(oldQualifiers) do
				if lib.IsSnakValue(snak) then
					local item = Formatters.getRawValue(snak)
					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 'Module:cs:Wikidata/Sorters'
		Sorters.sortQualifiers(qualifiers, options)
	end
	applyLimit(qualifiers, options['qualifiers limit'])
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 'Module:cs: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