Module:MathModDBHelperMethods: Difference between revisions

From MaRDI portal
No edit summary
No edit summary
Line 403: Line 403:
local entityId = frame.args[1]
local entityId = frame.args[1]
     local propertyId = frame.args[2]
     local propertyId = frame.args[2]
    local entityId = frame.args[1]
      
      
     -- Validate input parameter
     -- Validate input parameter
     if not entityId or entityId == '' then
     if not entityId or entityId == '' then
         return " "
         return "Error: No entity ID provided"
     end
     end
   
 
     -- Constructing the SPARQL query with dynamic entity entityId
     -- Constructing the SPARQL query with dynamic entity entityId
    -- P1684: specialized by property id
     local sparqlQuery = [[
     local sparqlQuery = [[
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
PREFIX entityId: <https://portal.mardi4nfdi.de/entity/]] .. entityId .. [[>
SELECT ?AssumptionURL ?AssumptionLabel
SELECT ?URL ?Label ?qualPropEntity ?qualPropLabel ?qualValue ?qualValueLabel
WHERE {
WHERE {
    entityId: p:]] .. propertyId .. [[ ?statement.
entityId: p:]] .. propertyId .. [[ ?statement .
    ?statement ps:]] .. propertyId .. [[ ?AssumptionURL.
?statement ps:]] .. propertyId .. [[ ?URL .
    ?AssumptionURL rdfs:label ?AssumptionLabel.
?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 ?AssumptionLabel
ORDER BY ?Label
]]
]]


-- Executing the SPARQL query and retrieving results in JSON format
-- Executing the SPARQL query and retrieving results in JSON format
Line 426: Line 446:
-- Validate results
-- Validate results
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
if not jsonResults or not jsonResults.results or not jsonResults.results.bindings then
       return "No computational task found"
       return "No specialized research fields found"
     end
     end
local assumptions = {}
local specializedModels = {}
     -- Get the number of computational tasks
     -- Get the number of specialized research fields
local totalAssumptions = #jsonResults.results.bindings
local totalSpecModels = #jsonResults.results.bindings
-- Loop through the bindings
-- Loop through the bindings
for index = 0, totalAssumptions do
for index = 0, totalSpecModels do
     local item = jsonResults.results.bindings[index]
     local item = jsonResults.results.bindings[index]
     if not item.AssumptionLabel.value then
     if not item.Label.value then
         return "Error: Missing item.Label.value"
         return "Error: Missing item.Label.value"
     elseif not item.AssumptionURL.value then
     elseif not item.URL.value then
         return "Error: Missing item.URL.value"
         return "Error: Missing item.URL.value"
     else
     else
        local label = item.AssumptionLabel.value
    local label = item.Label.value
         local url = item.AssumptionURL.value
         local url = item.URL.value
         local numericId = url:match("Q(%d+)")
         local numericId = url:match("Q(%d+)")
    local urlRendered = "https://portal.mardi4nfdi.de/wiki/Formula:" .. numericId
        local urlRendered = "https://portal.mardi4nfdi.de/wiki/Model:" .. numericId
         local labelWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(label))
         local qualValue = item.qualValueLabel or item.qualValue or ""
        local ordinal = item.Ordinal and item.Ordinal.value or nil
        local labelWithUrl = string.format('[%s %s]', tostring(urlRendered), tostring(label))
local prefix = ordinal and (ordinal .. ". ") or ""
        if qualValue ~= "" then
table.insert(assumptions, "| " .. prefix .. labelWithUrl)
        local qualPropWithUrl = string.format('[%s %s]', tostring(item.qualPropEntity.value), tostring(item.qualPropLabel.value))
        local qualValueWithUrl = string.format('[%s %s]', tostring(item.qualValue.value), tostring(item.qualValueLabel.value))
        local row = "| " .. labelWithUrl .. " || " .. qualPropWithUrl .. " || " ..  qualValueWithUrl
    table.insert(specializedModels, row)
        else
        table.insert(specializedModels, "| " .. labelWithUrl)
        end
     end
     end
end
end


-- Construct the Wikitext table
-- Construct the Wikitext table
local wikitextTable = "{| class='wikitable'\n" .. table.concat(assumptions, "\n|-\n") .. "\n|}"
local wikitextTable = "{| class='wikitable'\n" .. table.concat(specializedModels, "\n|-\n") .. "\n|}"
return wikitextTable
return wikitextTable
end
end


return p
return p

Revision as of 10:35, 19 September 2025

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 = {}

-- 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
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
    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"}
	local dataTable = helper.convertJsonToTableOrdered(jsonResults, fieldOrder)
	
	-- Create and return HTML table from the data
    local headers = {entityType}
    local htmlTable =  helper.createHtmlTableWithMergedCols(dataTable, headers, {{1, 2}})
	
	-- 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 subclass of items
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
			-- Extract the image filename
    		local imageFilename = imageClaims[index].mainsnak.datavalue.value
			local imageLegend = imageClaims[index].qualifiers[pidMediaLegend][1].datavalue.value.text
			-- 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, '&nbsp;')  
    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.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, '&nbsp;')  
    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 model assumptions
function p.getListWithQualifiers(frame)
	local entityId = frame.args[1]
    local propertyId = frame.args[2]
    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
    -- P1684: specialized by property id
    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 specializedModels = {}
    -- Get the number of specialized research fields
	local totalSpecModels = #jsonResults.results.bindings
	-- Loop through the bindings
	for index = 0, totalSpecModels  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(urlRendered), tostring(label))
	        if qualValue ~= "" then
	        	local qualPropWithUrl = string.format('[%s %s]', tostring(item.qualPropEntity.value), tostring(item.qualPropLabel.value))
	        	local qualValueWithUrl = string.format('[%s %s]', tostring(item.qualValue.value), tostring(item.qualValueLabel.value))
	        	local row = "| " .. labelWithUrl .. " || " .. qualPropWithUrl .. " || " ..  qualValueWithUrl
    			table.insert(specializedModels, row)
	        else
        		table.insert(specializedModels, "| " .. labelWithUrl)
        	end
    	end
	end

	-- Construct the Wikitext table
	local wikitextTable = "{| class='wikitable'\n" .. table.concat(specializedModels, "\n|-\n") .. "\n|}"
	return wikitextTable
end

return p