Module:CommonCargoQuery
Jump to navigation
Jump to search
Documentation for this module may be created at Module:CommonCargoQuery/doc
local p = {}
local cargo = mw.ext.cargo
-- Scribunto (Lua main): https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual
-- Cargo Lua support: https://www.mediawiki.org/wiki/Extension:Cargo/Other_features#Lua_support
-- Small utility for removing quotes from list of single quoted IN (...) params
function p.StripQuotes(frame)
return tostring(string.gsub(frame.args["from"], "'", ''))
end
-- Trims whitespace off of the left and right sides of a string
local function trim(str)
return str:gsub("^%s*(.-)%s*$", "%1")
end
-- Basically is null, is empty, or is whitespace
local function is_empty(str)
return str == nil or str.len(trim(str)) == 0
end
-- If a given string is empty or nil, return nil, otherwise give back the value
local function nil_or_value(str)
if is_empty(str) then
return nil
end
return str
end
-- Normally you'd do `table.insert(table, item)` to add to the end, but this
-- does not appear to be working in this Lua implementation so here's a workaround.
local function append_table(tbl, data)
tbl[#tbl + 1] = data
end
-- Split a string by comma, return each item trimmed in a table.
local function split(str)
local result = {}
for token in string.gmatch(str, "[^,]+") do
append_table(result, trim(token))
end
return result
end
-- Processes an argument clause, for example spawnLocation (all of the ones provided).
--
-- frame: The 'frame' argument provided to the entry point, used to retrieve the args.
-- argname: The name of the argument, i.e. "spawnLocation" or "name".
-- joinType: How to join the clauses together, can be "AND" or "OR".
-- clause: The clause string, including a %s. For example "spawnLocation HOLDS '%s'", don't forget to escape % with %% for 'LIKE'.
--
-- Returns all the clauses concatenated into a string surrounded by parentheses.
local function processArgClause(frame, argName, joinType, clause)
local arg = frame.args[argName]
if is_empty(arg) then return nil end
local result = {}
local items = split(arg)
for _, item in ipairs(items) do
append_table(result, string.format(clause, item))
end
-- Joins everything together with AND or OR between each clause
return "(" .. table.concat(result, " " .. joinType .. " ") .. ")"
end
-- Creates an entire query to use in the `where` argument of the cargo call.
--
-- frame: The 'frame' argument provided to the entry point, used to retrieve the args.
-- queryMapping: A table with all the possible arg names, mapped to joinType and clauses. See enemyQueryMapping for an example.
--
-- Returns a string with all clauses joined by the keyword "AND".
local function createQuery(frame, queryMapping)
local result = {}
for argName, argData in pairs(queryMapping) do
local clause = processArgClause(frame, argName, argData.joinType, argData.clause)
if clause ~= nil then
append_table(result, clause)
end
end
return table.concat(result, " AND ")
end
-- Does all of the stuff the old template would do normally in a generic way.
--
-- frame: The 'frame' argument provided to the entry point, used to retrieve the args.
-- queryMapping: A table with all the possible arg names, mapped to joinType and clauses. See enemyQueryMapping for an example.
-- tableName: Name of the Cargo table (i.e. "Enemies").
-- args: The args that would normally have been passed to cargo.
--
-- Returns the page contents to render.
local function doCommonQuery(frame, queryMapping, tableName, args)
-- Cannot use 'or' here as params provided but not given a value are still in the table (as empty string?)
local query = nil_or_value(frame.args["where"]) or createQuery(frame, queryMapping)
if string.len(query) == 0 then
return "''No query arguments were provided.''"
end
args.where = query
local results = cargo.query(tableName, args.fields, args)
-- Build the result page by instantiating all the row templates with the rows
local pageElements = {}
append_table(pageElements, frame.args["intro"] or "")
for _, row in ipairs(results) do
local rowText = frame:expandTemplate{ title = args.template, args = row }
append_table(pageElements, rowText)
end
if #pageElements == 0 then
return string.format("''There are no %s of the specified query.''", tableName)
end
append_table(pageElements, frame.args["outro"] or "")
return table.concat(pageElements)
end
--------------------------------------------------------------------------------
---- EnemyQuery ------------------------------------------------ EnemyQuery ----
-- Double % escapes a %, %s is for string
local enemyQueryMapping = {
["name"] = {
joinType = "OR",
-- clause = "name LIKE \"%%%s%%\"" -- Unable to differenciate between similar enemy variants (hurtta/heikkohurtta)
clause = "name = \"%s\""
},
["spawnLocation"] = {
joinType = "OR",
clause = "spawnLocation HOLDS \"%s\""
},
["ngplusSpawnLocation"] = {
joinType = "OR",
clause = "ngplusSpawnLocation HOLDS \"%s\""
},
["ngplus2SpawnLocation"] = {
joinType = "OR",
clause = "ngplus2SpawnLocation HOLDS \"%s\""
},
["ngplus3SpawnLocation"] = {
joinType = "OR",
clause = "ngplus3SpawnLocation HOLDS \"%s\""
},
["faction"] = {
joinType = "OR",
clause = "faction=\"%s\""
},
["notFaction"] = {
joinType = "AND",
clause = "NOT faction=\"%s\""
},
["category"] = {
joinType = "OR",
clause = "category=\"%s\""
},
["bloodMaterial"] = {
joinType = "OR",
clause = "blood LIKE \"%%%s%%\""
},
["corpseMaterial"] = {
joinType = "OR",
clause = "corpse LIKE \"%%%s%%\""
},
}
-- EnemyQuery (queries Enemies table)
-- This is a module function, referenced from the second argument to #invoke.
-- i.e. {{#invoke:CommonCargoQuery|EnemyQuery|arg1=...}}
function p.EnemyQuery(frame)
return doCommonQuery(frame, enemyQueryMapping, "Enemies", {
fields = "_pageName,image,icon,name,alias",
limit = 400,
orderBy = 'faction,_pageName,name',
template = frame.args["template"] or "EnemyQuery/row"
})
end
--------------------------------------------------------------------------------
---- SpellQuery ------------------------------------------------ SpellQuery ----
local spellQueryMapping = {
["id"] = {
joinType = "OR",
clause = "id=\"%s\""
},
["name"] = {
joinType = "OR",
clause = "name=\"%s\""
},
["nameLike"] = {
joinType = "OR",
clause = "name LIKE \"%%%s%%\""
},
["names"] = { -- Deprecated - use name instead
joinType = "OR",
clause = "name=\"%s\""
},
["type"] = {
joinType = "OR",
clause = "type=\"%s\""
},
["tier"] = {
joinType = "OR",
clause = "spellTier HOLDS \"%s\""
},
["tags"] = {
joinType = "OR",
clause = "tags HOLDS \"%s\""
},
["allOfTags"] = {
joinType = "AND",
clause = "tags HOLDS \"%s\""
},
["noneOfTags"] = {
joinType = "AND",
clause = "tags HOLDS NOT \"%s\""
},
}
-- SpellQuery (queries Spells table)
-- This is a module function, referenced from the second argument to #invoke.
-- i.e. {{#invoke:CommonCargoQuery|SpellQuery|arg1=...}}
-- pass in fields=one,two,three to specify custom field sets to return
function p.SpellQuery(frame)
return doCommonQuery(frame, spellQueryMapping, "Spells", {
fields = frame.args["fields"] or "_pageName,image,name,id,description,type",
limit = 400,
orderBy = nil_or_value(frame.args["orderBy"]) or 'type,sortKey,_pageName',
template = frame.args["template"] or "SpellQuery/row2",
})
end
--------------------------------------------------------------------------------
---- MaterialQuery ------------------------------------------ MaterialQuery ----
local materialQueryMapping = {
["name"] = {
joinType = "OR",
clause = "name LIKE \"%%%s%%\""
},
["id"] = {
joinType = "OR",
clause = "id=\"%s\""
},
["type"] = {
joinType = "OR",
clause = "type=\"%s\""
},
["tags"] = {
joinType = "OR",
clause = "tags HOLDS \"%s\""
},
}
-- MaterialQuery (queries Spells table)
-- This is a module function, referenced from the second argument to #invoke.
-- i.e. {{#invoke:CommonCargoQuery|MaterialQuery|arg1=...}}
-- pass in fields=one,two,three to specify custom field sets to return
function p.MaterialQuery(frame)
return doCommonQuery(frame, materialQueryMapping, "Materials", {
fields = frame.args["fields"] or "_pageName,image,name,id,icon,pouchIcon",
limit = frame.args["limit"] or 400,
orderBy = 'type,name',
template = frame.args["template"] or "MaterialQuery/row",
})
end
--------------------------------------------------------------------------------
---- PerkQuery -------------------------------------------------- PerkQuery ----
-- Double % escapes a %, %s is for string
local perkQueryMapping = {
["Name"] = {
joinType = "OR",
-- clause = "Name LIKE \"%%%s%%\"" -- Unable to differenciate between similar enemy variants (hurtta/heikkohurtta)
clause = "Name = \"%s\""
},
["Categories"] = {
joinType = "OR",
clause = "Categories HOLDS \"%s\""
},
["notCategories"] = {
joinType = "AND",
clause = "NOT Categories HOLDS \"%s\""
},
["OneOff"] = {
joinType = "OR",
clause = "OneOff = \"%s\""
},
["PlayerOnly"] = {
joinType = "OR",
clause = "PlayerOnly = \"%s\""
},
["Stackable"] = {
joinType = "OR",
clause = "Stackable = \"%s\""
},
["PerkPool"] = {
joinType = "OR",
clause = "PerkPool = \"%s\""
},
["MaxStack"] = {
joinType = "OR",
clause = "MaxStack = \"%s\""
},
["PoolMax"] = {
joinType = "OR",
clause = "PoolMax = \"%s\""
},
["ReappearsAfter"] = {
joinType = "OR",
clause = "ReappearsAfter = \"%s\""
},
}
-- PerkQuery (queries Perks table)
-- This is a module function, referenced from the second argument to #invoke.
-- i.e. {{#invoke:CommonCargoQuery|PerkQuery|arg1=...}}
function p.PerkQuery(frame)
return doCommonQuery(frame, perkQueryMapping, "Perks", {
fields = "_pageName,Image,Name",
limit = 400,
orderBy = ',_pageName,Name',
template = "PerkQuery/row"
})
end
return p