Module:Progress box

-- 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._whatToCount = data.whatToCount self:setCategory(data.category) return self end

function Category:setCategory(category) self._category = category end

function Category:getCategory return self._category end

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

function Category:getCount if not self._count then local counts = mw.site.stats.pagesInCategory(self:getCategory, '*') self._count = counts[self._whatToCount or 'pages'] if not self._count then error("the count type must be one of 'pages', 'subcats', 'files' or 'all'") end end return self._count end

function Category:getFormattedCount return lang:formatNum(self:getCount) end

function Category:exists return mw.title.makeTitle(14, self:getCategory).exists 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) do local category = self:message(			'dated-category-format',			data.baseCategory,			self._formattedDate,			data.from or self:message('dated-category-format-from'),			data.suffix or ''		) category = category:match('^%s*(.-)%s*$') -- trim whitespace self:setCategory(category) end 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] self._frame = mw.getCurrentFrame

-- Make the base category object if not args[1] then error('no base category specified', 3) end self._baseCategoryObj = Category.new{ cfg = self._cfg, category = args[1], }

-- Make datedCategory objects self._datedCategories = {} do local cfg = self._cfg local baseCategory = args[2] or self._baseCategoryObj:getCategory local whatToCount = args.count 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, baseCategory = baseCategory, whatToCount = whatToCount, 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

-- Make all-article category object do local allCategory if args[3] then allCategory = args[3] else allCategory = self:message(				'all-articles-category-format',				self._baseCategoryObj:getCategory			) allCategory = self._frame:preprocess(allCategory) end self._allCategoryObj = Category.new{ cfg = self._cfg, category = allCategory, }	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 local display = self:message('all-articles-label') if self._allCategoryObj:exists then return self._allCategoryObj:makeCategoryLink(display) else return display end end

function ProgressBox:getTotalCount local count = 0 for i, obj in ipairs(self._datedCategories) do		count = count + obj:getCount end count = count + self._baseCategoryObj:getCount return count end

function ProgressBox:getFormattedTotalCount return lang:formatNum(self:getTotalCount) 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) :css('background', '#ddf') :css('padding', '0.2em') :css('font-size', '125%') :wikitext(self._header)

-- Refresh row root:tag('tr'):tag('td') :attr('colspan', 2) :css('text-align', 'center') :wikitext(makePurgeLink{self:message('purge-link-display')})

-- Subtotals local subtotalTable = root :tag('tr') :tag('td') :attr('colspan', 2) :css('padding', 0) :tag('table') :addClass('collapsible') :addClass(self:isCollapsed and 'collapsed' or nil) :css('width', '100%') :css('margin', 0) subtotalTable :tag('tr') :tag('th') :attr('colspan', 2) :wikitext(self:message('subtotal-heading')) 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:getFormattedCount) end

-- Undated articles subtotalTable :tag('tr') :tag('td') :wikitext(self._baseCategoryObj:makeCategoryLink( self:message('undated-articles-label') ))				:done :tag('td') :css('text-align', 'right') :wikitext(self._baseCategoryObj:getFormattedCount)

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

return tostring(root) end

--- -- Exports ---

local p = {}

function p._exportClasses return { Category = Category, DatedCategory = DatedCategory, ProgressBox = ProgressBox, } end

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

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

return p