Jump to content

Module:Medical cases chart/sandbox2

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Ahecht (talk | contribs) at 14:49, 14 April 2020 (Change class names to reference Bar Box, not Bar Stacked). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')

local p = {}

function p._barColors(n)
	if n == 1 then
		return 'Black' --deaths
	elseif n == 2 then
		return 'SkyBlue' --recoveries
	elseif n == 3 then
		return 'Tomato' --cases or altlbl1
	elseif n == 4 then
		return 'Gold' --altlbl2
	elseif n == 5 then
		return 'OrangeRed' --altlbl3
	end

	return nil
end

function p._legend0(args)
	return '<span style="margin:0px; font-size:90%;">' .. '<span style="' .. 'border:' .. (args.border or 'none') .. '; background-color:' .. (args[1] or 'none') .. '; color:' .. (args[1] or 'none') .. ';">' .. '&nbsp;&nbsp;&nbsp;&nbsp;' .. '</span>' .. '&nbsp;' .. (args[2] or '') .. '</span>'
end

function p._buildBars(args)
	local lines = mw.text.split( args.data, '\n' )
	local i = 1
	if (not args.divisor) or (args.divisor == '') then
		local total1, total2 = 0, 0
		for parameter in mw.text.gsplit( lines[#lines], ';' ) do
			if not string.find( parameter, '^ *%a' ) then
				if i == 7 then
					total1 = tonumber( (string.gsub( parameter, '%D', '' )) )
					if not total1 then
						total1 = 0
					end
				elseif i == 9 then
					total2 = tonumber( (string.gsub( parameter, '%D', '' )) )
					if not total2 then
						total2 = 0
					end
					break
				end
				i = i + 1
			end
		end
		args.divisor = math.max( total1, total2 ) / ( 0.95 * args.barwidth )
	end
	local bars, barargs, prevDate = {}, {}, ''
	for k, line in pairs( lines ) do
		barargs, i = {}, 1
		for parameter in mw.text.gsplit( line, ';' ) do
			if string.find( parameter, '^ *%a' ) then
				parameter = mw.text.split( parameter, '=' )
				barargs[parameter[1]] = parameter[2]
			else
				if parameter ~= '' then
					barargs[i] = parameter
				end
				i = i + 1
			end
		end
		barargs.divisor = args.divisor
		barargs.numwidth = args.numwidth
		barargs.collapsible = args.collapsible
		barargs.prevdate = prevDate
		bars[#bars+1] = p._row(barargs)
		prevDate = barargs[1]
	end
	return table.concat(bars)
end

function p._row(args)
	local barargs = {}
	frame = mw.getCurrentFrame()
	lang=mw.language.getContentLanguage()
	local rowDate = args.prevdate or ''
	
	if args[1] then
		if pcall(function () lang:formatDate('', args[1]) end) then
			barargs[1] = args[1]
			rowDate = args[1]
		else
			barargs[1] = '<strong class="error">Error: Invalid time.</strong>'
		end
	else
		barargs[1] = '⋮'
	end
	
	local two = args[2] or 0
	barargs[2] = frame:callParserFunction('#expr', two)
	
	local three = args[3] or 0
	barargs[3] = frame:callParserFunction('#expr', three)
	
	if args['alttot1'] then
		barargs[4] = frame:callParserFunction('#expr', args['alttot1'])
	elseif args[4] then
		barargs[4] = frame:callParserFunction('#expr', (args[4] .. '-' .. two .. '-' .. three) )
	else
		barargs[4] = 0
	end
	
	local five = args[5] or 0
	barargs[5] = frame:callParserFunction('#expr', five)
	
	if args['alttot2'] then
		barargs[6] = frame:callParserFunction('#expr', args['alttot2'])
	elseif args[6] then
		barargs[6] = frame:callParserFunction('#expr', (args[6] .. '-' .. two .. '-' .. three) )
	else
		barargs[6] = 0
	end
	
	barargs[7] = args[7] or ''
	
	if yesno(args.firstright1) == true then
		barargs[8] = '(n.a.)'
	elseif (yesno(args.firstright1) == false) or (args.firstright1 == '') or (not args.firstright1) then
		if args[1] then
			if args[8] then
				barargs[8] = '(' .. args[8] .. ')'
			else
				barargs[8] = ''
			end
		else
			if args[7] then
				barargs[8] = '(=)'
			elseif args[8] then
				barargs[8] = '(' .. args[8] .. ')'
			else
				barargs[8] = ''
			end
		end
	else
		barargs[8] = ''
	end
			
	barargs[9] = args[9] or ''			
				
	if yesno(args.firstright2) == true then
		barargs[10] = '(n.a.)'
	elseif (yesno(args.firstright2) == false) or (args.firstright2 == '') or (not args.firstright2) then
		if args[1] then
			if args[10] then
				barargs[10] = '(' .. args[10] .. ')'
			else
				barargs[10] = ''
			end
		else
			if args[9] then
				barargs[10] = '(=)'
			elseif args[10] then
				barargs[10] = '(' .. args[10] .. ')'
			else
				barargs[10] = ''
			end
		end
	else
		barargs[10] = ''
	end
	
	barargs.divisor = args.divisor or 1
	
	barargs.numwidth = args.numwidth
	
	local elapsedDays = (lang:formatDate('\U') - lang:formatDate('\U',rowDate)) / 86400
	if yesno(args.collapsible) == true then
		if args.collapsed then
			barargs.collapsed = args.collapsed
		elseif ( elapsedDays > 15 ) then
			barargs.collapsed = 'y'
		else
			barargs.collapsed = ''
		end
		
		if args.id then
			barargs.id = args.id
		else
			barargs.id = mw.ustring.lower(lang:formatDate('\M',rowDate))
			if elapsedDays <= 15 then
				barargs.id = barargs.id .. '-l15'
			end
		end
	else
		barargs.collapsed = ''
		barargs.id = ''
	end
	
	return p._customBarStacked(barargs)
end

function p._customBarStacked(args)
	barargs = {}
	
	barargs[1] = args[1]
	
	local function _numwidth(n)
		if n == 'n' then
			return 0
		elseif n == 't' then
			return 2.45
		elseif n == 'm' then
			return 3.5
		elseif n == 'w' then
			return 4.55
		elseif n == d then
			return 3.5
		end
		
		return 3.5
	end
	
	width1 = 3.5
	width2 = 3.5
	if (args.numwidth or '') ~= '' then
		width1 = _numwidth( mw.ustring.sub(args.numwidth,1,1) )
		width2 = _numwidth( mw.ustring.sub(args.numwidth,2,2) )
		width3 = _numwidth( mw.ustring.sub(args.numwidth,3,3) )
		width4 = _numwidth( mw.ustring.sub(args.numwidth,4,4) )
	end
	
		
	barargs[2] =
		'<span class="nowrap">' ..
			'<span style="width:' .. width1 .. 'em;padding:0 0.3em 0 0" class="cbs-ibr">' .. (args[7] or '') .. '</span>' ..
			'<span style="width:' .. width2 .. 'em" class="cbs-ibl">' .. (args[8] or '') .. '</span>\n' ..
		'</span>'
		
	if mw.ustring.len(args.numwidth) == 4 then
		local padding = '0.3em'
		if mw.ustring.sub(args.numwidth,3,3) == 'n' then
			padding = '0'
		end
		
		barargs.note2 =
			'<span style="width:' .. width3 .. 'em;padding:0 ' .. padding .. ' 0 0"  class="cbs-ibr">' 
				.. (args[9] or '') .. 
			'</span>' ..
			'<span style="width:' .. width4 .. 'em"  class="cbs-ibl">' ..
				(args[10] or '') ..
			'</span>'
	end
	
	for i=1,5,1 do 
		barargs[(2*i) + 1] = p._barColors(i)
		barargs[(2*i) + 2] = tonumber(mw.ustring.format("%.2f", tonumber(args[i+1])/tonumber(args.divisor) ) )
		barargs['title' .. i] = args[i+1]
	end
	
	barargs.align = 'cdcc'
	barargs.collapsed = args.collapsed
	barargs.id = args.id
	
	return p._barStacked(barargs)
end

function p._barStacked(args)
	local function _align(n, default)
		if (args.align or '') ~= '' then
			local a = mw.ustring.sub(args.align,n,n)
			if a == 'l' then
				return 'left'
			elseif a == 'c' then
				return 'center'
			elseif a == 'r' then
				return 'right'
			elseif a == 'd' then
				return default
			end
		end
		
		return default
	end

	local output = {}
	
	if (args.id or '') ~= '' then
		output[1] = '<tr class="mw-collapsible' .. ( yesno(args.collapsed) and ' mw-collapsed' or '') ..
			'" id="mw-customcollapsible-' .. args.id .. '"}}>\n'
	else
		output[1] = '<tr>\n'
	end
	
	output[2] =
		'<td ' .. (args.note1 and '' or 'colspan="2" ') .. 
		'style="text-align:' .. _align(1,'left') .. '" class="bb-04em">' .. 
			mw.text.trim(args[1] or '') .. 
		'</td>\n'
	
	if (args.note1 or '') ~= '' then
		output[3] = 
			'<td style="text-align:' .. _align(2,'right') .. '" class="bb-04em">' .. 
				args.note1 .. 
			'</td>\n'
	else
		output[3] = ''
	end
	
	output[4] = '<td class="bb-lr">\n'
	
	for i=1,5,1 do 
		if ( (tonumber(args['title' .. i]) or 0) == 0) or ( (tonumber(args[(2*i) + 2]) or 0) == 0) then
			output[i+4] = ''
		else
			output[i+4] = 
				'<div title=' .. args['title' .. i] ..
				' style="background:' .. (args[(2*i) + 1] or 'gray') ..
				';width:' .. mw.text.trim(args[(2*i) + 2]) .. 'px" class="bb-fl">' ..
					'&#8203;' ..
				'</div>\n'
		end
	end
	
	output[10] = '</td>\n'
	
	output[11] = 
		'<td ' .. (args.note2 and '' or 'colspan="2" ') .. 
		'style="text-align:' .. _align(3,'left') .. '" class="bb-04em">' .. 
			mw.text.trim(args[2] or '') .. 
		'</td>\n'
	
	if (args.note2 or '') ~= '' then
		output[12] = 
		'<td style="text-align:' .. _align(4,'right') .. '" class="bb-04em">' .. 
			args.note2 ..
		'</td>\n'
	else
		output[12] = ''
	end
	
	output[13] = '</tr>\n'
	
	return table.concat(output)
end

function p._chart(args)
	local navbar = require('Module:Navbar')._navbar
	
	local barargs = {}
	
	local barwidth = 280
	if args.barwidth == 'thin' then
		barwidth = 120
	elseif args.barwidth == 'medium' then
		barwidth = 280
	elseif args.barwidth == 'wide' then
		barwidth = 400
	end
	
	local function _numwidth(p)
		local n = mw.ustring.sub((args.numwidth or ''),p,p)
		if n == 'n' then
			return 0
		elseif n == 't' then
			return 40
		elseif n == 'm' then
			return 55
		elseif n == 'w' then
			return 70
		elseif n == 'd' then
			return 55
		end
		
		return 0
	end
	
	local numwidth = 120
	local right1 = numwidth - 8 -- -8 because of padding
	if args.numwidth then 
		numwidth = _numwidth(1) + 10 + _numwidth(2)
		if mw.ustring.len(args.numwidth) == 4 then
			numwidth = numwidth + _numwidth(3) + _numwidth(4)
			if mw.ustring.sub(args.numwidth,3,3) == 'n' then
				numwidth = numwidth + 6
			else
				numwidth = numwidth + 10
			end
		end
		
		right1 = _numwidth(1) + 2 + _numwidth(2)
		if (not args.right2) and mw.ustring.len(args.numwidth) == 4 then
			right1 = right1 + _numwidth(3) + _numwidth(4)
			if mw.ustring.sub(args.numwidth,3,3) == 'n' then
				numwidth = numwidth + 6
			else
				numwidth = numwidth + 10
			end
		end
	end
	
	barargs.width = (85 + barwidth + numwidth) .. 'px'
	barargs.barwidth = barwidth .. 'px'
	barargs.float = args.float and args.float or 'right'
	
	local location = mw.ustring.gsub(args.location, 'the ', '')
	location = mw.ustring.upper(mw.ustring.sub(location,1,1)) .. mw.ustring.sub(location,2)
	
	local navbartitle = args.outbreak .. ' data/' ..
		((args.location3) and (args.location3 and '/') or '') ..
		((args.location2) and (args.location2 and '/') or '') ..
		location .. ' medical cases chart'
		
	local title = {}
	title[1] = ((args.pretitle) and (args.pretitle .. ' ') or ('')) ..
		args.disease .. ' cases in ' .. args.location .. 
		((args.location2) and (', ' .. args.location2) or '') ..
		((args.location3) and (', ' .. args.location3) or '') ..
		((args.posttitle) and (' ' .. args.posttitle) or '') .. '<span class="nowrap">&nbsp;&nbsp;</span>(' ..
		navbar({[1] = navbartitle, titleArg = (':' .. mw.getCurrentFrame():getParent():getTitle()), mini = 1, nodiv = 1}) .. 
		')<br />'
	title[2] = p._legend0({[1] = p._barColors(1),[2] = 'Deaths'})	
	if yesno(args.recoveries) == false then
		title[3] = ''
	else
		title[3] = '<span class="nowrap">&nbsp;&nbsp;&nbsp;</span>' .. p._legend0({[1] = p._barColors(2),[2] = (args.reclbl or 'Recoveries')})
	end
	title[4] = '<span class="nowrap">&nbsp;&nbsp;&nbsp;</span>' .. p._legend0({[1] = p._barColors(3),[2] = (args.altlbl1 or 'Active cases')})
	if args.altlbl2 then
		title[5] = '<span class="nowrap">&nbsp;&nbsp;&nbsp;</span>' .. p._legend0({[1] = p._barColors(4),[2] = args.altlbl2})
	else
		title[5] = ''
	end
	if args.altlbl3 then
		title[6] = '<span class="nowrap">&nbsp;&nbsp;&nbsp;</span>' .. p._legend0({[1] = p._barColors(5),[2] = args.altlbl3}) ..'\n'
	else
		title[6] = '\n'
	end
	title[7] = (args.togglesbar or '') -- this feature is WIP
	
	barargs.title = table.concat(title)
	barargs.left1 = 
		'<div class="center" style="width:77px;">' .. -- 85-8 because of padding
			"'''Date'''" ..
		'</div>'
	
	barargs.right1 = 
		'<div class="center" style="width:' .. right1 .. 'px;">' ..
			(args.right1 or "'''&#35; of cases'''") ..
		'</div>'
	
	if args.right2 then
		local right2 = _numwidth(3) + _numwidth(4)
		if mw.ustring.sub(args.numwidth,3,3) == 'n' then
			right2 = right2 - 2
		else
			right2 = right2 + 2
		end
		
		barargs.right2 = 
		'<div class="center" style="width:' ..right2 ..'px;">' .. 
			"'''" .. args.right2 .. "'''" ..
		'</div>'
	end

	if args.rows then
		barargs.bars = args.rows
	elseif args.data then
		barargs.bars = p._buildBars({
			barwidth = barwidth,
			data = args.data,
			divisor = args.divisor,
			numwidth = args.numwidth,
			collapsible = args.collapsible
			})
	end
	
	barargs.caption = args.caption
	barargs.css = 'Template:Medical_cases_chart/styles.css'
	return p._barBox(barargs)
end

function p._barBox(args)
	local width = args.width or 'auto'
	
	local class = 'barbox'
	if args.float == 'left' or args.float == 'right' or args.float == 'none' then
		class = 'barbox t' .. args.float
	elseif args.float == 'center' then
		class = 'barbox tnone'
	end
	
	local output = {}
	
	local css = ((args.css or '') ~= '') and args.css or 'Template:Bar box/styles.css'
	output[1] = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = {src=args.css} }
	
	if (args.float == 'left') or (args.float == 'right') then
		output[2] = ''
		output[12] = ''
	else
		output[2] = '<table style="margin:' .. ( (args.float == 'center') and '0 auto' or 'auto' ) .. '; border:none;"><tr><td style="border:none; padding:0;">'
		output[12] = '</td></tr></table>[[Category:Pages using bar box without float left or float right|' .. ( (width == 'auto') and 'Ω' or '' ) .. mw.title.getCurrentTitle().text .. ']]'
	end
	
	output[3] = 
		'<div class="' .. class .. '" style="overflow-x: auto;' .. (args.style or '') .. '">\n' ..
			'<div style="border:' .. (args.border_width or '1') .. 'px solid silver; font-size:88%; padding:0.4em; width:' .. width .. '; background: ' .. (args['background-color'] or 'white') .. ';">\n' ..
				'<table style="text-align:left; border-collapse:collapse; width:100%;">\n'

	if args.title then
		output[4] = 
			'<tr style="background:' .. (args.titlebar or 'none') .. '">' ..
				'<th style="text-align:center;" colspan="5">' .. args.title .. '</th>' ..
			'</tr>\n'
	else
		output[4] = ''
	end
	
	output[5] = 
		'<tr style="font-size:88%; height:4px;">\n' .. 
			'<td ' .. (args.left2 and '' or 'colspan="2"') .. ' style="padding:0 4px; text-align:left;">' .. 
				(args.left1 or '') .. 
			'</td>\n'
	
	if args.left2 then
		output[6] = '<td style="padding:0 4px; text-align:right;">' .. args.left2 .. '</td>\n'
	else
		output[6] = ''
	end
	
	output[7] = '<td style="width:' .. (args.barwidth or '100px') .. '; text-align:left;"></td>\n' ..
		'<td ' .. (args.right2 and '' or 'colspan="2"') .. ' style="padding:0 4px; width:1em; text-align:right;">' .. 
			(args.right1 or '') .. 
		'</td>\n'
	
	if args.right2 then
		output[8] = '<td style="padding:0 4px; text-align:right;">' .. args.right2 .. '</td>\n'
	else
		output[8] = ''
	end
	
	output[9] = '</tr>\n' .. (args.bars or '')
	
	if args.caption then
		output[10] = '<tr><td colspan="5" style="padding:5px; text-align:left;">' .. args.caption .. '</td></tr>\n'
	else
		output[10] = ''
	end
		
	output[11] = '</table>\n</div>\n</div>\n'
		
	-- output[12] defined above
	
	return table.concat(output)
end

function p.barBox(frame)
	local args = getArgs(frame, {
		valueFunc = function (key, value)
			if value then
				value = mw.text.trim(value)
				if (key == 'width') or (key == 'float') then
					value = mw.ustring.lower(value)
				end
				if value ~= '' then
					return value
				end
			end
			return nil
		end
	})
	return p._barBox(args)
end

function p.chart(frame)
	local args = getArgs(frame, {
		valueFunc = function (key, value)
			if value then
				value = mw.text.trim(value)
				if (key == 'numwidth') or (key == 'barwidth') or (key == 'recoveries') then
					value = mw.ustring.lower(value)
				end
				if value ~= '' then
					return value
				end
			end
			return nil
		end
	})
	return p._chart(args)
end

function p.buildBars(frame)
	return p._buildBars(frame.args)
end

return p