Module:Sandbox/SMWTable
Jump to navigation
Jump to search
Documentation for Module:Sandbox/SMWTable does not exist yet [create] (How does this work?)
local Icon = require("Module:Icon")
local yesno = require("Module:Yesno")
---@class SMWTableColumn A column that displays property values of Semantic Mediawiki entities.
---@field label string Header label for the column
---@field printouts {[string]: string} Map of property names to printout options used for the property; pagename property is keyed by empty string
---@field render fun(entity: table): string Function that renders the column's value for each queried entity
---@class SMWTable A table of Semantic Mediawiki entities, composed of multiple columns.
---@field selection string SMW query selection string
---@field printouts {[string]: string} Map of property names to printout options used for the property; pagename property is keyed by empty string
---@field columnLabels string[] Array of column header labels
---@field columnRenderers (fun(entity: table): string)[] Array of column render functions in the order they will appear
---@field options table Additional SMW query options for e.g. sorting etc
local SMWTable = {}
SMWTable.__index = SMWTable
---@param selection string Query selection for this table
---@return SMWTable
function SMWTable.new(selection, options)
---@type SMWTable
local instance = {
-- from constructor args
selection = selection,
options = options or {},
-- populated by :column()
printouts = {},
columnLabels = {},
columnRenderers = {},
}
setmetatable(instance, SMWTable)
return instance
end
-- Methods
---@param column SMWTableColumn Column definition
---@return self
function SMWTable:column(column)
-- apply printouts required for column
for propertyName, formatString in pairs(column.printouts) do
local existingFormat = self.printouts[propertyName]
if existingFormat then
if existingFormat ~= formatString then
error("Two columns requested different formats for property " .. propertyName .. ": \"" .. existingFormat .. "\", \"" .. formatString .. "\"")
end
else
self.printouts[propertyName] = formatString
end
end
table.insert(self.columnLabels, column.label)
table.insert(self.columnRenderers, column.render)
return self
end
---@param self SMWTable
---@return string
function SMWTable:__tostring()
local query = {self.selection}
for propertyName, formatString in pairs(self.printouts) do
table.insert(query, "?" .. propertyName .. formatString)
end
for key, value in pairs(self.options) do
query[key] = value
end
-- Execute query
local results = mw.smw.ask(query)
if not results or #results == 0 then
return "No results"
end
-- Render table
local out = "{| class=\"table\"\n"
-- Render table header
for _, label in ipairs(self.columnLabels) do
out = out .. "! " .. label .. "\n"
end
-- render rows
for _, item in ipairs(results) do
out = out .. "|-\n"
for _, render in ipairs(self.columnRenderers) do
out = out .. "|" .. tostring(render(item)) .. "\n"
end
end
out = out .. "|}"
return out
end
-- Static methods - column helpers
---@param options string | {property: string, label?: string}
---@return SMWTableColumn
function SMWTable.textColumn(options)
if type(options) == "string" then
options = {property = options}
end
return {
label = options.label or options.property,
printouts = {
[options.property] = "",
},
render = function (item)
local value = item[options.property]
if type(value) == "table" then
return table.concat(value, ", ")
end
return tostring(item[options.property])
end
}
end
---@param options {name?: string, iconSize?: ("small" | "mid" | "big"), allowWrap?: boolean?}
---@return SMWTableColumn
function SMWTable.nameAndIconColumn(options)
options = options or {}
return {
label = "Name",
printouts = {
[""] = "=Pagename#",
["Has context"] = "",
["Has game icon"] = "#",
["Has canonical name"] = "",
["Has dye channels"] = "",
["Has game icon frame type"] = "",
},
render = function(item)
return Icon.gameIconWithLabel({
type = item['Has context'],
frameType = item['Has game icon frame type'],
icon = item['Has game icon'],
link = item['Pagename'],
size = options.iconSize or "big",
dyeCount = item['Has dye channels'],
text = item['Has canonical name'] or item['Pagename'],
allowWrap = options.allowWrap or false
})
end
}
end
local classJobAbbreviations = {
-- DoH/DoL
alchemist = "ALC",
armorer = "ARM",
blacksmith = "BSM",
botanist = "BTN",
carpenter = "CRP",
culinarian = "CUL",
fisher = "FSH",
goldsmith = "GSM",
leatherworker = "LTW",
miner = "MIN",
weaver = "WVR",
-- Combat classes
marauder = "MRD",
gladiator = "GLA",
pugilist = "PGL",
lancer = "LNC",
archer = "ARC",
conjurer = "CNJ",
thaumaturge = "THM",
arcanist = "ACN",
rogue = "ROG",
-- Combat jobs
["dark knight"] = "DRK",
["red mage"] = "RDM",
["black mage"] = "BLM",
["white mage"] = "WHM",
["blue mage"] = "BLM",
astrologian = "AST",
bard = "BRD",
dragoon = "DRG",
monk = "MNK",
machinist = "MCH",
ninja = "NIN",
samurai = "SAM",
scholar = "SCH",
summoner = "SMN",
paladin = "PLD",
warrior = "WAR",
gunbreaker = "GNB",
dancer = "DNC",
reaper = "RPR",
sage = "SGE",
viper = "VPR",
pictomancer = "PCT",
}
function SMWTable.classJobRequirementColumn()
return {
label = "Requirement",
printouts = {
["Is for class"] = "#",
["Is for job"] = "#",
},
render = function(item)
local out = ""
local classes = item["Is for class"] or {}
if type(classes) == "string" then
classes = {classes}
end
local jobs = item["Is for job"] or {}
if type(jobs) == "string" then
jobs = {jobs}
end
for i, className in ipairs(classes) do
if i ~= 1 then
out = out .. " "
end
out = out .. "[[" .. className .. "|" .. classJobAbbreviations[className:lower()] .. "]]"
end
out = out .. " "
for i, jobName in ipairs(jobs) do
if i ~= 1 then
out = out .. " "
end
out = out .. "[[" .. jobName .. "|" .. classJobAbbreviations[jobName:lower()] .. "]]"
end
return out
end
}
end
function SMWTable.weaponDamageColumn()
return {
label = "Damage (Type)",
printouts = {
["Is for job"] = "#",
["Has weapon physical damage"] = "",
["Has weapon magic damage"] = "",
["Has hq weapon physical damage"] = "",
["Has hq weapon magic damage"] = "",
},
render = function(item)
-- only render damage if this weapon is for a job
local job = item["Is for job"]
if not job or job == "" then return "" end
job = job:lower()
local damageType = "magic"
if job == "warrior"
or job == "paladin"
or job == "monk"
or job == "dragoon"
or job == "bard"
or job == "ninja"
or job == "dark knight"
or job == "machinist"
or job == "samurai"
or job == "gunbreaker"
or job == "dancer"
or job == "reaper"
or job == "viper"
then
damageType = "physical"
end
local out = tostring(item["Has weapon " .. damageType .. " damage"])
local hqDamage = item["Has hq weapon " .. damageType .. " damage"]
if hqDamage and hqDamage ~= "" then
out = out .. " <sup style=\"font-size: 0.7em; font-weight: bold;\">" .. mw.getCurrentFrame():expandTemplate({title = "HQ"}) .. item["Has hq weapon physical damage"] .. "</sup>"
end
if damageType == "physical" then
out = out .. "[[File:Physical Damage.png|24px|link=Damage Types#Physical]]"
else
out = out .. "[[File:Magical Damage.png|24px|link=Damage Types#Magical]]"
end
return out
end
}
end
function SMWTable.materiaSlotsColumn()
return {
label = "Materia slots",
printouts = {
["Has materia slots"] = "",
["Has advanced melding"] = "",
},
render = function(item)
local out = item["Has materia slots"]
if yesno(item["Has advanced melding"]) then
out = out .. " <span style=\"color:red\" title=\"This item can undergo advanced melding\">(5)</span>"
end
return out
end
}
end
local attributes = {
"Strength",
"Dexterity",
"Intelligence",
"Mind",
"Vitality",
"Critical Hit",
"Determination",
"Direct Hit Rate",
"Skill Speed",
"Spell Speed",
"Tenacity",
"Piety",
}
local attributePrintouts = {}
for _, v in ipairs(attributes) do
attributePrintouts["Has " .. v:lower() .. " bonus"] = ""
attributePrintouts["Has hq " .. v:lower() .. " bonus"] = ""
end
attributePrintouts["Has customizable substats"] = ""
attributePrintouts["Has random substats"] = ""
function SMWTable.attributeBonusesColumn()
return {
label = "Stats and Attributes",
printouts = attributePrintouts,
render = function(item)
local out = ""
for _, attributeName in ipairs(attributes) do
local bonus = item["Has " .. attributeName:lower() .. " bonus"]
if bonus and bonus ~= "" then
out = out .. "[[" .. attributeName .. "]] +" .. bonus
local hqBonus = item["Has hq " .. attributeName:lower() .. " bonus"]
if hqBonus and hqBonus ~= "" then
out = out .. "<sup style=\"font-size: 0.7em; font-weight: bold;\">" .. mw.getCurrentFrame():expandTemplate({title = "HQ"}) .. item["Has hq " .. attributeName:lower() .. " bonus"] .. "</sup>"
end
out = out .. " "
end
end
if yesno(item["Has customizable substats"]) then
out = out .. "[[File:Item settings icon.png|24px|link=]] ''Customizable [[Attributes#Secondary_Attributes|secondary attributes]]''"
elseif yesno(item["Has random substats"]) then
out = out .. "''Random [[Attributes#Secondary_Attributes|secondary attributes]]''"
end
return out
end
}
end
-- testing
function SMWTable.main(frame)
return SMWTable.new("[[Has item type::Gunbreaker's Arm]]", {
sort = "Has level requirement,Has item level,Has canonical name",
limit = 999,
})
:column(SMWTable.nameAndIconColumn({label = "Item"}))
:column(SMWTable.textColumn({property = "Has level requirement", label = "Level"}))
:column(SMWTable.textColumn({property = "Has item level", label = "Item level"}))
:column(SMWTable.classJobRequirementColumn())
:column(SMWTable.weaponDamageColumn())
:column(SMWTable.materiaSlotsColumn())
:column(SMWTable.attributeBonusesColumn())
end
return SMWTable
--[===[ Test in console with:
= p.new("[[Has item type::Gunbreaker's Arm]][[Has level requirement::80]]", {sort = "Has level requirement,Has item level,Has canonical name"})
:column(p.nameAndIconColumn())
:column(p.textColumn({property = "Has level requirement", label = "Level"}))
:column(p.textColumn({property = "Has item level", label = "Item level"}))
:column(p.classJobRequirementColumn())
:column(p.weaponDamageColumn())
:column(p.materiaSlotsColumn())
:column(p.attributeBonusesColumn())
]===]