Амодуль:Wikidata/Filterers
Внешний вид
require 'Амодуль:No globals'
local p = {}
local lib = require 'Амодуль:Wikidata/lib'
local function in_array(value, array)
local TableTools = require 'Амодуль:TableTools'
return TableTools.inArray(array, value)
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
error(lib.raiseInvalidDatatype('isInLanguage', snak.datatype, 'monolingualtext'))
else
return lib.IsSnakValue(snak) and in_array(snak.datavalue.value.language, langs)
end
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 'Амодуль: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)
local datatype = snak.datatype
if datatype ~= 'wikibase-item' and datatype ~= 'wikibase-property' then
error(lib.raiseInvalidDatatype('hasLabel', datatype, { 'wikibase-item', 'wikibase-property' }))
end
if lib.IsSnakValue(snak) then
local i18n = mw.loadData('Амодуль:Wikidata/i18n')
local langs = mw.language.getFallbacksFor(i18n.lang)
table.insert(langs, 1, i18n.lang)
local Formatters = require 'Амодуль:Wikidata/Formatters'
if lib.getLabelInLanguage(Formatters.getRawValue(snak), langs) then
return true
end
end
return false
end
local function hasSitelink(statement)
local datatype = statement.mainsnak.datatype
if datatype ~= 'wikibase-item' then
error(lib.raiseInvalidDatatype('hasSitelink', datatype, { 'wikibase-item' }))
end
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)
local datatype = snak.datatype
if datatype ~= 'wikibase-item' and datatype ~= 'wikibase-property' then
error(lib.raiseInvalidDatatype('isInstance', datatype, { 'wikibase-item', 'wikibase-property' }))
end
if lib.IsSnakValue(snak) then
local Formatters = require 'Амодуль: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 hasUnit(statement, unit)
local datatype = statement.mainsnak.datatype
if datatype ~= 'quantity' then
error(lib.raiseInvalidDatatype('hasUnit', datatype, { 'quantity' }))
end
if lib.IsSnakValue(statement.mainsnak) then
return (lib.getItemIdFromURI(statement.mainsnak.datavalue.value.unit) or 'Q199') == unit
else
return false
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 getValuesFromQualifiers(qualifiers)
local Values = {}
local Formatters = require 'Амодуль: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 'Амодуль: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
-- 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 'Амодуль: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 'Амодуль: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 'Амодуль: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