Module:Requests/union of
Jump to navigation
Jump to search
Example of calls:
- {{#invoke:Requests/union_of|put_queries|id=Q11163999}}
- Instances of children's and youth literature (Q11163999) that are instances of none of the classes young adult literature (Q1233720) , children's literature (Q131539) and picture book (Q254554) [1]
- {{#invoke:Requests/union_of|put_queries|id=Q188745}}
- Instances of Platonic solid (Q188745) that are instances of two (or more) of the classes: [2]
- Instances of Platonic solid (Q188745) that are instances of none of the classes regular tetrahedron (Q14918679) , cube (Q812880) , regular dodecahedron (Q16629569) , regular icosahedron (Q18015071) and regular octahedron (Q12557050) [3]
Translations of resulting wikitext possible in:
Code
local f = require("Module:Functional")
local requests = require("Module:Requests")
local fun = require("Module:Luafun")
local props = require("Module:properties")
local union = {}
-- test item for "union of" : Q11163999
-- test item for "disjoint union of" : Q188745
-- "constant", used as a default name for the query result variable
local var_name = "?item"
----------------------------------------------------------------
-- utility functions
----------------------------------------------------------------
-- Utility : local functions, dependant of the constant above (transformation into one parameter function of existing functions)
local function instances(class)
return requests.instances(var_name, class)
end
local function subclasses_instances(subclass_qidlist)
return requests.union(f.map(instances, subclass_qidlist))
end
-- utility : stack manipulation functions
local function pop(list)
local elem = list[#list]
table.remove(list, #list)
return elem, list
end
union.pop = pop
local function push(list, elem)
table.insert(list, elem)
end
union.push = push
---------------------------------
-- query generators
---------------------------------
-- Union of query : verify if each of the instances of the subject class
-- is an instance of at least one of the child class, as present as values of the "of" qualifier of the statement
-- Sample result for not_in_union("Q79529", {"Q11173", "Q2512777"}) ) :
--select ?item where {
-- ?item wd:P279*/wd:P31 wd:Q79529 minus {
-- { ?item wdt:P279*/wdt:P31 wd:Q11173 } union { ?item wdt:P279*/wdt:P31 wd:Q2512777 }
-- }
--}
function union.not_in_union(class_qid, subclass_qidlist)
local out_items = requests.minus(instances(class_qid), subclasses_instances(subclass_qidlist))
return requests.pi({var_name}, out_items)
end
-- disjoint union of query : verify that no instance of one subclas is an instance of another subclass
-- is an instance of at least one of the child class, as present as values of the "of" qualifier of the statement
-- Sample result for not_in_union("Q5", {"Q51", "Q52"}) ) :
--select ?item where {
-- ?item wdt:P279*/wdt:P31 wd:Q5 .
--
-- {
-- ?item wdt:P279*/wdt:P31 wd:Q51 .
-- ?item wdt:P279*/wdt:P31 wd:Q52 .
-- } union {
-- { ?item wdt:P279*/wdt:P31 wd:Q51 } union { ?item wdt:P279*/wdt:P31 wd:Q52 } .
-- ?item wdt:P279*/wdt:P31 wd:Q53 .
-- }
--}
function union.disjoint(class_qid, subclass_qidlist)
local intersected = {}
local to_union = {}
--init : the first element is automatically intersected with itself
push(intersected, pop(subclass_qidlist))
while #subclass_qidlist>0 do
local elem, subclass_qidlist = pop(subclass_qidlist)
local intersect_with_elem_instances = requests.intersect({subclasses_instances(intersected), instances(elem)})
push(to_union, intersect_with_elem_instances)
push(intersected, elem)
end
return requests.pi({var_name}, requests.intersect({instances(class_qid), requests.union(to_union)}))
end
------------------------------------------------------
-- function that generates a pair of query with their description for documentation
------------------------------------------------------
-- returns a list of elements from a list of lists of elements
-- composes by the elements of the sublists
local function flatten(list)
return fun.reduce(fun.chain, {}, list):totable()
end
local function call_template(template, class_qid, subclass_qidlist)
return mw.getCurrentFrame():expandTemplate{
title=template,
args=flatten({{class_qid}, subclass_qidlist})
}
end
local function describe_not_in_union(class_qid, subclass_qidlist)
return call_template("doc not in union", class_qid, subclass_qidlist)
end
local function describe_disjoint(class_qid, subclass_qidlist)
return call_template("doc disjoint union", class_qid, subclass_qidlist)
end
local function described_not_in_union(class_qid, subclass_qidlist)
local res = {
["query"] = union.not_in_union(class_qid, subclass_qidlist);
["descr"] = describe_not_in_union(class_qid, subclass_qidlist);
}
return res
end
local function described_disjoint(class_qid, subclass_qidlist)
local res = {
["query"] = union.disjoint(class_qid, subclass_qidlist);
["descr"] = describe_disjoint(class_qid, subclass_qidlist);
}
return res
end
-- constants
-- map of query generation function list relevant for a given property
local prop_query_map = {
-- for a disjoint union : verify both
[props.disjoint_union_of] = {described_disjoint; described_not_in_union} ;
-- for a simple union : verify only the covering of the subclasses wrt. the parent one
[props.union_of] = {described_not_in_union} ;
}
--------------------------------------------------------------
-- list of queries generators
--------------------------------------------------------------
-- get a list of relevant queries from a statement
function union.queries_from_statement(stmt, item_qid, query_gens)
local ids = fun.map(
function(snak) return "Q" .. tostring(snak.datavalue.value["numeric-id"]) end,
stmt.qualifiers[props.list_item]
)
-- apply each generator for this kind of statements to get a list of queries
local described_queries = fun.map(function (generator) return generator(item_qid, ids:totable()) end,
query_gens)
return described_queries
end
local function getRelevantProperties(item)
local props = item:getProperties() -- all properties used in statement of the item
local present_claims = fun.filter( -- filter those who are not in our list (see prop_query_map )
function (prop) return (prop_query_map[prop] ~= nil) end,
props)
return present_claims
end
function union.has_union_claims(item)
return getRelevantProperties(item):length() > 0
end
local function load_item(item_or_qid)
if type(item_or_qid) == "table" then
return item_or_qid
else
return mw.wikibase.getEntity(item_or_qid)
end
end
-- get a list of all relevant queries from a whole item
function union.queries(item_qid)
-- loading item datas
local item = load_item(item_qid)
local queries = {}
-- compute the id of the subject class
local id = item.id
-- for each statements of a relevant property, get a list of queries, and flatten this into a list of queries for each property
queries = getRelevantProperties(item):map(
function(property)
return fun.map(
function(claim)
return union.queries_from_statement(claim, id, prop_query_map[property])
end,
item.claims[property]
):reduce(fun.chain, {})
end
)
-- flatten the list of queries for each property into a plain list of query
return flatten(queries)
end
------------------------------------------------------------
-- Interface : generate wikitext
------------------------------------------------------------
-- Interface auxiliary : arguments cleanup
local function args(frame)
local cleanargs ={}
for i, j in pairs(frame.args) do
if j ~= '' then cleanargs[i] = j end
end
if not cleanargs.id then
cleanargs["id"] = frame:preprocess("{{BASEPAGENAME}}")
end
return cleanargs
end
-- computes a list of queries formatted in wikitext ; to be used in item doc related templates
function union.put_queries(frame)
local res = ""
local cleanargs = args(frame)
for _, query in pairs(union.queries(cleanargs["id"])) do
res = res .. "*" .. query.descr .. frame:expandTemplate{title="Wdquery", args={["query"] = query.query}} .. "\n"
end
return res
end
-- Documentation : get a list of localized values the "of" arguments qualifiers
function union.doc_class_list(frame)
local classes = frame:getParent().args
-- apply Q' on each classes and get a linguistic conjuction
return mw.text.listToText(
fun.tail(classes) -- the first argument is the parent class ; remove it
:filter(function(elem) return elem ~= nil and elem ~= "" end) -- clean empty arguments
:map(
function(class) return frame:expandTemplate{title="Q'", args = {class}} end
)
:totable()
)
end
function union.has_queries(frame)
local cleanargs = args(frame)
local item=load_item(cleanargs.id)
if union.has_union_claims(item) then
return "yes"
else
return ""
end
end
return union