Module:entityUtilities
From Rhizome Artbase
Documentation for this module may be created at Module:entityUtilities/doc
local p = {}
local m = require('Module:manifest')
-- Return entity from either an entity table or an entity ID.
-- Throw error if string cannot be parsed as entity ID or if
-- given table does not look like an entity table.)
function p.entity(e)
if type(e) == 'string' then
if not mw.wikibase.isValidEntityId(e) then
error('No entity or entity ID provided.')
end
e = mw.wikibase.getEntity(e)
elseif not p.isEntity(e) then
error('Not an entity table: ' .. mw.dumpObject(e))
end
return e
end
-- Get list of statements that are not deprecated.
-- Arguments:
-- [1]: item (QID or item table)
-- [2]: property to list statements from (PID)
-- 'filter': only return items that match this 'instance of' (Single QID
-- or list of QIDs) - optional
-- 'order_by': sort by this propery, ascending (single PID) - optional
-- 'desc': if true, reverse order_by results
--
-- =mw.dumpObject( p.statementList{'Q2508', 'P45', order_by='P26', desc=true} )
function p.statementList(arg)
local entity = p.entity(arg[1])
local prop = arg[2] or nil
local filter = arg.filter or nil
local order_by = arg.order_by or nil
local desc = arg.desc or false
if filter then
if type(filter) == 'string' then
filter = {filter}
end
end
local collected = {}
local results = {}
-- If staments with this property exist for the current entity
-- collect statements ranked as 'normal' or 'preferred'
if entity.claims[prop] then
for i,statement in pairs(entity.claims[prop]) do
if p.valueInTable({'normal','preferred'}, statement.rank) then
-- handle value statements (TODO: handle 'unknown' and 'no value')
if statement.mainsnak.snaktype == 'value' then
-- add statement to collected list
table.insert(collected, statement)
elseif statement.mainsnak.snaktype == 'somevalue' then
-- 'unknown' value
table.insert(collected, statement)
end
end
end
-- go through all the statements that were collected
for i,statement in ipairs(collected) do
local filter_match = true -- if no filter was requested, all entities match by default
-- reference to another entity that might be subject to filtering
if statement.mainsnak.snaktype == 'value' then -- skip 'unknown' or 'no value'
if statement.mainsnak.datavalue.type == 'wikibase-entityid' then
local nested_entity = mw.wikibase.getEntity(statement.mainsnak.datavalue.value.id)
-- Filter 'for instance of'
if filter then
filter_match = p.entityIs(nested_entity, filter)
end
end
end
-- Add statement or referenced entity to result list
if filter_match then
local nested_entity = nil
if statement.mainsnak.snaktype == 'value' then -- skip 'unknown' or 'no value'
if statement.mainsnak.datavalue.type == 'wikibase-entityid' then
nested_entity = mw.wikibase.getEntity(statement.mainsnak.datavalue.value.id)
end
end
table.insert(results, nested_entity or statement)
end
end
end
-- If order_by is given, the results should be ordered by that property.
-- Depending on what is the properties datatype, different functions will
-- be used
if order_by then
-- check if the order key is a property
if mw.wikibase.isValidEntityId(order_by) then
local property = mw.wikibase.getEntity(order_by)
-- check for type of property
if property.type == 'property' then
-- sort by a property of datatype time
if property.datatype == 'time' then
-- It's a time
local timeFromStatement = function(item)
if p.keyInTable(item.claims, order_by) then
local statements = mw.wikibase.getBestStatements(item.id, order_by)
return statements[1].mainsnak.datavalue.value.time
end
return '0'
end
table.sort(results, function(statement1, statement2) return timeFromStatement(statement1) < timeFromStatement(statement2) end)
-- reverse table if asked for it
if desc then
local resultsDesc = {}
local itemCount = #results
for k, v in ipairs(results) do
resultsDesc[itemCount + 1 - k] = v
end
results = resultsDesc
end
-- unsupported datatype
else
error('`sort_by` unsupported property type: ' .. property.datatype)
end
else
error('`sort_by` must be PID of a property.')
end
end
end
return results
end
-- collect statements from nested entities
-- entity is the base entity to start from
-- nesting_property is the property om the base entity that contains
-- more entities; these are the nested entities
-- collect_property is the property value on a nested entity
-- that should be collected
function p.nestedStatementList(entity, nesting_property, collect_property)
local entity = p.entity(entity)
-- check if the nested property is of type wikibase-item
local nesting_property_entity = p.entity(nesting_property)
if nesting_property_entity.datatype ~= 'wikibase-item' then
error(nesting_property .. ' is not of type "wikibase-item"')
end
local results = {}
local nested_items = p.statementList{entity, nesting_property}
for _,nested_item in ipairs(nested_items) do
local collected_values = p.statementList{nested_item, collect_property}
for __,v in ipairs(collected_values) do
table.insert(results, v)
end
end
return results
end
-- Check if string is empty
function p.isEmpty(str)
if str == nil or string.len(str:gsub('%s*', '')) == 0 then
return true
else
return false
end
end
-- Check if table t contains value v
function p.valueInTable(t, v)
if type(v) == 'string' then
v={v}
end
for table_key,table_value in pairs(t) do
for i,match_value in pairs(v) do
if table_value == match_value then
return table_key
end
end
end
return false
end
-- Check if table t has key k
function p.keyInTable(t, k)
return t[k] ~= nil
end
-- Retrieve QID that is either handed over in a frame object,
-- or that is taken from the SiteLink, or from the page's URL.
function p.entityIdFromFrame(frame)
-- Is the first argument of the frame a QID?
if type(frame) == 'table' then
if type(frame.args) == 'table' then
if type(frame.args[1]) == 'string' then
-- Remove space characters (can appear when invoked
-- from a wikipage)
local entity_id = string.gsub(frame.args[1], "%s+", "")
-- is this an entity ID at all?
if mw.wikibase.isValidEntityId(entity_id) then
return entity_id
end
else
local page_title = frame:getParent():getTitle()
if mw.wikibase.isValidEntityId(page_title) then
return page_title
end
end
end
end
-- If no QID given, try to take from SiteLink.
local entity_id = mw.wikibase.getEntityIdForCurrentPage()
if not entity_id then
-- Everything failed!
error('No QID given, page has no SiteLink, and the page URL does not contain an entity ID')
else
return entity_id
end
end
-- check if a given table looks like an entity table
function p.isEntity(var)
if type(var) == 'table' and var.type and p.valueInTable({'item', 'property'}, var.type) then
return true
else
return false
end
end
-- check if entity is an instance of an item (like 'is this an artwork?')
function p.entityIs(entity, type_QID)
local entity = p.entity(entity)
if type(type_QID) == 'string' then
type_QID = {type_QID}
end
local instances_of = entity:getBestStatements(m.prop.instance_of)
for _,instance_of in pairs(instances_of) do
if p.valueInTable(type_QID, instance_of.mainsnak.datavalue.value.id) then
return true
end
end
return false
end
-- set of functions to check if statement references an
-- 'unknown' or 'no value' statement
function p.entityIsValueType(entity, value_type)
if type(entity) == 'table' then
if p.keyInTable(entity, 'mainsnak') then
if entity.mainsnak.datatype == 'wikibase-item' then
if entity.mainsnak.snaktype == value_type then
return true
end
end
end
end
return false
end
function p.entityIsUnknown(entity)
return p.entityIsValueType(entity, 'somevalue')
end
function p.entityIsNoValue(entity)
return p.entityIsValueType(entity, 'novalue')
end
-- get all 'instance of' IDs of an entity
function p.instances_of(entity)
local entity = p.entity(entity)
local instance_of_ids = {}
local instance_of_statements = entity:getBestStatements(m.prop.instance_of)
for _,statement in ipairs(instance_of_statements) do
table.insert(instance_of_ids, statement.mainsnak.datavalue.value.id)
end
return instance_of_ids
end
--------------------------------------------------------------------------------
-- check if a wikipage exists
function p.pageExists(pageName)
local page = mw.title.new(pageName)
return page.exists
end
--------------------------------------------------------------------------------
return p