Difference between revisions of "Module:Progress box"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(set the subtotal objects up so that we don't have to fetch the date format every time)
blackwiki>Mr. Stradivarius
(add undated articles - this involves splitting up the Subtotal class into two classes, Category and DatedCategory)
Line 24: Line 24:
  
 
-------------------------------------------------------------------------------
 
-------------------------------------------------------------------------------
-- Subtotal class
+
-- Category class
 
-------------------------------------------------------------------------------
 
-------------------------------------------------------------------------------
  
local Subtotal = {}
+
local Category = {}
Subtotal.__index = Subtotal
+
Category.__index = Category
Subtotal.message = message
+
Category.message = message
  
function Subtotal.new(data)
+
function Category.new(data)
local self = setmetatable({}, Subtotal)
+
local self = setmetatable({}, Category)
 
self._cfg = data.cfg
 
self._cfg = data.cfg
self._categoryBase = data.categoryBase
+
self._category = data.category
 +
return self
 +
end
 +
 
 +
function Category:getCategory()
 +
return self._category
 +
end
 +
 
 +
function Category:makeCategoryLink(display)
 +
local cat = self:getCategory()
 +
display = display or cat
 +
return string.format('[[:Category:%s|%s]]', cat, display)
 +
end
 +
 
 +
function Category:getCount()
 +
-- @TODO: implement formatnum
 +
if not self._count then
 +
self._count = mw.site.stats.pagesInCategory(self:getCategory(), 'pages')
 +
end
 +
return self._count
 +
end
 +
 
 +
-------------------------------------------------------------------------------
 +
-- DatedCategory class
 +
-- Inherits from Category
 +
-------------------------------------------------------------------------------
 +
 
 +
local DatedCategory = {}
 +
DatedCategory.__index = DatedCategory
 +
setmetatable(DatedCategory, Category)
 +
 
 +
function DatedCategory.new(data)
 +
local self = setmetatable(Category.new(data), {__index = DatedCategory})
 
self._date = data.date
 
self._date = data.date
 
self._dateFormat = data.dateFormat or self:message('date-format')
 
self._dateFormat = data.dateFormat or self:message('date-format')
 
self._formattedDate = self:formatDate(self._date)
 
self._formattedDate = self:formatDate(self._date)
 
self._category = self:message(
 
self._category = self:message(
'category-format',
+
'dated-category-format',
data.categoryBase,
+
data.undatedCategory,
 
self._formattedDate,
 
self._formattedDate,
 
data.from or '',
 
data.from or '',
Line 46: Line 78:
 
)
 
)
 
self._category = self._category:match('^%s*(.-)%s*$') -- trim whitespace
 
self._category = self._category:match('^%s*(.-)%s*$') -- trim whitespace
-- Using pagesInCategory is expensive, but we will have to do it every time
 
-- so it may as well go here.
 
self._subtotal = mw.site.stats.pagesInCategory(self._category, 'pages')
 
 
return self
 
return self
 
end
 
end
  
function Subtotal:formatDate(date)
+
function DatedCategory:formatDate(date)
 
return lang:formatDate(self._dateFormat, date)
 
return lang:formatDate(self._dateFormat, date)
 
end
 
end
  
function Subtotal:getDate()
+
function DatedCategory:getDate()
 
return self._date
 
return self._date
 
end
 
end
  
function Subtotal:getFormattedDate()
+
function DatedCategory:getFormattedDate()
 
return self._formattedDate
 
return self._formattedDate
end
 
 
function Subtotal:getCategory()
 
return self._category
 
end
 
 
function Subtotal:makeCategoryLink(display)
 
local cat = self:getCategory()
 
display = display or cat
 
return string.format('[[:Category:%s|%s]]', cat, display)
 
end
 
 
function Subtotal:getSubtotal()
 
return self._subtotal
 
 
end
 
end
  
Line 99: Line 114:
 
self._header = args[1]
 
self._header = args[1]
  
-- Make subtotal objects
+
-- Make the undated category object
self._subtotals = {}
+
do
 +
local undatedCategory = args[2] or args[1]
 +
if not undatedCategory then
 +
error('no category specified', 3)
 +
end
 +
self._undatedCategoryObj = Category.new{
 +
cfg = self._cfg,
 +
category = undatedCategory,
 +
}
 +
end
 +
 
 +
-- Make datedCategory objects
 +
self._datedCategories = {}
 
do
 
do
 
local cfg = self._cfg
 
local cfg = self._cfg
local categoryBase = args[2] or args[1]
+
local undatedCategory = self._undatedCategoryObj:getCategory()
if not categoryBase then
+
local from = args.from or self:message('dated-category-format-from')
error('no category base specified', 3)
 
end
 
local from = args.from or self:message('category-format-from')
 
 
local suffix = args.suffix
 
local suffix = args.suffix
 
local currentDate = lang:formatDate('Y-m')
 
local currentDate = lang:formatDate('Y-m')
Line 113: Line 137:
 
local dateFormat = self:message('date-format')
 
local dateFormat = self:message('date-format')
 
while date <= currentDate do
 
while date <= currentDate do
local subtotalObj = Subtotal.new{
+
local datedCategoryObj = DatedCategory.new{
 
cfg = cfg,
 
cfg = cfg,
categoryBase = categoryBase,
+
undatedCategory = undatedCategory,
 
from = from,
 
from = from,
 
suffix = suffix,
 
suffix = suffix,
Line 121: Line 145:
 
dateFormat = dateFormat,
 
dateFormat = dateFormat,
 
}
 
}
if subtotalObj:getSubtotal() > 0 then
+
if datedCategoryObj:getCount() > 0 then
table.insert(self._subtotals, subtotalObj)
+
table.insert(self._datedCategories, datedCategoryObj)
 
end
 
end
 
date = ProgressBox.incrementDate(date)
 
date = ProgressBox.incrementDate(date)
Line 202: Line 226:
 
:css('width', '100%')
 
:css('width', '100%')
 
:css('margin', 0)
 
:css('margin', 0)
for i, subtotalObj in ipairs(self._subtotals) do
+
for i, datedCategoryObj in ipairs(self._datedCategories) do
 
subtotalTable
 
subtotalTable
 
:tag('tr')
 
:tag('tr')
 
:tag('td')
 
:tag('td')
:wikitext(subtotalObj:makeCategoryLink(subtotalObj:getFormattedDate()))
+
:wikitext(datedCategoryObj:makeCategoryLink(
 +
datedCategoryObj:getFormattedDate()
 +
))
 
:done()
 
:done()
 
:tag('td')
 
:tag('td')
 
:css('text-align', 'right')
 
:css('text-align', 'right')
:wikitext(subtotalObj:getSubtotal())
+
:wikitext(datedCategoryObj:getCount())
 
end
 
end
 +
 +
-- Undated articles
 +
subtotalTable
 +
:tag('tr')
 +
:tag('td')
 +
:wikitext(self._undatedCategoryObj:makeCategoryLink(
 +
self:message('undated-category-link-display')
 +
))
 +
:done()
 +
:tag('td')
 +
:wikitext(self._undatedCategoryObj:getCount())
  
 
-- Total
 
-- Total

Revision as of 16:07, 15 May 2015

This module implements Template:Progress box. Please see the template page for usage instructions.

Configuration

This module has a configuration module at Module:Progress box/config.



-- This module implements [[Template:Progress box]]

local makePurgeLink = require('Module:Purge')._main
local lang = mw.language.getContentLanguage()
local CONFIG_MODULE = 'Module:Progress box/config'

-------------------------------------------------------------------------------
-- Message mixin
-- 
-- This function is mixed into all of the other classes
-------------------------------------------------------------------------------

local function message(self, key, ...)
	local msg = self._cfg[key]
	if not msg then
		error(string.format("no message found with key '%s'", tostring(key)), 2)
	end
	if select('#', ...) > 0 then
		return mw.message.newRawMessage(msg, ...):plain()
	else
		return msg
	end
end

-------------------------------------------------------------------------------
-- Category class
-------------------------------------------------------------------------------

local Category = {}
Category.__index = Category
Category.message = message

function Category.new(data)
	local self = setmetatable({}, Category)
	self._cfg = data.cfg
	self._category = data.category
	return self
end

function Category:getCategory()
	return self._category
end

function Category:makeCategoryLink(display)
	local cat = self:getCategory()
	display = display or cat
	return string.format('[[:Category:%s|%s]]', cat, display)
end

function Category:getCount()
	-- @TODO: implement formatnum
	if not self._count then
		self._count = mw.site.stats.pagesInCategory(self:getCategory(), 'pages')
	end
	return self._count
end

-------------------------------------------------------------------------------
-- DatedCategory class
-- Inherits from Category
-------------------------------------------------------------------------------

local DatedCategory = {}
DatedCategory.__index = DatedCategory
setmetatable(DatedCategory, Category)

function DatedCategory.new(data)
	local self = setmetatable(Category.new(data), {__index = DatedCategory})
	self._date = data.date
	self._dateFormat = data.dateFormat or self:message('date-format')
	self._formattedDate = self:formatDate(self._date)
	self._category = self:message(
		'dated-category-format',
		data.undatedCategory,
		self._formattedDate,
		data.from or '',
		data.suffix or ''
	)
	self._category = self._category:match('^%s*(.-)%s*$') -- trim whitespace
	return self
end

function DatedCategory:formatDate(date)
	return lang:formatDate(self._dateFormat, date)
end

function DatedCategory:getDate()
	return self._date
end

function DatedCategory:getFormattedDate()
	return self._formattedDate
end

-------------------------------------------------------------------------------
-- ProgressBox class
-------------------------------------------------------------------------------

local ProgressBox = {}
ProgressBox.__index = ProgressBox
ProgressBox.message = message

function ProgressBox.new(args, cfg, title)
	local self = setmetatable({}, ProgressBox)

	-- Argument defaults
	args = args or {}
	self._cfg = cfg or mw.loadData(CONFIG_MODULE)
	self._title = title or mw.title.getCurrentTitle()

	-- Set data
	self._float = args.float or 'left'
	self._margin = args.float == 'none' and 'auto' or nil
	self._header = args[1]

	-- Make the undated category object
	do
		local undatedCategory = args[2] or args[1]
		if not undatedCategory then
			error('no category specified', 3)
		end
		self._undatedCategoryObj = Category.new{
			cfg = self._cfg,
			category = undatedCategory,
		}
	end

	-- Make datedCategory objects
	self._datedCategories = {}
	do
		local cfg = self._cfg
		local undatedCategory = self._undatedCategoryObj:getCategory()
		local from = args.from or self:message('dated-category-format-from')
		local suffix = args.suffix
		local currentDate = lang:formatDate('Y-m')
		local date = self:findEarliestCategoryDate()
		local dateFormat = self:message('date-format')
		while date <= currentDate do
			local datedCategoryObj = DatedCategory.new{
				cfg = cfg,
				undatedCategory = undatedCategory,
				from = from,
				suffix = suffix,
				date = date,
				dateFormat = dateFormat,
			}
			if datedCategoryObj:getCount() > 0 then
				table.insert(self._datedCategories, datedCategoryObj)
			end
			date = ProgressBox.incrementDate(date)
		end
	end

	return self
end

-- Increments a date in the format YYYY-MM
function ProgressBox.incrementDate(date)
	local year, month = date:match('^(%d%d%d%d)%-(%d%d)$')
	year = tonumber(year)
	month = tonumber(month)
	if not year or not month then
		error(string.format("error parsing date '%s'", tostring(date)), 2)
	end
	month = month + 1
	if month > 12 then
		month = 1
		year = year + 1
	end
	return string.format('%04d-%02d', year, month)
end

function ProgressBox:findEarliestCategoryDate()
	return self:message('start-date')
end

function ProgressBox:isCollapsed()
	return self._title.namespace ~= 10 -- is not in template namespace
end

function ProgressBox:makeTotalLabel()
	-- @TODO
	return 'Total label'
end

function ProgressBox:getTotalCount()
	-- @TODO
	return 7
end

function ProgressBox:__tostring()
	data = data or {}
	local root = mw.html.create('table')
	
	-- Base classes and styles
	root
		:addClass('infobox')
		:css('float', self._float)
		:css('clear', self._float)
		:css('margin', self._margin)
		:css('width', '22em')

	-- Header row
	root:tag('tr'):tag('th')
		:attr('colspan', 2)
		:addClass('navbox-title')
		:css('padding', '0.2em')
		:css('font-size', '125%')
		:wikitext(self._header)

	-- Refresh row
	root:tag('tr'):tag('th')
		:attr('colspan', 2)
		:css('text-align', 'center')
		:wikitext(makePurgeLink{'(refresh)'})

	-- Subtotals
	local subtotalTable = root
		:tag('tr')
			:tag('th')
				:attr('colspan', 2)
				:css('padding', 0)
				:tag('table')
					:addClass('collapsible')
					:addClass(self:isCollapsed() and 'collapsed' or nil)
					:css('width', '100%')
					:css('margin', 0)
	for i, datedCategoryObj in ipairs(self._datedCategories) do
		subtotalTable
			:tag('tr')
				:tag('td')
					:wikitext(datedCategoryObj:makeCategoryLink(
						datedCategoryObj:getFormattedDate()
					))
					:done()
				:tag('td')
					:css('text-align', 'right')
					:wikitext(datedCategoryObj:getCount())
	end

	-- Undated articles
	subtotalTable
		:tag('tr')
			:tag('td')
				:wikitext(self._undatedCategoryObj:makeCategoryLink(
					self:message('undated-category-link-display')
				))
				:done()
			:tag('td')
				:wikitext(self._undatedCategoryObj:getCount())

	-- Total
	root
		:tag('tr')
			:css('font-size', '110%')
			:tag('th')
				:wikitext(self:makeTotalLabel())
				:done()
			:tag('td')
				:css('text-align', 'right')
				:wikitext(string.format("'''%d'''", self:getTotalCount()))

	return tostring(root)
end

-------------------------------------------------------------------------------
-- Exports
-------------------------------------------------------------------------------

local p = {}

function p._main(args)
	return tostring(ProgressBox.new(args))
end

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = 'Template:Progress box'
	})
	return p._main(args)
end

return p