Module:MathModDBHelperMethods
From MaRDI portal
Documentation for this module may be created at Module:MathModDBHelperMethods/doc
-- Module for executing SPARQL queries
local sparql = require('SPARQL')
-- Required module containing helper methods
local helper = require('Module:HelperMethods')
-- MediaWiki library for logging and utilities
local mw = require('mw')
local json = require("mw.text")
-- Main table to hold all functions
local p = {}
-------------------------------- Local Functions -------------------------------
-- Utility: get all P31 values of an entity
local function getInstanceOf(qid)
local e = mw.wikibase.getEntityObject(qid)
local result = {}
if not e or not e.claims or not e.claims["P31"] then
return result
end
for _, claim in pairs(e.claims["P31"]) do
local id = claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value.id
if id then
table.insert(result, id)
end
end
return result
end
-------------------------------- Local Functions -------------------------------
-- Function to return a canonical URL as clickable wikitext
-- Accepts either frame (from #invoke) or raw QID
-- Optional: pass label="YourText" as a named argument in frame
function p.canonicalLink(frameOrQID)
local qid, customLabel
-- Determine how function was called
if frameOrQID then
if frameOrQID.args then
-- Called via #invoke
qid = frameOrQID.args[1]
customLabel = frameOrQID.args.label
else
-- Called directly from Lua
qid = frameOrQID
end
end
if not qid then return "No QID provided" end
local url
-- Get the entity
local e = mw.wikibase.getEntity(qid)
if not e or not e.sitelinks or not e.sitelinks.mardi then
return "https://portal.mardi4nfdi.de/wiki/Item:" .. qid
end
-- Build URL using the sitelink title
local title = e.sitelinks.mardi.title
local url = "https://portal.mardi4nfdi.de/wiki/" .. mw.uri.encode(title, "PATH")
-- Determine label to show
local label = customLabel or mw.wikibase.label(qid) or title
-- Return clickable wikitext link
if frameOrQID.args then
-- called via #invoke, preprocess needed for wikitext
return frameOrQID:preprocess(string.format("[%s %s]", url, label))
else
-- called from Lua, return plain wikitext string
return string.format("%s", url)
end
end
-- Function to debug sitelinks
-- It prints the entire entity object, including sitelinks, claims, etc.
function p.debugSitelink(frame)
local qid = frame.args[1]
local e = mw.wikibase.getEntity(qid)
return mw.text.nowiki(mw.dumpObject(e))
end
-- Function to get instance of types as links
function p.getInstanceOfTypes(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return ""
end
-- SPARQL query: get instance of items and their labels
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?InstanceOfType ?InstanceOfTypeLabel WHERE {
entityId: p:P31 ?statement .
?statement ps:P31 ?InstanceOfType .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY ?InstanceOfTypeLabel
]]
-- Execute SPARQL
local jsonResults = sparql.runQuery(sparqlQuery)
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
end
local InstanceOfTypes = {}
local total = #jsonResults.results.bindings
-- Loop through results
for i = 0, total do
local item = jsonResults.results.bindings[i]
if item and item.InstanceOfType and item.InstanceOfType.value then
local qid = item.InstanceOfType.value:match(".*/(Q%d+)$") or item.InstanceOfType.value
local label = (item.InstanceOfTypeLabel and item.InstanceOfTypeLabel.value) or qid
local url = "https://portal.mardi4nfdi.de/wiki/Item:" .. qid
local link = string.format("[%s %s]", url, label)
table.insert(InstanceOfTypes, link)
end
end
-- Build wikitext table
local wikitextTable = "{| class='wikitable'\n|-\n| " .. table.concat(InstanceOfTypes, " || ") .. "\n|}"
return wikitextTable
end
-- Function to get instance of types
-- Function to replace pattern in the comma pattern separated list
function p.replace_pattern(frame)
local input = frame.args[1] or ""
local sep = frame.args[2] or " " -- default to space if no separator given
-- Replace comma+pattern (,xxxx) with sep
local result = string.gsub(input, ",xxxx%s*", sep)
return result
end
-- Function to generate a table listing a type of individuals
-- test with =p.getList{args={'Model'}}
function p.getList(frame)
local entityType = frame.args[1] or 'Model'
local height = frame.args[2] or '400px' -- Default height if not specified
local width = frame.args[3] or '800px' -- Default width if not specified
local baseUrl = mw.site.server -- Get the current URL
-- Define mapping of entity types to their corresponding Q IDs and URL prefixes
local entityConfig = {
['Model'] = {qid = 'Q68663', urlPrefix = 'Model:', title = 'mathematical models'},
['Academic discipline'] = {qid = 'Q60231', urlPrefix = 'Academic_discipline:', title = 'academic disciplines'},
['Research problem'] = {qid = 'Q6534292', urlPrefix = 'Research_problem:', title = 'research problems'},
['Task'] = {qid = 'Q6534247', urlPrefix = 'Task:', title = 'computational tasks'},
['Quantity'] = {qid = 'Q6534237', urlPrefix = 'Quantity:', title = 'quantities'},
['Quantity kind'] = {qid = 'Q6534245', urlPrefix = 'Quantity:', title = 'quantity kind items'},
['Formula'] = {qid = 'Q6481152', urlPrefix = 'Formula:', title = 'mathematical expressions'}
}
-- Get configuration for the specified entity type
local config = entityConfig[entityType]
if not config then
return "Invalid entity type. Valid options are: Model, Academic discipline, Research problem, Task, Quantity, Quantity kind, Formula"
end
-- Q6534265 refers to MathModDB community
local sparqlQuery = [[
SELECT ?itemLabel ?modelURL ?item
WHERE {
?item wdt:P31 wd:]] .. config.qid .. [[;
wdt:P1495 wd:Q6534265 .
?item rdfs:label ?itemLabel .
FILTER(LANG(?itemLabel) = "en")
BIND(REPLACE(STR(?item), "^.*/Q", "]] .. baseUrl .. [[/wiki/]] .. config.urlPrefix .. [[") AS ?modelURL)
}
ORDER BY LCASE(?itemLabel)
]]
local jsonResults = sparql.runQuery(sparqlQuery)
-- Handle error in SPARQL query execution
if jsonResults and jsonResults.error then
mw.log("Error in SPARQL query: " .. tostring(jsonResults.error))
return nil
end
if not jsonResults then
return "Could not fetch data."
end
if helper.countElementsInBindings(jsonResults.results.bindings) == 0 then
return "No records found."
end
-- Convert the JSON results into a Lua table
local fieldOrder = {"modelURL", "itemLabel", "item"}
local dataTable = helper.convertJsonToTableOrdered(jsonResults, fieldOrder)
-- Create and return HTML table from the data
local headers = {entityType}
local htmlTable = helper.createHtmlTableWithMergedCols(dataTable, headers, {{3}})
-- Create a parent container for the table
local parentContainer = mw.html.create('div')
:addClass('parent-container')
:css('width', width)
local heading = mw.html.create('h2')
:wikitext('List of ' .. config.title)
-- Add the table and chart to the parent container
parentContainer
:node(heading)
:node(htmlTable)
return tostring(parentContainer)
end
-- Function to get linked items
function p.getLinkedItems(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT DISTINCT ?itemLabel ?item ?propertyLabel ?property
WHERE {
# Replace with your specific entity
?itemInter ?ps entityId: .
# Convert property URL to entity-style URL
BIND(
IRI(REPLACE(STR(?ps),
"^https://portal.mardi4nfdi.de/prop/(direct|qualifier|statement)/",
"https://portal.mardi4nfdi.de/entity/"))
AS ?property
)
# Skip unwanted property
FILTER(?property != <http://schema.org/about>)
# Clean URL: keep only QID, remove /statement/ and trailing UUID
BIND(
IRI(
REPLACE(
STR(?itemInter),
".*/(Q[0-9]+).*",
"https://portal.mardi4nfdi.de/entity/$1"
)
) AS ?item
)
# Get labels
SERVICE wikibase:label {
bd:serviceParam wikibase:language "en".
?item rdfs:label ?itemLabel .
?property rdfs:label ?propertyLabel .
}
}
ORDER BY LCASE(?itemLabel)
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
return ""
end
local linkedItems = {}
local totalLinkedItems = #jsonResults.results.bindings
if totalLinkedItems >= 0 then
-- Loop through the bindings
for index = 0, totalLinkedItems do
local item = jsonResults.results.bindings[index]
if not item then
return ""
end
if not item.itemLabel or not item.itemLabel.value then
return ""
elseif not item.item or not item.item.value then
return ""
else
local itemLabel = item.itemLabel.value
local itemURL = item.item.value
local propertyLabel = item.propertyLabel and item.propertyLabel.value or ""
local propertyURL = item.property and item.property.value or ""
local itemWithURL = string.format('[%s %s]', tostring(itemURL), tostring(itemLabel))
local propertyWithURL = string.format('[%s %s]', tostring(propertyURL), tostring(propertyLabel))
table.insert(linkedItems, '| ' .. itemWithURL .. ' || ' .. propertyWithURL)
end
end
-- if there are linked items
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" ..
"! Item\n! Property\n" .. -- header row
"|-\n" .. -- separator before first data row
table.concat(linkedItems, "\n|-\n") ..
"\n|}"
return wikitextTable
else
return ""
end
end
-- Function to get subclass of items
function p.getSubclassOf(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Constructing the SPARQL query with dynamic entity entityId
-- P36: subclass of property id
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?URL ?Label
WHERE {
entityId: wdt:P36 ?URL.
?URL rdfs:label ?Label
FILTER(LANG(?Label) = "en")
}
ORDER BY ?Label
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
return "No specialized academic discipline found"
end
local subClasses = {}
-- Get the number of specialized academic disciplines
local totalSubClasses = #jsonResults.results.bindings
-- Loop through the bindings
for index = 0, totalSubClasses do
local item = jsonResults.results.bindings[index]
if not item then
return ""
end
if not item.Label.value then
return ""
elseif not item.URL.value then
return ""
else
local label = item.Label.value
local url = item.URL.value
local numericId = url:match("Q(%d+)")
local urlRendered = "https://portal.mardi4nfdi.de/wiki/Academic_discipline:" .. numericId
local labelWithUrl = string.format('[%s %s]', tostring(url), tostring(label))
table.insert(subClasses, "| " .. labelWithUrl)
end
end
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" .. table.concat(subClasses, "\n|-\n") .. "\n|}"
return wikitextTable
end
-- Function to get described by source
function p.getDescribedBySource(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Constructing the SPARQL query with dynamic entity entityId
-- P286: described by source property id
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?URL ?Label
WHERE {
entityId: wdt:P286 ?URL.
?URL rdfs:label ?Label
FILTER(LANG(?Label) = "en")
}
ORDER BY ?Label
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
return "No specialized academic discipline found"
end
local describedBySources = {}
-- Get the number of specialized academic disciplines
local totalSources = #jsonResults.results.bindings
-- Loop through the bindings
for index = 0, totalSources do
local item = jsonResults.results.bindings[index]
if not item then
return ""
end
if not item.Label.value then
return ""
elseif not item.URL.value then
return ""
else
local label = item.Label.value
local url = item.URL.value
local numericId = url:match("Q(%d+)")
local urlRendered = "https://portal.mardi4nfdi.de/wiki/Academic_discipline:" .. numericId
local labelWithUrl = string.format('[%s %s]', tostring(url), tostring(label))
table.insert(describedBySources, "| " .. labelWithUrl)
end
end
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" .. table.concat(describedBySources, "\n|-\n") .. "\n|}"
return wikitextTable
end
function p.getImageWithLegend(frame)
-- Property ID for the image
local pidImage = "P356"
-- Property ID for the (qualifier) media legend
local pidMediaLegend = "P401"
local defaultLegend = "No legend available."
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Attempt to retrieve entity data
local entity = mw.wikibase.getEntity(entityId)
local imageClaims = entity.claims[pidImage]
local imageStrings = {}
if imageClaims and type(imageClaims) == "table" then
local numClaims = #imageClaims
-- Loop through the image claims
-- for index = 1, numClaims do
local index = 1
for _, claim in ipairs(imageClaims) do
-- Extract the image filename
-- local imageFilename = imageClaims[index].mainsnak.datavalue.value
local imageFilename = claim.mainsnak.datavalue.value
-- Default legend (empty string)
local imageLegend = ""
-- Check if the media legend qualifier exists
if claim.qualifiers and claim.qualifiers[pidMediaLegend]
and #claim.qualifiers[pidMediaLegend] > 0
and claim.qualifiers[pidMediaLegend][1].datavalue
and claim.qualifiers[pidMediaLegend][1].datavalue.value
and claim.qualifiers[pidMediaLegend][1].datavalue.value.text then
imageLegend = claim.qualifiers[pidMediaLegend][1].datavalue.value.text
end
-- local imageLegend = imageClaims[index].qualifiers[pidMediaLegend][1].datavalue.value.text
-- Add this image line to the list
-- table.insert(imageStrings, {filename = imageFilename, legend = imageLegend})
table.insert(imageStrings, string.format("[[File:%s|thumb|380px|%s]]", imageFilename, imageLegend))
if index < numClaims then
-- spacer column
table.insert(imageStrings, ' ')
end
index = index + 1
end
-- Combine all image cells into one row of a wikitable
local imageTable = '{| class="wikitable"\n| ' .. table.concat(imageStrings, ' || ') .. '\n|}'
return imageTable .. "\n\n"
else
return ""
end
end
function p.getVideoWithLegend(frame)
-- Property ID for the video
local pidVideo = "P797"
-- Property ID for the (qualifier) media legend
local pidMediaLegend = "P401"
local defaultLegend = "No legend available."
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Attempt to retrieve entity data
local entity = mw.wikibase.getEntity(entityId)
local videoClaims = entity.claims[pidVideo]
local videoStrings = {}
if videoClaims and type(videoClaims) == "table" then
local numClaims = #videoClaims
-- Loop through the image claims
for index = 1, numClaims do
-- Extract the image filename
local videoFilename = videoClaims[index].mainsnak.datavalue.value
local videoLegend = videoClaims[index].qualifiers[pidMediaLegend][1].datavalue.value.text
-- Add this image line to the list
table.insert(videoStrings, string.format("[[File:%s|thumb|340px|%s]]", videoFilename, videoLegend))
if index < numClaims then
-- spacer column
table.insert(videoStrings, ' ')
end
end
-- Combine all image cells into one row of a wikitable
local videoTable = '{| class="wikitable"\n| ' .. table.concat(videoStrings, ' || ') .. '\n|}'
return videoTable .. "\n\n"
else
return " "
end
end
function p.getLocalImageWithLegend(frame)
-- Property ID for the image
local pidImage = "P1640"
-- Property ID for the (qualifier) media legend
local pidMediaLegend = "P401"
local defaultLegend = "No legend available."
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Attempt to retrieve entity data
local entity = mw.wikibase.getEntity(entityId)
local imageClaims = entity.claims[pidImage]
local imageStrings = {}
if imageClaims and type(imageClaims) == "table" then
local numClaims = #imageClaims
-- Loop through the image claims
for index = 1, numClaims do
-- Extract the image filename
local imageFilename = imageClaims[index].mainsnak.datavalue.value
-- Default legend (empty string)
local imageLegend = ""
-- Check if the media legend qualifier exists
if imageClaims[index].qualifiers and imageClaims[index].qualifiers[pidMediaLegend]
and #imageClaims[index].qualifiers[pidMediaLegend] > 0
and imageClaims[index].qualifiers[pidMediaLegend][1].datavalue
and imageClaims[index].qualifiers[pidMediaLegend][1].datavalue.value
and imageClaims[index].qualifiers[pidMediaLegend][1].datavalue.value.text then
imageLegend = imageClaims[index].qualifiers[pidMediaLegend][1].datavalue.value.text
end
-- Add this image line to the list
table.insert(imageStrings, string.format("[[File:%s|thumb|380px|%s]]", imageFilename, imageLegend))
if index < numClaims then
-- spacer column
table.insert(imageStrings, ' ')
end
end
-- Combine all image cells into one row of a wikitable
local imageTable = '{| class="wikitable"\n| ' .. table.concat(imageStrings, ' || ') .. '\n|}'
return imageTable .. "\n\n"
else
return " "
end
end
function p.getLocalVideoWithLegend(frame)
-- Property ID for the video
local pidVideo = "P1675"
-- Property ID for the (qualifier) media legend
local pidMediaLegend = "P401"
local defaultLegend = "No legend available."
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Attempt to retrieve entity data
local entity = mw.wikibase.getEntity(entityId)
local videoClaims = entity.claims[pidVideo]
local videoStrings = {}
if videoClaims and type(videoClaims) == "table" then
local numClaims = #videoClaims
-- Loop through the image claims
for index = 1, numClaims do
-- Extract the image filename
local videoFilename = videoClaims[index].mainsnak.datavalue.value
local videoLegend = videoClaims[index].qualifiers[pidMediaLegend][1].datavalue.value.text
-- Add this image line to the list
table.insert(videoStrings, string.format("[[File:%s|thumb|340px|%s]]", videoFilename, videoLegend))
if index < numClaims then
-- spacer column
table.insert(videoStrings, ' ')
end
end
-- Combine all image cells into one row of a wikitable
local videoTable = '{| class="wikitable"\n| ' .. table.concat(videoStrings, ' || ') .. '\n|}'
return videoTable .. "\n\n"
else
return " "
end
end
-- Function to get list with qualifier values only
function p.getListWithQualifierValues(frame)
local entityId = frame.args[1]
local propertyId = frame.args[2]
-- Validate input parameter
if not entityId or entityId == '' then
return " "
end
-- Constructing the SPARQL query with dynamic entity entityId
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?URL ?Label ?qualPropEntity ?qualPropLabel ?qualValue ?qualValueLabel
WHERE {
entityId: p:]] .. propertyId .. [[ ?statement .
?statement ps:]] .. propertyId .. [[ ?URL .
?URL rdfs:label ?Label
FILTER(LANG(?Label) = "en")
OPTIONAL {
?statement ?qualProp ?qualValue .
FILTER(STRSTARTS(STR(?qualProp), STR(pq:))) # only qualifiers
# Map qualifier property to its property entity
BIND(IRI(REPLACE(STR(?qualProp), "prop/qualifier", "entity")) AS ?qualPropEntity)
OPTIONAL {
?qualPropEntity rdfs:label ?qualPropLabel .
FILTER(LANG(?qualPropLabel) = "en")
}
OPTIONAL {
?qualValue rdfs:label ?qualValueLabel .
FILTER(LANG(?qualValueLabel) = "en")
}
}
}
ORDER BY ?Label
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
return "No specialized research fields found"
end
local listItems = {}
-- Get the number of specialized research fields
local totallistItems = #jsonResults.results.bindings
-- Loop through the bindings
for index = 0, totallistItems do
local item = jsonResults.results.bindings[index]
if not item.Label.value then
return "Error: Missing item.Label.value"
elseif not item.URL.value then
return "Error: Missing item.URL.value"
else
local label = item.Label.value
local url = item.URL.value
local numericId = url:match("Q(%d+)")
local urlRendered = "https://portal.mardi4nfdi.de/wiki/Model:" .. numericId
local qualValue = item.qualValueLabel or item.qualValue or ""
local labelWithUrl = string.format('[%s %s]', tostring(url), tostring(label))
if qualValue ~= "" then
local qualValueWithUrl = string.format('[%s %s]', tostring(item.qualValue.value), tostring(item.qualValueLabel.value))
local row = "| " .. labelWithUrl .. " || (" .. qualValueWithUrl .. ")"
table.insert(listItems, row)
else
table.insert(listItems, "| " .. labelWithUrl)
end
end
end
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" .. table.concat(listItems, "\n|-\n") .. "\n|}"
return wikitextTable
end
-- Function to get list with qualifier values and qualifier labels
function p.getListWithQualifierValuesLabels(frame)
local entityId = frame.args[1]
local propertyId = frame.args[2]
-- Validate input parameter
if not entityId or entityId == '' then
return " "
end
-- Constructing the SPARQL query with dynamic entity entityId
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?URL ?Label ?qualPropEntity ?qualPropLabel ?qualValue ?qualValueLabel
WHERE {
entityId: p:]] .. propertyId .. [[ ?statement .
?statement ps:]] .. propertyId .. [[ ?URL .
?URL rdfs:label ?Label
FILTER(LANG(?Label) = "en")
OPTIONAL {
?statement ?qualProp ?qualValue .
FILTER(STRSTARTS(STR(?qualProp), STR(pq:))) # only qualifiers
# Map qualifier property to its property entity
BIND(IRI(REPLACE(STR(?qualProp), "prop/qualifier", "entity")) AS ?qualPropEntity)
OPTIONAL {
?qualPropEntity rdfs:label ?qualPropLabel .
FILTER(LANG(?qualPropLabel) = "en")
}
OPTIONAL {
?qualValue rdfs:label ?qualValueLabel .
FILTER(LANG(?qualValueLabel) = "en")
}
}
}
ORDER BY ?Label
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
return "No specialized research fields found"
end
local listItems = {}
-- Get the number of specialized research fields
local totallistItems = #jsonResults.results.bindings
-- Loop through the bindings
for index = 0, totallistItems do
local item = jsonResults.results.bindings[index]
if not item.Label.value then
return "Error: Missing item.Label.value"
elseif not item.URL.value then
return "Error: Missing item.URL.value"
else
local label = item.Label.value
local url = item.URL.value
local numericId = url:match("Q(%d+)")
local urlRendered = "https://portal.mardi4nfdi.de/wiki/Model:" .. numericId
local qualValue = item.qualValueLabel or item.qualValue or ""
local qualPropLabel = item.qualPropLabel or ""
local labelWithUrl = string.format('[%s %s]', tostring(url), tostring(label))
if qualValue ~= "" then
local qualValueWithUrl = string.format('[%s %s]', tostring(item.qualValue.value), tostring(item.qualValueLabel.value))
local row = "| " .. labelWithUrl .. " || " .. qualPropLabel.value .. " || " .. qualValueWithUrl
table.insert(listItems, row)
else
table.insert(listItems, "| " .. labelWithUrl)
end
end
end
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" .. table.concat(listItems, "\n|-\n") .. "\n|}"
return wikitextTable
end
-- Function to get instance of types
function p.getInstanceOfTypes(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Constructing the SPARQL query with dynamic entity entityId
-- P31: instance of property id
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?InstanceOfTypeLabel WHERE {
entityId: p:P31 ?statement .
?statement ps:P31 ?InstanceOfType .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY ?InstanceOfTypeLabel
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
end
local InstanceOfTypes = {}
-- Get the number of model types
local totalInstanceOfTypes = #jsonResults.results.bindings
-- Loop through the bindings
for index = 0, totalInstanceOfTypes do
local item = jsonResults.results.bindings[index]
if item and item.InstanceOfTypeLabel and item.InstanceOfTypeLabel.value then
local label = item.InstanceOfTypeLabel.value
table.insert(InstanceOfTypes, label)
end
end
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n|-\n| " .. table.concat(InstanceOfTypes, " || ") .. "\n|}"
return wikitextTable
end
-- Function to get instance of types
function p.getListNamedAfter(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Constructing the SPARQL query with dynamic entity entityId
-- P31: instance of property id
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?namedAfterLabel ?namedAfter
WHERE {
entityId: p:P558 ?statement .
?statement ps:P558 ?namedAfter .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY ?namedAfterLabel
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
end
local ListNamedAfter = {}
-- Get the number of persons named after
local totalPersons = #jsonResults.results.bindings
-- Loop through the bindings
for index = 0, totalPersons do
local item = jsonResults.results.bindings[index]
if item and item.namedAfterLabel and item.namedAfterLabel.value then
local label = item.namedAfterLabel.value
local url = item.namedAfter.value
local numericId = url:match("Q(%d+)")
local urlRendered = "https://portal.mardi4nfdi.de/wiki/Person:" .. numericId
local labelWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(label))
table.insert(ListNamedAfter, labelWithUrl)
end
end
return table.concat(ListNamedAfter, ', ')
-- end of function getListNamedAfter
end
-- Function to get Formulations with Quantities
function p.getFormulationsWithQuantities(frame)
local entityId = frame.args[1]
-- Validate input parameter
if not entityId or entityId == '' then
return "Error: No entity ID provided"
end
-- Constructing the SPARQL query with dynamic entity entityId
-- P1560: contains property id
-- P989: defining formula property id
local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?Formula ?defining_formulation ?IDFormula ?qualValueLabel ?qualValue ?qualProp ?seriesOrdinal
WHERE {
# full statement node for P1560
entityId: p:P1560 ?statement .
?statement ps:P1560 ?IDFormula .
OPTIONAL {
?IDFormula rdfs:label ?Formula .
FILTER(LANG(?Formula) = "en")
}
OPTIONAL { ?IDFormula wdt:P989 ?defining_formulation . }
# qualifiers on this statement
OPTIONAL {
?statement ?qualProp ?qualValue .
FILTER(STRSTARTS(STR(?qualProp), STR(pq:))) # only qualifiers
OPTIONAL {
?qualProp rdfs:label ?qualPropLabel .
FILTER(LANG(?qualPropLabel) = "en")
}
OPTIONAL {
?qualValue rdfs:label ?qualValueLabel .
FILTER(LANG(?qualValueLabel) = "en")
}
# 👇 extract series ordinal when qualifier is P146
OPTIONAL {
FILTER(?qualProp = pq:P146)
?qualValue wikibase:quantityAmount ?seriesOrdinal .
}
}
}
ORDER BY
IF(BOUND(?seriesOrdinal), ?seriesOrdinal, 1e9)
]]
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
return "\"No mathematical expressions found.\""
end
-- local resultTable = p.convertJsonToTable(jsonResults)
-- return "<pre>" .. mw.text.nowiki(json.jsonEncode(resultTable)) .. "</pre>"
-- local jsonString = mw.text.jsonEncode(jsonResults)
-- return "<pre>" .. mw.text.nowiki(jsonString) .. "</pre>"
return p.extractDefiningFormulationsWithQuantities(jsonResults)
end
-- Function to extract Formulations with Quantities
function p.extractDefiningFormulationsWithQuantities(jsonResults)
local frame = mw.getCurrentFrame()
-- Table to store contained entities
local containedEntities = {}
-- Table to store quantity symbol and name id
local quantitySymbolNameIdPairs = {}
local jsonString = mw.text.jsonEncode(jsonResults)
-- Decode the JSON string into a Lua table
local data = mw.text.jsonDecode(jsonString)
-- Get the number of contained entities
local totalContainedEntities = #data.results.bindings
if totalContainedEntities > 0 then
-- Create a table to keep track of seen IDs
local seenEquationIDs = {}
-- Loop through the bindings
for index = 1, totalContainedEntities do
local item = data.results.bindings[index]
local itemLabel = nil
if item and item.Formula and item.Formula.value
then
itemLabel = item.Formula.value
end
local equationIDURL = item.IDFormula.value
local equationID = equationIDURL:match(".*/(.-)$") or equationIDURL -- Extracts the part after the last '/'
-- Check if we’ve already seen this ID
if not seenEquationIDs[equationID] then
-- mark it as seen to avoid duplicates
seenEquationIDs[equationID] = true
local instances = getInstanceOf(equationID)
for _, id in ipairs(instances) do
if(id == "Q6481152") then
-- if is about a mathematical expression (Q6481152) that is contained
local numericId = equationID:sub(2) -- remove "Q" to get "6674540"
local urlRendered = p.canonicalLink(equationID)
local equationWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(itemLabel))
-- add new row that starts with equationWithUrl
table.insert(containedEntities, string.format('| %s ', equationWithUrl))
local equation = mw.wikibase.getEntity(equationID)
-- Get all statements for property P989 (defining formula)
local definingFormulas = equation:getBestStatements('P989')
-- Check if there are any
if definingFormulas and #definingFormulas > 0 then
for i = 1, #definingFormulas do
local definingFormula = definingFormulas[i]
local value = definingFormula.mainsnak.datavalue.value
mathTag = frame:extensionTag{
name = "math",
content = value
}
if(i == 1) then
-- for the first mathematical expression (value of defining formula) concatenate it next to the equation label (with URL)
containedEntities[#containedEntities] = containedEntities[#containedEntities] .. string.format('|| %s ', mathTag)
else
-- for every other mathematical expression (value of defining formula) create a new row
table.insert(containedEntities, string.format('| || %s', mathTag))
end
local qualValue = item.qualValueLabel or item.qualValue or ""
local qualValueWithUrl = nil
-- Skip qualifier P146 completely
if item.qualProp and item.qualProp.value then
local qualPid = item.qualProp.value:match("P%d+$")
if qualPid == "P146" then
else
local qualValue = item.qualValueLabel or item.qualValue or ""
local qualValueWithUrl = ""
if qualValue ~= "" then
local qv = item.qualValue
-- Only render if it’s not a numeric-looking string
if not (qv.type == "string" and tonumber(qv.value)) then
if qv.type == "wikibase-entityid" then
-- entity → clickable link
qualValueWithUrl = string.format(
'[[%s|%s]]',
qv.value.id,
item.qualValueLabel and item.qualValueLabel.value or qv.value.id
)
elseif qv.type == "quantity" then
-- numeric qualifier → optional: skip or format
qualValueWithUrl = mw.ustring.gsub(qv.value.amount, "^%+", "")
elseif qv.type == "time" then
-- time qualifier
qualValueWithUrl = tostring(qv.value.time)
else
-- safe fallback for strings, monolingualtext, etc.
qualValueWithUrl =
(item.qualValueLabel and item.qualValueLabel.value)
or tostring(qv.value)
end
-- Only append if there’s something meaningful
if qualValueWithUrl and qualValueWithUrl ~= "" then
containedEntities[#containedEntities] =
containedEntities[#containedEntities] .. string.format('|| %s ', qualValueWithUrl)
end
end
end
end
end
-- go over defining formulas
end
else
-- local qualValue = item.qualValueLabel or item.qualValue or ""
-- local qualValueWithUrl = ""
-- if qualValue ~= "" then
-- qualValueWithUrl = string.format('[%s %s]', tostring(item.qualValue.value), tostring(item.qualValueLabel.value))
-- end
-- if qualValue ~= "" then
-- local row = "| " .. equationWithUrl .. " || " .. qualValueWithUrl
-- table.insert(containedEntities, row)
-- else
-- table.insert(containedEntities, "| " .. equationWithUrl)
-- end
end
quantitySymbolNameIdPairs = p.extractQuantities(equationID)
if type(quantitySymbolNameIdPairs) == "table" then
-- Accessing the stored pairs
for i, pair in ipairs(quantitySymbolNameIdPairs) do
local pairFirstValue = pair[1]
local pairFirstValue = mw.text.decode(pairFirstValue or "")
local quantitySymbolMathTag = frame:extensionTag{
name = "math",
content = pairFirstValue
}
-- Construct the Portal URL
local numericId = pair[3]:sub(2) -- remove "Q" to get "6674540"
local instances = getInstanceOf(pair[3])
-- Define a set of allowed IDs for Quantity
local allowedQuantityIds = {
Q6534245 = true,
Q6534237 = true,
}
local url = ""
for _, id in ipairs(instances) do
if allowedQuantityIds[id] then
-- if the symbol is a quantity kind or a quantity
-- Construct the Portal URL
url = "https://portal.mardi4nfdi.de/wiki/Quantity:"
elseif(id == "Q6481152") then
-- if is about a mathematical expression (Q6481152) that is contained
url = "https://portal.mardi4nfdi.de/wiki/Formula:"
end
-- go over values of instances
end
-- local fullUrl = url .. numericId
local fullUrl = p.canonicalLink(pair[3])
local labelWithUrl = string.format('[%s %s]', tostring(fullUrl), tostring(pair[2]))
table.insert(containedEntities, "| " .. " || ".. quantitySymbolMathTag .. " represents " .. labelWithUrl)
end
table.insert(containedEntities, "| " .. " " )
else
end
elseif(id == "Q68663") then
-- if is about a model (Q68663) that is contained
local numericId = equationID:sub(2) -- remove "Q" to get "6674540"
-- local urlRendered = "https://portal.mardi4nfdi.de/wiki/Model:" .. numericId
local urlRendered = p.canonicalLink(equationID)
local modelWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(itemLabel))
table.insert(containedEntities, "| " .. modelWithUrl)
elseif(id == "Q6534237") then
-- if is about a quantity (Q6534237) that is contained
local numericId = equationID:sub(2) -- remove "Q" to get "6674540"
-- local urlRendered = "https://portal.mardi4nfdi.de/wiki/Quantity:" .. numericId
local urlRendered = p.canonicalLink(equationID)
local quantityWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(itemLabel))
-- add new row that starts with quantityWithUrl
table.insert(containedEntities, string.format('| %s ', quantityWithUrl))
local equation = mw.wikibase.getEntity(equationID)
-- Get all statements for property P989 (defining formula)
local definingFormulas = equation:getBestStatements('P989')
-- Check if there are any
if definingFormulas and #definingFormulas > 0 then
-- go over defining formula values (if multiple)
for i = 1, #definingFormulas do
local definingFormula = definingFormulas[i]
local value = definingFormula.mainsnak.datavalue.value
mathTag = frame:extensionTag{
name = "math",
content = value
}
if(i == 1) then
-- for the first mathematical expression (value of defining formula) concatenate it next to the equation label (with URL)
containedEntities[#containedEntities] = containedEntities[#containedEntities] .. string.format('|| %s ', mathTag)
else
-- for every other mathematical expression (value of defining formula) create a new row
table.insert(containedEntities, string.format('| || %s', mathTag))
end
local qualValue = item.qualValueLabel or item.qualValue or ""
local qualValueWithUrl = ""
if qualValue ~= "" then
qualValueWithUrl = string.format('[%s %s]', tostring(item.qualValue.value), tostring(item.qualValueLabel.value))
containedEntities[#containedEntities] = containedEntities[#containedEntities] .. string.format('|| %s ', qualValueWithUrl)
end
-- go over defining formulas
end
else
--local qualValue = item.qualValueLabel or item.qualValue or ""
--if qualValue ~= "" then
-- local row = "| " .. quantityWithUrl .. " || " .. qualValue.value
-- table.insert(containedEntities, row)
-- -- table.insert(containedEntities, "| " .. quantityWithUrl, qualValue.value )
--else
-- table.insert(containedEntities, "| " .. quantityWithUrl)
-- end
end
quantitySymbolNameIdPairs = p.extractQuantities(equationID)
if type(quantitySymbolNameIdPairs) == "table" then
-- Accessing the stored pairs
for i, pair in ipairs(quantitySymbolNameIdPairs) do
local pairFirstValue = pair[1]
local pairFirstValue = mw.text.decode(pairFirstValue or "")
local quantitySymbolMathTag = frame:extensionTag{
name = "math",
content = pairFirstValue
}
-- Construct the Portal URL
local url = "https://portal.mardi4nfdi.de/wiki/Quantity:"
local numericId = pair[3]:sub(2) -- remove "Q" to get "6674540"
-- local fullUrl = url .. numericId
local fullUrl = p.canonicalLink(pair[3])
local labelWithUrl = string.format('[%s %s]', tostring(fullUrl), tostring(pair[2]))
table.insert(containedEntities, "| " .. " || ".. quantitySymbolMathTag .. " represents " .. labelWithUrl)
end
table.insert(containedEntities, "| " .. " " )
else
end
-- if is about a quantity (Q6534237) that is contained
end
-- Loop through the instances
end
-- if not seen
end
-- Loop through the bindings
end
-- Construct the Wikitext table
local wikitextTable = [[
{| class="wikitable" style="table-layout: auto;"
]] .. table.concat(containedEntities, "\n|-\n") .. "\n|}"
return wikitextTable
else
return "No mathematical expressions found."
end
end
function p.extractQuantities(qid)
-- Property ID for in defining formula
local pidInDefiningFormula = "P983"
-- Property ID for the (qualifier) symbol represents
local pidSymbolRepresents = "P984"
-- Attempt to retrieve entity data
local entity = mw.wikibase.getEntity(qid)
if not entity or not entity.claims[pidInDefiningFormula] then
return "No formulation found"
end
local inDefiningFormulaClaims = entity.claims[pidInDefiningFormula]
local count = 0
-- Table to store pairs of quantity symbol and quantity name
local quantitySymbolNameIdPairs = {}
for _ in pairs(inDefiningFormulaClaims or {}) do
count = count + 1
-- Get the quantity symbol
local quantitySymbol = inDefiningFormulaClaims[count].mainsnak.datavalue.value
if not quantitySymbol then
return "No valid symbol found"
end
quantity = inDefiningFormulaClaims[count].qualifiers[pidSymbolRepresents][1].datavalue.value.text
quantityId = inDefiningFormulaClaims[count].qualifiers[pidSymbolRepresents][1].datavalue.value.id
local quantityName = mw.wikibase.label(quantityId)
-- Insert pair into the table
table.insert(quantitySymbolNameIdPairs, {quantitySymbol, quantityName, quantityId})
end
return quantitySymbolNameIdPairs
end
function p.get_instances_of_target(frame)
local item = frame.args[1] -- e.g., "Q12345"
local target_q = frame.args[2] or "Q6775701"
if not item then return "" end
local entity = mw.wikibase.getEntity(item)
if not entity then return "" end
local results = {}
-- get label and make link
local itemLabel = mw.wikibase.getLabel(item) or item
local itemUrlRendered = p.canonicalLink(item)
local itemWithUrl = string.format('[%s %s]', tostring(itemUrlRendered), tostring(itemLabel))
local p31_claims = entity.claims["P31"] or {}
for _, claim in ipairs(p31_claims) do
local mainsnak = claim.mainsnak
if mainsnak.datatype == "wikibase-item" and mainsnak.datavalue then
local value_id = mainsnak.datavalue.value.id
local value_entity = mw.wikibase.getEntity(value_id)
if value_entity then
local value_p31_claims = value_entity.claims["P31"] or {}
for _, vc in ipairs(value_p31_claims) do
local v_sn = vc.mainsnak
if v_sn.datatype == "wikibase-item" and v_sn.datavalue then
local v_id = v_sn.datavalue.value.id
if v_id == target_q then
-- get label and make link
local label = mw.wikibase.getLabel(value_id) or value_id
local urlRendered = p.canonicalLink(value_id)
local valueWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(label))
table.insert(results, '| ' .. itemWithUrl .. ' || ' .. valueWithUrl)
break -- stop after first match
end
end
end
end
end
end
-- return as line-separated list
-- return table.concat(results, "<br />")
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" ..
"! Computational Task\n! Algorithmic Task\n" .. -- header row
"|-\n" .. -- separator before first data row
table.concat(results, "\n|-\n") ..
"\n|}"
return wikitextTable
end
return p