Module:HelperMethods: Difference between revisions
From MaRDI portal
No edit summary Tag: Reverted |
No edit summary Tag: Reverted |
||
| Line 425: | Line 425: | ||
local supers = { | local supers = { | ||
["0"] = "⁰", ["1"] = "¹", ["2"] = "²", ["3"] = "³", | ["0"]="⁰",["1"]="¹",["2"]="²",["3"]="³",["4"]="⁴", | ||
["5"]="⁵",["6"]="⁶",["7"]="⁷",["8"]="⁸",["9"]="⁹", | |||
["n"]="ⁿ",["k"]="ᵏ",["i"]="ⁱ",["j"]="ʲ",["m"]="ᵐ" | |||
} | } | ||
local subs = { | local subs = { | ||
["0"] = "₀", ["1"] = "₁", ["2"] = "₂", ["3"] = "₃", | ["0"]="₀",["1"]="₁",["2"]="₂",["3"]="₃",["4"]="₄", | ||
["5"]="₅",["6"]="₆",["7"]="₇",["8"]="₈",["9"]="₉" | |||
} | } | ||
local function to_super(s) | |||
return (s:gsub(".", function(c) return supers[c] or c end)) | |||
end | |||
local function strip_latex(s) | local function strip_latex(s) | ||
s = s:gsub("\\%a+%b{}", function(cmd) | s = s:gsub("\\%a+%b{}", function(cmd) | ||
return cmd:match("{(.*)}") | return cmd:match("{(.*)}") | ||
end) | end) | ||
s = s:gsub("\\%a+", "") | s = s:gsub("\\%a+", "") | ||
s = s:gsub("[{}]", "") | s = s:gsub("[{}]", "") | ||
s = s:gsub("(%a)_(%d)", function(base, sub) | s = s:gsub("(%a)_(%d)", function(base, sub) | ||
return base .. (subs[sub] or sub) | return base .. (subs[sub] or sub) | ||
end) | end) | ||
s = s:gsub("%s+", " ") | s = s:gsub("%s+", " ") | ||
return s | return s | ||
end | |||
local function convert_exponents(txt) | |||
txt = txt:gsub("(%d)%^{(%w+)}", function(base, exp) | |||
return base .. to_super(exp) | |||
end) | |||
txt = txt:gsub("(%d)%^(%w+)", function(base, exp) | |||
return base .. to_super(exp) | |||
end) | |||
return txt | |||
end | end | ||
| Line 463: | Line 473: | ||
if type(text) ~= "string" then text = tostring(text) end | if type(text) ~= "string" then text = tostring(text) end | ||
text = text:gsub("%$(.-)%$", function(tex) | text = text:gsub("%$(.-)%$", function(tex) | ||
local base, exp = tex:match("^(%a)%^(%d)$") | local base, exp = tex:match("^(%a)%^(%d)$") | ||
| Line 472: | Line 481: | ||
end) | end) | ||
text = text:gsub("\\%((.-)\\%)", function(inner) | text = text:gsub("\\%((.-)\\%)", function(inner) | ||
return strip_latex(inner) | return strip_latex(inner) | ||
end) | end) | ||
text = text:gsub("\\%[(.-)\\%]", function(inner) | text = text:gsub("\\%[(.-)\\%]", function(inner) | ||
return strip_latex(inner) | return strip_latex(inner) | ||
end) | end) | ||
text = convert_exponents(text) | |||
text = text:gsub("%s+", " "):gsub("^%s*(.-)%s*$", "%1") | text = text:gsub("%s+", " "):gsub("^%s*(.-)%s*$", "%1") | ||
Revision as of 13:44, 25 November 2025
Documentation for this module may be created at Module:HelperMethods/doc
------------------------------------------------------------------------------------
-- HelperMethods --
-- --
-- This module includes a number of helper functions for dealing with lists, --
-- e.g. in the person template. It is a meta-module, meant to be called from --
-- other Lua modules, and should not be called directly from #invoke. --
------------------------------------------------------------------------------------
local M = {}
-- Required modules for SPARQL queries and HTML table generation
local sparql = require('SPARQL')
local mwHtml = require('mw.html')
-- Return a string if an item's title is not available
local titleNotAvailablePageName = "TitleNotAvailable"
function M.titleNotAvailableStr(arg)
local entity
if type(arg) == "table" and arg.args then
-- Called from template (e.g. Template:Publication)
entity = arg.args[1]
elseif type(arg) == "string" then
-- Called from another module
entity = arg
else
-- No parameter given
entity = "Title"
end
return entity .. " not available ([[" .. titleNotAvailablePageName .. "|Why is that?]])"
end
-- Utility function to trim and lowercase a string
function M.trimAndLower(str)
if str == nil then return nil end
str = str:gsub("^%s*(.-)%s*$", "%1")
return str:lower()
end
-- Utility function to count number of results in JSON answer of the SPQARL query
function M.countElementsInBindings(bindings)
if not bindings then return 0 end
local count = 0
while bindings[count] do
count = count + 1
end
return count
end
-- This function will replace spaces with + and encode other non-alphanumeric
-- characters into their percent-encoded representations, making the string
-- safe to use in a URL.
function M.urlencode(str)
if str then
str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^%w %-%_%.%~])",
function (c) return string.format("%%%02X", string.byte(c)) end)
str = string.gsub(str, " ", "+")
end
return str
end
-- Function to convert JSON results into a Lua table
function M.convertJsonToTable(jsonResults)
local resultsTable = {}
if jsonResults and jsonResults.results and jsonResults.results.bindings then
local bindings = jsonResults.results.bindings
for j = 0, #bindings do
local row = {}
for key, value in pairs(bindings[j]) do
table.insert(row, value.value)
end
table.insert(resultsTable, row)
end
end
return resultsTable
end
-- Function to convert JSON results into a Lua table with specified field order
function M.convertJsonToTableOrdered(jsonResults, fieldOrder)
local resultsTable = {}
if jsonResults and jsonResults.results and jsonResults.results.bindings then
local bindings = jsonResults.results.bindings
-- Iterate through each result row in the bindings
for j = 0, #bindings do
local row = {}
for _, fieldName in ipairs(fieldOrder) do
-- Extract the value from the current binding
local value = bindings[j][fieldName]
if value and value.value then
table.insert(row, value.value)
else
-- Use " " for missing or empty fields
table.insert(row, ' ')
end
end
table.insert(resultsTable, row)
end
end
return resultsTable
end
function M.makeWikiLinkOLD(link, name)
if string.sub(link, 1, 34) == "https://portal.mardi4nfdi.de/wiki/" then
return "[[" .. string.sub(link, 35) .. "|" .. name .. "]]"
end
return "[" .. link .. " " .. name .. "]"
end
function M.makeWikiLink(link, name)
local baseWiki = "https://portal.mardi4nfdi.de/wiki/"
local baseEntity = "https://portal.mardi4nfdi.de/entity/"
-- Case 1: Workaround for the "not available" info links -> return external link
if string.find(name, titleNotAvailablePageName) then
return "[" .. link .. " " .. name .. "]"
end
-- Case 2: full wiki URL -> strip prefix
if string.sub(link, 1, #baseWiki) == baseWiki then
local page = string.sub(link, #baseWiki + 1):match("^[^%?]+")
return "[[" .. page .. "|" .. name .. "]]"
end
-- Case 3: entity URL -> convert to wiki/Item:Qxxx
if string.sub(link, 1, #baseEntity) == baseEntity then
local qid = string.sub(link, #baseEntity + 1)
return "[[Item:" .. qid .. "|" .. name .. "]]"
end
-- fallback
return "[" .. link .. " " .. name .. "]"
end
-- Function to convert JSON results into a comma-separated string
-- E.g. from a SPQRQL query: local jsonResults = sparql.runQuery(sparqlQuery)
-- Expected: "valueLabel" (name or description) and "value" (URL)
function M.convertJsonToCommaSeparatedList(jsonResults)
local resultsString = ""
if jsonResults and jsonResults.results and jsonResults.results.bindings then
local bindings = jsonResults.results.bindings
for i = 0, #bindings do
local binding = bindings[i]
if binding.valueLabel and binding.valueLabel.value then
if resultsString ~= "" then
resultsString = resultsString .. ", "
end
-- get value
local value = binding.value.value
-- get label
local label = binding.valueLabel.value
-- if string.find(label, "https://") then
-- label = "N/A"
-- end
local nameAndLink = "[" .. value .. " " .. label .. "]"
resultsString = resultsString .. M.makeWikiLink(value, label)
end
end
end
return resultsString
end
-- Additional function to generate the histogram data
-- colWithYear: Contains the index of the column that contains the year information. Expected format "2023-12-30"
function M.generateHistogramChartFromTable(dataTable, colWithYear)
local yearCounts = {}
for _, row in ipairs(dataTable) do
-- Extract the year from the fourth column (publication date)
local date = row[colWithYear]
if date then -- Check if the date exists
local year = date:sub(1, 4) -- Extract the year
if year ~= "" then
yearCounts[year] = (yearCounts[year] or 0) + 1
end
end
end
local years = {}
local counts = {}
for year, count in pairs(yearCounts) do
table.insert(years, year)
table.insert(counts, count)
end
-- Sort the years to maintain chronological order
table.sort(years)
local sortedCounts = {}
for _, year in ipairs(years) do
table.insert(sortedCounts, yearCounts[year])
end
local chartData = {
type = 'bar',
data = {
labels = years, -- x-axis labels (years)
datasets = {{
label = 'Number of Publications',
data = sortedCounts, -- y-axis data (counts)
backgroundColor = 'rgba(54, 162, 235, 0.2)',
borderColor = 'rgba(54, 162, 235, 1)',
hoverBackgroundColor = 'red',
borderWidth = 1
}}
},
options = {
scales = {
y = {
beginAtZero = true
}
}
}
}
return chartData
end
-- Function to create a HTML table from a Lua table where columns are merged
-- mergeColumns: contains a list of which columns should be merged, e.g. {{1, 2}, {4}} or {{2, 4}, {1, 3}}
function M.createHtmlTableWithMergedCols(dataTable, headers, mergeColumns, itemprop)
local htmlTable = mwHtml.create('table')
htmlTable:addClass('wikitable'):attr('border', '1')
htmlTable:addClass('sortable') -- This line ensures your table has the 'sortable' class
local headerRow = htmlTable:tag('tr')
-- Use the provided headers
for _, header in ipairs(headers) do
headerRow:tag('th'):wikitext(header)
end
for _, row in ipairs(dataTable) do
if not string.find(row[1], "/entity/statement/") then
local dataRow = htmlTable:tag('tr')
for _, cols in ipairs(mergeColumns) do
local combinedData
if #cols == 1 then
-- If only one column index is provided, use it as is, default to "N/A" string if nil
combinedData = row[cols[1]] or "N/A"
else
-- If two column indices are provided, merge them, defaulting to "N/A" string if nil
local col1Data = row[cols[1]] or "N/A"
local col2Data = row[cols[2]] or "N/A"
-- Check if col1Data is " "
if col1Data == " " then
combinedData = col2Data
-- Check if both col1Data and col2Data are " "
elseif col1Data == " " and col2Data == " " then
combinedData = " "
else
-- combinedData = '[' .. col1Data .. ' ' .. col2Data .. ']'
combinedData = M.makeWikiLink(col1Data, col2Data)
end
if itemprop then
combinedData = combinedData .. ' <link itemprop="' .. itemprop .. '" href="' .. col1Data .. '"/> '
end
end
dataRow:tag('td'):wikitext(combinedData)
end
end
end
-- mw.log(htmlTable)
return tostring(htmlTable)
end
-- Helper function to convert table contents to a single string
function tableToString(t)
local stringParts = {}
for key, value in pairs(t) do
if type(value) == 'string' then
table.insert(stringParts, value)
elseif type(value) == 'table' then
-- Recursively convert nested tables to string
table.insert(stringParts, tableToString(value))
elseif type(value) == 'number' or type(value) == 'boolean' then
-- Convert numbers and booleans to string directly
table.insert(stringParts, tostring(value))
end
end
return table.concat(stringParts, " ") -- Combine with a space as separator
end
-- Function to convert text to JSON valid
function M.validJSON(text)
-- Ensure text is always a string (handles nil cases)
text = tostring(text or "")
-- Use mw.text.jsonEncode to escape special characters
return mw.text.jsonEncode(text)
end
-- Function to convert Markdown text to Mediawiki format
function M.markdownToMediawiki(mdText)
-- Check if the input is a table and convert it
if type(mdText) == 'table' then
mdText = tableToString(mdText)
elseif type(mdText) ~= 'string' then
error("Expected a string or a table, got " .. type(mdText))
end
-- Process the string to convert Markdown to MediaWiki formatting
-- Replace newline and tab placeholders
mdText = string.gsub(mdText, "\\N", "\n")
mdText = string.gsub(mdText, "\\T", "\t")
-- Convert bold markdown (**text**) to MediaWiki bold ('''text''')
mdText = string.gsub(mdText, "%*%*(.-)%*%*", "'''%1'''")
-- Convert markdown links [text](url) to MediaWiki [url text]
mdText = string.gsub(mdText, "%[(.-)%]%((.-)%)", "[%2 %1]")
-- Convert headings: match at start of line and beginning of string
mdText = string.gsub(mdText, "\n### (.-)\n", "\n=== %1 ===\n")
mdText = string.gsub(mdText, "\n## (.-)\n", "\n== %1 ==\n")
mdText = string.gsub(mdText, "\n# (.-)\n", "\n= %1 =\n")
mdText = string.gsub(mdText, "^### (.-)\n", "=== %1 ===\n")
mdText = string.gsub(mdText, "^## (.-)\n", "== %1 ==\n")
mdText = string.gsub(mdText, "^# (.-)\n", "= %1 =\n")
return mdText
end
-- Created a string containing all values for a given property of a given item
function M.getValuesForProperty(frame)
-- Ensure text is always a string (handles nil cases)
local target1 = tostring(frame.args[1] or "")
local PID = tostring(frame.args[2] or "")
-- Constructing the SPARQL query with dynamic entity target1
local sparqlQuery = [[
PREFIX target1: <https://portal.mardi4nfdi.de/entity/]] .. target1 .. [[>
PREFIX wdt: <https://portal.mardi4nfdi.de/prop/direct/>
PREFIX wd: <https://portal.mardi4nfdi.de/entity/>
SELECT ?property ?propertyLabel ?value ?valueLabel
WHERE {
target1: wdt:]] .. PID .. [[ ?value .
OPTIONAL {
?value rdfs:label ?valueLabel .
FILTER(LANG(?valueLabel) = "en")
}
BIND(wd:]] .. PID .. [[ AS ?property)
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}
]]
-- mw.log( sparqlQuery )
-- Executing the SPARQL query and retrieving results in JSON format
local jsonResults = sparql.runQuery(sparqlQuery)
-- mw.logObject(jsonResults)
-- Handle error in SPARQL query execution
if jsonResults and jsonResults.error then
mw.log("Error in SPARQL query: " .. tostring(jsonResults.error))
return nil
end
return jsonResults
end
-- Returns a list of the values for this property.
-- To test:
-- local HM = require("Module:HelperMethods")
-- local fakeFrame = {args = {[1] = "Q6767920", [2] = "P56"}}
-- result = HM.getValuesForPropertyList(fakeFrame)
-- mw.log(result)
function M.getValuesForPropertyList(frame)
local target1 = frame.args[1]
local PID = frame.args[2]
target1 = tostring(target1 or "")
if not target1 or target1 == '' then
return "No records found"
end
local fakeFrame = { args = {[1] = target1, [2] = PID } }
jsonResults = M.getValuesForProperty( fakeFrame )
if not jsonResults then
return "Could not fetch data."
end
if not jsonResults.results then
return "Could not fetch any data."
end
if M.countElementsInBindings(jsonResults.results.bindings) == 0 then
return "No records found."
end
local list = M.convertJsonToCommaSeparatedList(jsonResults)
-- mw.log(licenseList)
return list
end
local supers = {
["0"]="⁰",["1"]="¹",["2"]="²",["3"]="³",["4"]="⁴",
["5"]="⁵",["6"]="⁶",["7"]="⁷",["8"]="⁸",["9"]="⁹",
["n"]="ⁿ",["k"]="ᵏ",["i"]="ⁱ",["j"]="ʲ",["m"]="ᵐ"
}
local subs = {
["0"]="₀",["1"]="₁",["2"]="₂",["3"]="₃",["4"]="₄",
["5"]="₅",["6"]="₆",["7"]="₇",["8"]="₈",["9"]="₉"
}
local function to_super(s)
return (s:gsub(".", function(c) return supers[c] or c end))
end
local function strip_latex(s)
s = s:gsub("\\%a+%b{}", function(cmd)
return cmd:match("{(.*)}")
end)
s = s:gsub("\\%a+", "")
s = s:gsub("[{}]", "")
s = s:gsub("(%a)_(%d)", function(base, sub)
return base .. (subs[sub] or sub)
end)
s = s:gsub("%s+", " ")
return s
end
local function convert_exponents(txt)
txt = txt:gsub("(%d)%^{(%w+)}", function(base, exp)
return base .. to_super(exp)
end)
txt = txt:gsub("(%d)%^(%w+)", function(base, exp)
return base .. to_super(exp)
end)
return txt
end
function M.cleanTitle(frame)
local text = frame.args[1] or ""
if type(text) ~= "string" then text = tostring(text) end
text = text:gsub("%$(.-)%$", function(tex)
local base, exp = tex:match("^(%a)%^(%d)$")
if base and exp and supers[exp] then
return base .. supers[exp]
end
return tex
end)
text = text:gsub("\\%((.-)\\%)", function(inner)
return strip_latex(inner)
end)
text = text:gsub("\\%[(.-)\\%]", function(inner)
return strip_latex(inner)
end)
text = convert_exponents(text)
text = text:gsub("%s+", " "):gsub("^%s*(.-)%s*$", "%1")
return text
end
return M