Module:Database report
This module provides a Lua interface to configure database reports.
Usage
If your SQL code and other config is static, you probably don't need to use this module. You can directly use {{database report}}. This module is intended for advanced use cases where you want to share parts of the report configuration across multiple reports.
The high-level usage pattern is as follows:
local Report = require('Module:Database report')
local p = {}
p.main = function()
local report = Report:new()
-- Write your SQL.
report:setSQL("...WRITE YOUR SQL HERE...")
-- Customize the report as required. See the detailed documentation below for each function
report:useWikilinks(2, 'c1')
report:hideColumns(1)
-- The final step: returns the serialized representation of the report. Don't change this line.
return report:generate()
end
return p
It is recommended to create your module as a subpage of Module:Database reports (use the preload form on that page).
Next, create the report page with the content:
{{Database report
| lua_source = Module:Database report/...name of subpage... <!-- Module page where the report config is defined. -->
<!-- Optional parameters -->
| lua_function = main <!-- Name of the function which returns the generated report. Default: main -->
| lua_arg_foo = bar <!-- Pass the parameter foo=bar to the Lua function -->
}}
An example of a parametrized report is Module:Database_reports/Long_pages_by_namespace. Parameters passed to {{database report}} beginning with lua_arg_ are made available to the module.
For each parameter accepted by {{database report}}, a corresponding method exists in the Report class (see below). In addition, there is the Lua-specific method setHeadContent, which can be used by parametrized reports to include some common lead text across reports. Templatestyles can also included this way.
Function documentation
Report:new
Initialize a new database report instance. This is the constructor method that should be called to create a new report instance.
Signature:
function Report:new()
Returns:
- (Report) A new Report instance with default settings
Usage examples:
local report = Report:new()
Report:setSQL
Set the SQL query for the database report. This is a required parameter. The SQL must be a valid SELECT statement.
Signature:
function Report:setSQL(sql)
Parameters:
- sql (string) - The SQL query to execute (must be non-empty)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setSQL("SELECT page_title FROM page LIMIT 10")
Raises:
- error if sql is nil, not a string, or empty
Report:useWikilinks
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.
Signature:
function Report:useWikilinks(column, namespace, show)
Parameters:
- column (number|table) - Column number to be wikilinked, or table of column configurations
- 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.
- show (boolean) - Whether the namespace prefix should be displayed in the link (default: false)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:useWikilinks(1) -- Simple wikilink for column 1
report:useWikilinks(1, 0, true) -- Column 1, namespace 0, show prefix
report:useWikilinks({{column=1, namespace="c2"}, {column=3}}) -- Multiple columns
Report:setWidth
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.
Signature:
function Report:setWidth(column, width)
Parameters:
- column (number|table) - Column number to set width for, or table of width configurations
- width (string) - CSS width value (e.g., "10em", "50px", "20%", "200px")
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setWidth(1, "200px") -- Set column 1 to 200px width
report:setWidth({{column=1, width="10em"}, {column=2, width="50%"}}) -- Multiple columns
Report:setCommentColumns
Set columns to be treated as comments (edit summaries or log action summaries) in the report.
Signature:
function Report:setCommentColumns(...)
Parameters:
- ... (number) - Variable number of column numbers to mark as comments
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setCommentColumns(3, 5) -- Mark columns 3 and 5 as comments
Report:removeUnderscores
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.
Signature:
function Report:removeUnderscores(...)
Parameters:
- ... (number) - Variable number of column numbers to remove underscores from
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:removeUnderscores(1, 2) -- Remove underscores from columns 1 and 2
Report:hideColumns
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.
Signature:
function Report:hideColumns(...)
Parameters:
- ... (number) - Variable number of column numbers to hide
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:hideColumns(2, 4) -- Hide columns 2 and 4 from display
Report:useExcerpt
Configure generation of article excerpts (summaries). Creates excerpts from source column that should contain page titles, and places them in a destination column.
Signature:
function Report:useExcerpt(srcColumn, destColumn, namespace, charLimit, charHardLimit)
Parameters:
- srcColumn (number) - Column containing page title whose excerpt should be created
- destColumn (number) - Destination column number to place the excerpt.
- 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.
- charLimit (number) - Optional character limit for the excerpt
- charHardLimit (number) - Optional hard character limit (truncates if exceeded)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:useExcerpt(1, 2) -- Create excerpt for titles in column 1, place in column 2
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
Report:setTableStyle
Set the CSS style for the report table. Applies custom CSS styling to the generated table element.
Signature:
function Report:setTableStyle(style)
Parameters:
- style (string) - CSS style string to apply to the table
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setTableStyle("border: 1px solid #ccc; width: 100%")
Report:setTableClass
Set the CSS class for the report table. Applies a CSS class to the generated table element for styling.
Signature:
function Report:setTableClass(class)
Parameters:
- class (string) - CSS class name to apply to the table
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setTableClass("wikitable sortable")
Report:setInterval
Set the update interval for the database report. Controls how often the report is automatically refreshed with new data from the database.
Signature:
function Report:setInterval(days)
Parameters:
- days (number) - Number of days between updates (must be >= 1)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setInterval(7) -- Update every 7 days
Raises:
- error if days is not a number or is less than 1
Report:setPagination
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.
Signature:
function Report:setPagination(count)
Parameters:
- count (number) - Number of rows to display per page.
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setPagination(1000) -- Show 1000 rows per page
Raises:
- error if count is not a positive number
Report:setMaxPages
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.
Signature:
function Report:setMaxPages(count)
Parameters:
- count (number) - Maximum number of pages to display. Should not be greater than 20.
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setMaxPages(10) -- Limit to 10 pages maximum
Raises:
- error if count is not a positive number
Report:setRowTemplate
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.
Signature:
function Report:setRowTemplate(template)
Parameters:
- template (string) - Name of the template to use for row formatting
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setRowTemplate("MyRowTemplate")
Report:useNamedParamsInRowTemplate
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=...}}.
Signature:
function Report:useNamedParamsInRowTemplate(value)
Parameters:
- value (boolean) - Whether to use named parameters (truthy/falsy)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:useNamedParamsInRowTemplate(true) -- Use named parameters
Report:skipTable
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.
Signature:
function Report:skipTable(value)
Parameters:
- value (boolean) - Whether to skip table generation (truthy/falsy)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:skipTable(true) -- Skip table generation
Report:setHeaderTemplate
Set a template to be used for generating the table header.
Signature:
function Report:setHeaderTemplate(template)
Parameters:
- template (string) - Name of the template to use for the header
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setHeaderTemplate("MyHeaderTemplate")
Report:setFooterTemplate
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}}.
Signature:
function Report:setFooterTemplate(template)
Parameters:
- template (string) - Name of the template to use for the footer
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setFooterTemplate("MyFooterTemplate")
Report:setPostprocessJS
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.
Signature:
function Report:setPostprocessJS(js)
Parameters:
- js (string) - JavaScript code
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setPostprocessJS("console.log('Report loaded');")
Report:setSilent
Enable or disable silent mode. In silent mode, all visible output from the template is suppressed. Only the generated table is shown.
Signature:
function Report:setSilent(value)
Parameters:
- value (boolean) - Whether to enable silent mode (truthy/falsy)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setSilent(true) -- Enable silent mode
Report:setHeadContent
Set content placed before the report. Not to be confused with setHeaderTemplate. This can be used to include some lead text before the report, templatestyles tags, etc.
Signature:
function Report:setHeadContent(value)
Returns:
- (Report) Returns self for method chaining
Usage examples:
report:setHeadContent('This appears before the report')
Report:generate
Generate the complete database report configuration. This is the main method that produces the output for use by the bot.
Signature:
function Report:generate()
Returns:
- (string) Complete database report template configuration
Usage examples:
return report:generate() -- Return the serialized representation of the report config
Raises:
- error if validation fails (e.g., missing SQL)
--[[
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