Modul:Wikidata/Filterers
Dokumentaci tohoto modulu lze vytvořit na stránce Nápověda:Modul:Wikidata/Filterers
require 'strict'
local p = {}
local lib = require 'Modul:Wikidata/lib'
local function in_array(value, array)
local TableTools = require 'Modul:TableTools'
return TableTools.inArray(array, value)
end
local function assertDatatype(actual, expected, method)
if actual ~= expected then
error(lib.raiseInvalidDatatype(method, actual, expected))
end
end
local function assertEntityDatatype(datatype, method)
if lib.datatypeToValueType[datatype] ~= 'wikibase-entityid' then
local dtypes = {}
for dt, vt in pairs(lib.datatypeToValueType) do
if vt == 'wikibase-entityid' then
table.insert(dtypes, dt)
end
end
error(lib.raiseInvalidDatatype(method, datatype, dtypes))
end
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 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 filterMainsnak(statements, callback, ...)
filter(statements, function (statement, ...)
return callback(statement.mainsnak, ...)
end, ...)
end
local function isInLanguage(snak, langs)
assertDatatype(snak.datatype, 'monolingualtext', 'isInLanguage')
local langs = lib.textToTable(langs)
return lib.IsSnakValue(snak) and in_array(snak.datavalue.value.language, langs)
end
local function hasSnaktype(snak, somevalue, novalue)
local snaktype = snak.snaktype
if snaktype == 'somevalue' then
return somevalue or false
elseif snaktype == 'novalue' then
return novalue or false
end
return lib.IsSnakValue(snak)
end
local function hasTarget(snak, target)
local Formatters = require 'Modul:Wikidata/Formatters'
return tostring(Formatters.getRawValue(snak)) == tostring(target)
end
local function hasQualifier(statement, prop, value)
if statement.qualifiers then
prop = prop:upper()
for _, snak in ipairs(statement.qualifiers[prop] or {}) do
if not value or hasTarget(snak, value) then
return true
end
end
end
return false
end
local function withoutQualifier(statement, prop, value)
return not hasQualifier(statement, prop, value)
end
local function hasRanks(statement, ranks)
return in_array(statement.rank, ranks)
end
local function hasReferences(statement, options)
if statement.references then
if #p.filterReferences(statement.references, options) > 0 then
return true
end
end
return false
end
local function hasLabel(snak)
assertEntityDatatype(snak.datatype, 'hasLabel')
if lib.IsSnakValue(snak) then
local i18n = mw.loadData('Modul:Wikidata/i18n')
local langs = mw.language.getFallbacksFor(i18n.lang)
table.insert(langs, 1, i18n.lang)
local Formatters = require 'Modul:Wikidata/Formatters'
if lib.getLabelInLanguage(Formatters.getRawValue(snak), langs) then
return true
end
end
return false
end
local function hasSitelink(statement)
assertDatatype(statement.mainsnak.datatype, 'wikibase-item', 'hasSitelink')
if lib.IsSnakValue(statement.mainsnak) then
if mw.wikibase.sitelink(Formatters.getRawValue(statement.mainsnak)) then
return true
end
end
return false
end
local function isInstance(snak, instance)
assertEntityDatatype(snak.datatype, 'isInstance')
if lib.IsSnakValue(snak) then
local Formatters = require 'Modul:Wikidata/Formatters'
local item = Formatters.getRawValue(snak)
if mw.wikibase.getReferencedEntityId(item, 'P279', lib.textToTable(instance)) then
return true
end
end
return false
end
local function hasProperty(snak, property, value)
assertEntityDatatype(snak.datatype, 'hasProperty')
if not lib.IsSnakValue(snak) then
return false
end
local Formatters = require 'Modul:Wikidata/Formatters'
local id = Formatters.getRawValue(snak)
local statements = mw.wikibase.getBestStatements(id, property:upper())
if value then
filterMainsnak(statements, hasTarget, value)
end
return #statements > 0
end
local function hasUnit(statement, unit)
assertDatatype(statement.mainsnak.datatype, 'quantity', 'hasUnit')
if lib.IsSnakValue(statement.mainsnak) then
return (lib.getItemIdFromURI(statement.mainsnak.datavalue.value.unit) or 'Q199') == unit
else
return false
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
error(lib.formatError('param-not-provided', 'property'))
end
if not entity then
return {}
end
local statements = mw.clone(entity:getAllStatements(options.property))
if #statements ~= 0 then
p.filterStatements(statements, options)
end
return statements
end
function p.filterStatements(statements, options)
-- apply filter by rank
local rank = options.rank or "valid"
if rank ~= "all" then
if rank == "valid" then
filter(statements, hasRanks, { "normal", "preferred" })
elseif rank == "best" then
local bestRank = "normal"
for _, statement in ipairs(statements) do
if statement.rank == "preferred" then
bestRank = "preferred"
break
end
end
filter(statements, hasRanks, { bestRank })
else
filter(statements, hasRanks, { rank })
end
if #statements == 0 then return end
end
-- apply filter by source
if options.ref then
filter(statements, hasReferences, options)
if #statements == 0 then return end
end
-- apply filter by snak type
filterMainsnak(statements, hasSnaktype, options.somevalue and true, options.novalue and true)
if #statements == 0 then return end
-- apply filter by target value
if options.withtarget then
filterMainsnak(statements, hasTarget, options.withtarget)
if #statements == 0 then return end
end
-- apply filter by qualifier property
if options.withqualifier then
filter(statements, hasQualifier, options.withqualifier, options.withqualifiervalue)
if #statements == 0 then return end
end
if options.withoutqualifier then
filter(statements, withoutQualifier, options.withoutqualifier, options.withoutqualifiervalue)
if #statements == 0 then return end
end
-- apply filter by language
if options.withlang then
filterMainsnak(statements, isInLanguage, options.withlang)
if #statements == 0 then return end
end
-- apply filter by unit
if options.withunit then
filter(statements, hasUnit, options.withunit)
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
local osDate = os.date('!*t')
date = Time.new{
year = osDate.year,
month = osDate.month,
day = osDate.day,
precision = Time.PRECISION.DAY,
calendar = Time.CALENDAR.GREGORIAN,
}
else
date = Time.newFromIso8601(options.date)
end
if not date then
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
filterMainsnak(statements, hasLabel)
if #statements == 0 then return end
end
if lib.IsOptionTrue(options, 'withsitelink') then
filter(statements, hasSitelink)
if #statements == 0 then return end
end
if options.hasproperty then
filterMainsnak(statements, hasProperty, options.hasproperty, options.haspropertyvalue)
if #statements == 0 then return end
end
-- apply filter by class
if options.instance then
filterMainsnak(statements, isInstance, options.instance)
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)
filter(qualifiers, hasSnaktype, options.somevalue and true, options.novalue and true)
if #qualifiers == 0 then return end
if options.withlang then
filter(qualifiers, isInLanguage, options.withlang)
if #qualifiers == 0 then return end
end
if lib.IsOptionTrue(options, 'withlabel') then
filter(qualifiers, hasLabel)
if #qualifiers == 0 then return end
end
if options.instance then
filter(qualifiers, isInstance, options.instance)
if #qualifiers == 0 then return end
end
if options.sort then
local Sorters = require 'Modul:Wikidata/Sorters'
Sorters.sortQualifiers(qualifiers, options)
end
applyLimit(qualifiers, options.limit)
end
function p.filterReferences(references, 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