模組:Database report
外观
--[[
Lua module for generating {{database report}} configurations.
This module provides a class-based interface to create database report configurations.
Usage from Lua:
local p = {}
p.main = function()
local Report = require('Module:Database report')
local report = Report:new()
report:setSQL("SELECT page_title FROM page LIMIT 10")
report:useWikilinks(1)
report:setInterval(7)
return report:generate()
end
return p
]]
local Report = {}
Report.__index = Report
--- Initialize a new database report instance.
-- This is the constructor method that should be called to create a new report instance.
-- @return Report A new Report instance with default settings
-- @usage local report = Report:new()
function Report:new()
local obj = {
sql = nil,
wikilinks = {},
widths = {},
comments = {},
remove_underscores = {},
hide = {},
excerpts = {},
table_style = nil,
table_class = nil,
interval = nil,
pagination = nil,
max_pages = nil,
row_template = nil,
row_template_named_params = nil,
skip_table = nil,
header_template = nil,
footer_template = nil,
postprocess_js = nil,
silent = nil,
head_content = nil
}
setmetatable(obj, self)
return obj
end
--- Set the SQL query for the database report.
-- This is a required parameter.
-- The SQL must be a valid SELECT statement.
-- @param sql string The SQL query to execute (must be non-empty)
-- @return Report Returns self for method chaining
-- @raise error if sql is nil, not a string, or empty
-- @usage report:setSQL("SELECT page_title FROM page LIMIT 10")
function Report:setSQL(sql)
if not sql or type(sql) ~= "string" or sql:match("^%s*$") then
error("SQL must be a non-empty string")
end
self.sql = sql
return self
end
--- Configure wikilinks for one or more columns in the report.
-- Wikilinks convert column values into clickable links to other pages.
-- Can be called multiple times to configure different columns.
-- @param column number|table Column number to be wikilinked, or table of column configurations
-- @param namespace number|string Optional namespace number for links. If the namespace number is in another column, use "c1" if it's in the first column, "c2" if it's in the second column, etc.
-- @param show boolean Whether the namespace prefix should be displayed in the link (default: false)
-- @return Report Returns self for method chaining
-- @usage report:useWikilinks(1) -- Simple wikilink for column 1
-- @usage report:useWikilinks(1, 0, true) -- Column 1, namespace 0, show prefix
-- @usage report:useWikilinks({{column=1, namespace="c2"}, {column=3}}) -- Multiple columns
function Report:useWikilinks(column, namespace, show)
if type(column) == "number" then
table.insert(self.wikilinks, {
column = column,
namespace = namespace,
show = show
})
elseif type(column) == "table" then
-- Support for multiple wikilinks at once
for _, link in ipairs(column) do
table.insert(self.wikilinks, link)
end
end
return self
end
--- Set the width for one or more columns in the report.
-- Controls the display width of columns using CSS width values.
-- Can be called multiple times to set different column widths.
-- @param column number|table Column number to set width for, or table of width configurations
-- @param width string CSS width value (e.g., "10em", "50px", "20%", "200px")
-- @return Report Returns self for method chaining
-- @usage report:setWidth(1, "200px") -- Set column 1 to 200px width
-- @usage report:setWidth({{column=1, width="10em"}, {column=2, width="50%"}}) -- Multiple columns
function Report:setWidth(column, width)
if type(column) == "number" and type(width) == "string" then
table.insert(self.widths, {column = column, width = width})
elseif type(column) == "table" then
-- Support for multiple widths at once
for _, w in ipairs(column) do
table.insert(self.widths, w)
end
end
return self
end
--- Set columns to be treated as comments (edit summaries or log action summaries) in the report.
-- @param ... number Variable number of column numbers to mark as comments
-- @return Report Returns self for method chaining
-- @usage report:setCommentColumns(3, 5) -- Mark columns 3 and 5 as comments
function Report:setCommentColumns(...)
for _, col in ipairs({...}) do
if type(col) == "number" then
table.insert(self.comments, col)
end
end
return self
end
--- Configure columns to have underscores removed from their values.
-- Useful for page titles where underscores should be converted to spaces. For columns with wikilinks or excerpts configured, this will be done automatically.
-- @param ... number Variable number of column numbers to remove underscores from
-- @return Report Returns self for method chaining
-- @usage report:removeUnderscores(1, 2) -- Remove underscores from columns 1 and 2
function Report:removeUnderscores(...)
for _, col in ipairs({...}) do
if type(col) == "number" then
table.insert(self.remove_underscores, col)
end
end
return self
end
--- Hide specified columns from the report display.
-- Hidden columns are still processed but not shown in the final output.
-- Useful for columns used only for processing (like namespace numbers) but not for display.
-- @param ... number Variable number of column numbers to hide
-- @return Report Returns self for method chaining
-- @usage report:hideColumns(2, 4) -- Hide columns 2 and 4 from display
function Report:hideColumns(...)
for _, col in ipairs({...}) do
if type(col) == "number" then
table.insert(self.hide, col)
end
end
return self
end
--- Configure generation of article excerpts (summaries).
-- Creates excerpts from source column that should contain page titles, and places them in a destination column.
-- @param srcColumn number Column containing page title whose excerpt should be created
-- @param destColumn number Destination column number to place the excerpt.
-- @param namespace number|string Namespace number for the page title (default: 0 for main namespace). If the namespace number is in another column, use "c1" if it's in the first column, "c2" if it's in the second column, etc.
-- @param charLimit number Optional character limit for the excerpt
-- @param charHardLimit number Optional hard character limit (truncates if exceeded)
-- @return Report Returns self for method chaining
-- @usage report:useExcerpt(1, 2) -- Create excerpt for titles in column 1, place in column 2
-- @usage report:useExcerpt(1, 2, 0, 200, 250) -- Create excerpt for titles in column 1 with namespace 2 (user namespace), 200 char limit, 250 hard limit
function Report:useExcerpt(srcColumn, destColumn, namespace, charLimit, charHardLimit)
table.insert(self.excerpts, {
srcColumn = srcColumn,
destColumn = destColumn,
namespace = namespace,
charLimit = charLimit,
charHardLimit = charHardLimit
})
return self
end
--- Set the CSS style for the report table.
-- Applies custom CSS styling to the generated table element.
-- @param style string CSS style string to apply to the table
-- @return Report Returns self for method chaining
-- @usage report:setTableStyle("border: 1px solid #ccc; width: 100%")
function Report:setTableStyle(style)
self.table_style = style
return self
end
--- Set the CSS class for the report table.
-- Applies a CSS class to the generated table element for styling.
-- @param class string CSS class name to apply to the table
-- @return Report Returns self for method chaining
-- @usage report:setTableClass("wikitable sortable")
function Report:setTableClass(class)
self.table_class = class
return self
end
--- Set the update interval for the database report.
-- Controls how often the report is automatically refreshed with new data from the database.
-- @param days number Number of days between updates (must be >= 1)
-- @return Report Returns self for method chaining
-- @raise error if days is not a number or is less than 1
-- @usage report:setInterval(7) -- Update every 7 days
function Report:setInterval(days)
if type(days) == "number" and days >= 1 then
self.interval = days
else
error("Interval must be a number >= 1")
end
return self
end
--- Set the number of rows per page for pagination.
-- Enables pagination by limiting the number of rows displayed per page. By default, no pagination is done.
-- @param count number Number of rows to display per page.
-- @return Report Returns self for method chaining
-- @raise error if count is not a positive number
-- @usage report:setPagination(1000) -- Show 1000 rows per page
function Report:setPagination(count)
if type(count) == "number" and count > 0 then
self.pagination = count
else
error("Pagination must be a positive number")
end
return self
end
--- Set the maximum number of pages when using pagination.
-- Limits the total number of pages that can be created in a paginated report. The default value is 5.
-- @param count number Maximum number of pages to display. Should not be greater than 20.
-- @return Report Returns self for method chaining
-- @raise error if count is not a positive number
-- @usage report:setMaxPages(10) -- Limit to 10 pages maximum
function Report:setMaxPages(count)
if type(count) == "number" and count > 0 then
self.max_pages = count
else
error("Max pages must be a positive number")
end
return self
end
--- Set a template to be used for formatting each row.
-- Allows custom formatting of individual rows using MediaWiki templates. The "Template:" prefix can be skipped.
-- @param template string Name of the template to use for row formatting
-- @return Report Returns self for method chaining
-- @usage report:setRowTemplate("MyRowTemplate")
function Report:setRowTemplate(template)
self.row_template = template
return self
end
--- Enable use of named parameters in the row template.
-- When enabled, values to the row template are passed by column name instead of position. For a query with columns page_title, page_namespace and rev_len, the row template generated would be {{MyRowTemplate|page_title=...|page_namespace=...|rev_len=...}} instead of {{MyRowTemplate|1=...|2=...|3=...}}.
-- @param value boolean Whether to use named parameters (truthy/falsy)
-- @return Report Returns self for method chaining
-- @usage report:useNamedParamsInRowTemplate(true) -- Use named parameters
function Report:useNamedParamsInRowTemplate(value)
self.row_template_named_params = value
return self
end
--- Skip generating the table structure for the report.
-- Useful when row_template is used and the table structure is not needed.
-- Useful when using custom templates for display.
-- @param value boolean Whether to skip table generation (truthy/falsy)
-- @return Report Returns self for method chaining
-- @usage report:skipTable(true) -- Skip table generation
function Report:skipTable(value)
self.skip_table = value
return self
end
--- Set a template to be used for generating the table header.
-- @param template string Name of the template to use for the header
-- @return Report Returns self for method chaining
-- @usage report:setHeaderTemplate("MyHeaderTemplate")
function Report:setHeaderTemplate(template)
self.header_template = template
return self
end
--- Set a template to be used as the report footer.
-- This can be used to complement the header template. For example, if the header template is {{div col}}, the footer template can be {{div col end}}.
-- @param template string Name of the template to use for the footer
-- @return Report Returns self for method chaining
-- @usage report:setFooterTemplate("MyFooterTemplate")
function Report:setFooterTemplate(template)
self.footer_template = template
return self
end
--- Set JavaScript code to be executed for postprocessing the report content.
-- Allows for arbitrary manipulation of the report data with limited access to Wikimedia APIs.
-- See [[Template:Database report#postprocess_js]] for more details.
-- @param js string JavaScript code
-- @return Report Returns self for method chaining
-- @usage report:setPostprocessJS("console.log('Report loaded');")
function Report:setPostprocessJS(js)
self.postprocess_js = js
return self
end
--- Enable or disable silent mode.
-- In silent mode, all visible output from the template is suppressed. Only the generated table is shown.
-- @param value boolean Whether to enable silent mode (truthy/falsy)
-- @return Report Returns self for method chaining
-- @usage report:setSilent(true) -- Enable silent mode
function Report:setSilent(value)
self.silent = value
return self
end
--- Set content placed before the report. Not to be confused with [[#Report:setHeaderTemplate|setHeaderTemplate]].
-- This can be used to include some lead text before the report, templatestyles tags, etc.
-- @param value Text
-- @return Report Returns self for method chaining
-- @usage report:setHeadContent('This appears before the report')
function Report:setHeadContent(value)
self.head_content = value
return self
end
--- Generate the complete database report configuration.
-- This is the main method that produces the output for use by the bot.
-- @return string Complete database report template configuration
-- @raise error if validation fails (e.g., missing SQL)
-- @usage return report:generate() -- Return the serialized representation of the report config
function Report:generate()
if not self.sql then
error("SQL must be set before generating")
end
-- Build the configuration
local config = {}
-- Add SQL (required)
table.insert(config, "|sql = " .. self.sql)
-- Add optional parameters
local optionalParams = {
wikilinks = formatWikilinks(self.wikilinks),
comments = formatColumnList(self.comments),
widths = formatWidths(self.widths),
table_style = self.table_style,
table_class = self.table_class,
excerpts = formatExcerpts(self.excerpts),
remove_underscores = formatColumnList(self.remove_underscores),
interval = self.interval,
pagination = self.pagination,
max_pages = self.max_pages,
hide = formatColumnList(self.hide),
row_template = self.row_template,
row_template_named_params = self.row_template_named_params,
skip_table = self.skip_table,
header_template = self.header_template,
footer_template = self.footer_template,
postprocess_js = self.postprocess_js,
silent = self.silent,
head_content = self.head_content
}
-- Add non-empty optional parameters
for param, value in pairs(optionalParams) do
if value and value ~= "" then
table.insert(config, "|" .. param .. " = " .. tostring(value))
end
end
-- Join configuration lines
local configText = table.concat(config, "\n")
-- Return the complete database report template
return "{{Database report\n" .. configText .. "\n}}"
end
-- Helper function to format wikilinks
function formatWikilinks(wikilinks)
if not wikilinks or #wikilinks == 0 then return nil end
local parts = {}
for _, link in ipairs(wikilinks) do
if type(link) == "table" and link.column then
local part = tostring(link.column)
if link.namespace then
part = part .. ":" .. tostring(link.namespace)
end
if link.show then
part = part .. ":show"
end
table.insert(parts, part)
elseif type(link) == "string" then
table.insert(parts, link)
end
end
return #parts > 0 and table.concat(parts, ", ") or nil
end
-- Helper function to format widths
function formatWidths(widths)
if not widths or #widths == 0 then return nil end
local parts = {}
for _, width in ipairs(widths) do
if type(width) == "table" and width.column and width.width then
table.insert(parts, tostring(width.column) .. ":" .. width.width)
elseif type(width) == "string" then
table.insert(parts, width)
end
end
return #parts > 0 and table.concat(parts, ", ") or nil
end
-- Helper function to format column lists
function formatColumnList(columns)
if not columns or #columns == 0 then return nil end
local parts = {}
for _, col in ipairs(columns) do
if type(col) == "number" then
table.insert(parts, tostring(col))
elseif type(col) == "string" and col:match("^%d+$") then
table.insert(parts, col)
end
end
return #parts > 0 and table.concat(parts, ", ") or nil
end
-- Helper function to format excerpts
function formatExcerpts(excerpts)
if not excerpts or #excerpts == 0 then return nil end
local parts = {}
for _, excerpt in ipairs(excerpts) do
if type(excerpt) == "table" and excerpt.srcColumn then
local part = tostring(excerpt.srcColumn)
if excerpt.destColumn then
part = part .. ":" .. tostring(excerpt.destColumn)
end
if excerpt.namespace then
part = part .. ":" .. tostring(excerpt.namespace)
end
if excerpt.charLimit then
part = part .. ":" .. tostring(excerpt.charLimit)
end
if excerpt.charHardLimit then
part = part .. ":" .. tostring(excerpt.charHardLimit)
end
table.insert(parts, part)
elseif type(excerpt) == "string" then
table.insert(parts, excerpt)
end
end
return #parts > 0 and table.concat(parts, ", ") or nil
end
return Report