Module:NUMBEROF

From Meta, a Wikimedia project coordination wiki
Jump to navigation Jump to search
Module documentation

-- Cached for performance
local floor = math.floor
local trim = mw.text.trim
local lower = mw.ustring.lower
local localSite = string.match(mw.site.server, '.*//(.*)%.org$') -- examples: 'af.wikipedia', 'commons.wikimedia'
local siteStats = mw.site.stats
-- local contentLanguage = mw.getContentLanguage() -- for the default site language
-- local contentLanguage = mw.language.getContentLanguage() -- equivalent, for the default site language
local contentLanguage = mw.language.new(mw.getCurrentFrame():preprocess('{{PAGELANGUAGE}}')) -- for the current page, not the default site language
local loadData = mw.loadData
local dataMeta, dataData, dataRank, dataOther -- lazily initialized

local function trimArg(arg, i)
	arg = trim(arg or '')
	if arg == '' then
		if i then
			error('Parameter ' .. i .. ' is missing. See template documentation')
		end
		return nil
	end
	return lower(arg)
end

local function getValue(stats, action, map)
	action = map and map[action]
	if action == 'depth' then
		-- https://meta.wikimedia.org/wiki/Wikipedia_article_depth
		-- This gives silly results if, for example, the number of articles is small.
		local pages = map and map.pages or 0
		local articles = map and map.articles or 0
		if pages ~= 0 and articles ~= 0 then
			local edits = map and map.edits or 0
			return floor(((pages - articles) / articles) ^ 2 * edits / pages)
		end
		return 0
	end
	return stats[action]
end

local function getIfLocal(site, action)
	-- If wanted site is the local site where module is running,
	-- return numberof result for given action, or nil.
	-- This is faster than reading the cached table, and gives the current value.
	-- localSite examples: 'af.wikipedia', 'commons.wikimedia'
	if site == localSite then
		if action == 'activeusers' then
			action = 'activeUsers'
		end
		return getValue(siteStats, action)
	end
end

local _parentFrame = nil -- cache
local function getArgs(frame)
	if not _parentFrame then
		_parentFrame = frame:getParent() -- cached for performance (Scribunto interface is really slow)
	end
	return _parentFrame.args
end

local function main(frame)
	local metaWords = { active = true, closed = true, languages = true, }
	local args = getArgs(frame)
	local action = trimArg(args[1], 1) -- activeusers, admins, articles, edits, files, pages, users, depth, active, closed, languages
	if action:sub(1, 8) == 'numberof' then -- numberofX is an alias for X
		action = trimArg(action:sub(9), 1)
	end
	local wantMeta = metaWords[action]
	local site = trimArg(args[2], 2)
	if not wantMeta and not site:find('.', 1, true) then
		-- site is like "total" or "af" (not "af.wikipedia", "af.wikiquote", "total.wikisource", etc.)
		site = site .. '.wikipedia'
	end
	local result
	if wantMeta then
		dataMeta = dataMeta or loadData('Module:NUMBEROF/meta') -- loaded lazily and cached
		local nrActive = dataMeta.nrActive[site]
		local nrClosed = dataMeta.nrClosed[site]
		-- If either is set, site is valid but there may not be an entry for both active and closed.
		if nrActive or nrClosed then
			if action == 'active' then
				result = (nrActive or 0)
			elseif action == 'closed' then
				result = (nrClosed or 0)
			elseif action == 'languages' then
				result = (nrActive or 0) + (nrClosed or 0)
			end
		end
	else
		result = getIfLocal(site, action)
		if not result then
			dataData = dataData or loadData('Module:NUMBEROF/data') -- loaded lazily and cached
			local data = dataData.data
			result = data[site]
			if result then
				local map = dataData.map
				result = getValue(result, action, map)
			end
		end
	end
	local wantComma = trimArg(args[3]) -- nil for no commas in output; "N" or anything nonblank inserts commas
	result = result or 0
	return wantComma and contentLanguage:formatNum(result) or result -- number or formatted string
end

local function rank(frame)
	-- Rank sites in a specified sister project by their number of articles.
	local args = getArgs(frame)
	local parm = trimArg(args[1], 1) -- a number like 12 or a site name like "af" (not "af.wikipedia")
	local base = trimArg(args[2]) or 'wikipedia' -- base of full site name like "wikipedia" or "wikiquote"
	local data
	if base == 'wikipedia' then
		dataRank = dataRank or loadData('Module:NUMBEROF/rank') -- loaded lazily and cached
		data = dataRank[base]
	else
		dataOther = dataOther or loadData('Module:NUMBEROF/other') -- loaded lazily and cached
		data = dataOther[base]
	end
	parm = tonumber(parm) or parm
	if type(parm) == 'number' then -- site with this rank (string)
		return data and data.rankByIndex[parm] or ''
	else -- rank for this site (number or formatted string)
		local wantComma = trimArg(args[3]) -- nil for no commas in output; "N" or anything nonblank inserts commas
		local result = data and data.rankBySite[parm] or 0
		return wantComma and contentLanguage:formatNum(result) or result
	end
end

return {
	main = main,
	rank = rank,
}