Module:QualityTable
From Fallen London Wiki
Documentation for this module may be created at Module:QualityTable/doc
-- This module takes a quality as an argument, and returns a table comparing
-- all the actions that gain that quality in some fashion.
-- Unfortunately, there's no way (that I can find) to expand the members of a
-- category from within Lua. However, we *can* find out the *count* of the
-- members, and we can also expand the category *onto the rendered page* using
-- <categorytree>. So the overall strategy is to hold a pre-rendered table
-- as the "data" argument, and do a lightweight check to see if the count
-- of rows matches the number of elements in the category.
-- If the counts don't match, we prompt the user to begin the update process.
-- This consists of several rounds of copying the rendered output from the
-- preview screen over the template. This serves as a sort of "poor man's
-- recursion," allowing us to work around limitations like the inability to
-- access the output of <categorytree> and limits on the number of calls to
-- frame:getContent().
-- Thus, this module/template doesn't act like a normal template, but more
-- like a table that has a built-in way of updating itself quickly.
-- That said, this is very much work-in-progress, and doesn't function
-- correctly yet.
local p = {}
-- Maximum # of expensive calls we'll make, generally to frame:getContent().
-- If we go over the limit of 100 (on the whole page), the script will be
-- aborted with no ability to trap the error. We leave some extra room to
-- allow for other expensive functions on the page.
local EXPENSIVE_CALL_MAX = 90
-- Useful helper for formatting error text.
function format_error(err)
return "<span style='color:red'>'''" .. err .. "'''</span>"
end
-- This wiki's version of Scribunto isn't very up to date, use this to find out
-- what functions are available. TODO: Delete when everything's finished.
function table_keys(tab)
local keys = {}
local n = 0
for k, v in pairs(tab) do
n = n + 1
keys[n] = "*" .. k .. "\n"
end
return table.concat(keys, "")
end
-- Returns the string to prepend before the table, informing the user how
-- to update (if it is time to do so).
function get_update_text(current_count, true_count)
if current_count == true_count then
return ""
end
return string.format([[<div class="mw-collapsible mw-collapsed">
''This table is out of date: It has %d rows vs. %d members of the category.''
<div class="mw-collapsible-content">
To get rid of this message, edit this page using the source editor and add
update=1 as an argument to the template. Then '''preview''' the page and follow
the instructions: you'll have to replace the template with what it generates a
couple of times before you can submit.
</div></div>]],
current_count,
true_count)
end
function get_merged_args(frame)
local args = {}
for k, v in pairs(frame:getParent().args) do
args[k] = v
end
for k, v in pairs(frame.args) do -- Override with local arguments
args[k] = v
end
return args
end
function handle_update(frame, args, category_name)
-- The list of valid arguments (arguments we will pass along), in
-- the order we'll write them.
local valid_args = {"update", "name", "expensive_calls",
"num_processed", "data", "actions"}
-- Figure out where we're at in the update process.
if (args["actions"] or "") == "" then
-- No actions, fill out <categorytree> and also reread content for as
-- many rows as we can.
-- It's OK to modify args, since handle_update is a tail-call.
args["actions"] = frame:preprocess(
'<categorytree mode="pages" hideroot="on">' ..
category_name .. "</categorytree>")
else
-- Use num_processed to figure out which rows are already processed,
-- reconcile that against the action list, read content for new rows,
-- and possibly clean up args if we're done now.
local referenced_actions = mw.text.split(args["actions"], "\n", true)
local result_table = {}
for i, v in ipairs(referenced_actions) do
result_table[i] = mw.title.new(v):getContent()
end
args["data"] = frame:preprocess(
"<nowiki>" .. table.concat(result_table, "\n") .. "</nowiki>")
end
local flattened_args = {}
for i, k in ipairs(valid_args) do
if args[k] then
table.insert(flattened_args, k .. "=" .. args[k])
end
end
return string.format([[
'''YOU ARE NOT DONE YET!''' Replace the template you are editing with the
code that appears below. You may have to repeat this procedure multiple times.
This is step %d/%d. '''XXXXX BEGIN COPY-PASTE XXXXX'''
{{#invoke:QualityTable|main|%s}}
'''XXXXX END COPY-PASTE XXXXX''']],
1, 2, table.concat(flattened_args, "|"))
end
-- Wrap the real main in an error handler so we get useful error messages
-- when things go wrong.
function p.main(frame)
success, result = pcall(p.raw_main, frame)
if success then
return result
else
return format_error("Script error:" .. result)
end
end
-- The true main function. You can substitute this directly for main, but don't.
function p.raw_main(frame)
local args = get_merged_args(frame)
EXPENSIVE_CALL_MAX = args["expensive_calls"] or EXPENSIVE_CALL_MAX
local quality_item_name = args["name"]
if quality_item_name == nil then
return format_error("Module:QualityTable requires a name= argument!")
end
local bare_category_name = quality_item_name .. " Sources"
local category_name = "Category:" .. bare_category_name
if args["update"] ~= nil then
-- Abort further processing
return handle_update(frame, args, category_name)
end
local num_in_category = mw.site.stats.pagesInCategory(bare_category_name, "pages")
return get_update_text(#referenced_actions, num_in_category) ..
frame:preprocess(
"<pre><nowiki>\n" .. (args[data] or "") .. "\n</nowiki></pre>")
end
return p