Module:Medical cases chart/sandbox3 explained

local getArgs = require('Module:Arguments').getArgslocal yesno = require('Module:Yesno')local barBox = require('Module:Bar box')

local language = 'en-US' -- local default language

local i18n = require("Module:Medical cases chart/i18n")[language]local navbar = require('Module:Navbar')._navbar

local function is(v) return (v or ) ~= end

local p =

function p._barColors(n) local colors =

return colors[n]end

function p._legend0(args) return '

' .. '' .. '    ' .. '' .. ' ' .. (args[2] or ) .. ''end

function p._customBarStacked(args) barargs = barargs[1] = args[1]

local function _numwidth(nw) if nw

'n' then return 0 elseif nw

't' then return 2.45 elseif nw

'm' then return 3.5 elseif nw

'w' then return 4.55 elseif nw

'x' then return 5.6 elseif nw

'd' then return 3.5 end

return 3.5 end

width1 = 3.5 width2 = 3.5 if is(args.numwidth) 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] = '

' .. (args[7] or ) .. '' .. '' .. (args[8] or ) .. ''

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 = '

' .. (args[9] or ) .. '' .. '' .. (args[10] or ) .. '' end

for i=1,5 do barargs[2*i + 1] = p._barColors(i) barargs[2*i + 2] = (tonumber(args[i+1]) or 0)/(tonumber(args.divisor) or 1) barargs['title' .. i] = args[i+1] end

barargs.align = 'cdcc' barargs.collapsed = args.collapsed barargs.id = args.id barargs.rowstyle = is(tonumber(args.rowheight)) and ('line-height:'..args.rowheight..';') or nil

return barBox._stacked(barargs)end

function p._row(args) local barargs = local rowDate = args.prevDate or

if is(args[1]) then if pcall(function mw.getContentLanguage:formatDate(, args[1]) end) then barargs[1] = args[1] rowDate = args[1] else barargs[1] = '' .. i18n.invalidTime .. '' end else barargs[1] = '⋮' end barargs[1] = barargs[1] .. (args['note0'] or )

barargs[2] = args[2] or 0 barargs[3] = args[3] or 0

if is(args['alttot1']) then barargs[4] = args['alttot1'] elseif args[4] then barargs[4] = (tonumber(args[4]) or 0) - (tonumber(barargs[2]) or 0) - (tonumber(barargs[3]) or 0) else barargs[4] = 0 end

barargs[5] = args[5] or 0

if is(args['alttot2']) then barargs[6] = args['alttot2'] elseif args[6] then barargs[6] = (tonumber(args[6]) or 0) - (tonumber(barargs[2]) or 0) - (tonumber(barargs[3]) or 0) else barargs[6] = 0 end

barargs[7] = args[7] or

local function changeArg(firstright, valuecol, changecol) local change = if yesno(args['firstright' .. firstright])

true then change = '(' .. i18n.na .. ')' elseif yesno(args['firstright' .. firstright])

false or not is(args['firstright' .. firstright]) then if not is(args[1]) and is(args[valuecol]) then change = '(' .. i18n['='] .. ')' else change = is(args[changecol]) and '(' .. args[changecol] .. ')' or end end change = change .. (args['note' .. firstright] or )

return change end

barargs[8] = changeArg(1,7,8) barargs[9] = args[9] or barargs[10] = changeArg(2,9,10)

barargs.divisor = args.divisor or 1 barargs.numwidth = args.numwidth barargs.rowheight = args.rowheight

if yesno(args.collapsible)

true then local duration = tonumber(args.duration) or 15 if args.collapsed then barargs.collapsed = args.collapsed elseif args.rowsToEnd >= duration then barargs.collapsed = 'y' else barargs.collapsed = end

if args.id then barargs.id = args.id elseif args.nooverlap and args.rowsToEnd < duration then barargs.id = 'l' .. duration else barargs.id = mw.ustring.lower(mw.getLanguage('en'):formatDate('M', rowDate)) if args.rowsToEnd < duration then barargs.id = barargs.id .. '-l' .. duration end end else barargs.collapsed = barargs.id = end

return p._customBarStacked(barargs)end

function p._buildBars(args) local lines = mw.text.split(args.data, '\n') local frame = mw.getCurrentFrame local lang = mw.getContentLanguage

local bars, rows, months, prevRow, maxparam =,,, , 1 for k, line in pairs(lines) do local barargs, i =, 1 for parameter in mw.text.gsplit(line, ';') do parameter = mw.text.trim(parameter) if string.find(parameter, '^%a') then parameter = mw.text.split(parameter, '=') if parameter[1]

'alttot1' or parameter[1]

'alttot2' then parameter[2] = tonumber(frame:callParserFunction('#expr', parameter[2])) if is(parameter[2]) then maxparam = math.max(maxparam, parameter[2]) end end barargs[parameter[1]] = parameter[2] else if is(parameter) then if i >= 2 and i <= 6 then parameter = tonumber(frame:callParserFunction('#expr', frame:callParserFunction('formatnum',parameter,'R'))) maxparam = math.max(maxparam, parameter or 1) end barargs[i] = parameter if i

7 or i

9 then parameter = tonumber(mw.ustring.match(frame:callParserFunction('formatnum',parameter,'R'), '^%d*')) maxparam = math.max(maxparam, parameter or 1) end end i = i + 1 end end

local function fillCols(col, change) local data = args['right' .. col .. 'data'] local changetype = args['changetype' .. col] local value, num, prevnum

if data

'alttot1' then num = tonumber(barargs.alttot1 or barargs[4]) prevnum = tonumber(prevRow.alttot1 or prevRow[4]) elseif data

'alttot2' then num = tonumber(barargs.alttot2 or barargs[6]) prevnum = tonumber(prevRow.alttot2 or prevRow[6]) elseif is(data) then num = tonumber(barargs[tonumber(data) + 1]) prevnum = tonumber(prevRow[tonumber(data) + 1]) end

if is(data) and num then -- nothing in column, source found, and data exists value = changetype

'o' and or lang:formatNum(num) -- set value to num if changetype isn't 'o' if not change and yesno(barargs['firstright' .. col] ~= true) then if prevnum and prevnum ~= 0 then -- data on previous row if num - prevnum ~= 0 then --data has changed since previous row change = num-prevnum if changetype

'a' then -- change type is "absolute" if change > 0 then change = '+' .. lang:formatNum(change) end else -- change type is "percent", "only percent" or undefined local percent = 100 * change / prevnum -- calculate percent local rounding = math.abs(percent) >= 10 and "%.0f" or math.abs(percent) >= 1 and "%.1f" or "%.2f" percent = tonumber(mw.ustring.format(rounding, percent)) -- round to two sigfigs if percent > 0 then change = '+' .. lang:formatNum(percent) .. '%' elseif percent < 0 then change = lang:formatNum(percent) .. '%' else change = i18n['='] end end else -- data has not changed since previous row change = i18n['='] end else -- no data on previous row barargs['firstright' .. col] = true -- set to (n.a.) end end end

return value, change end

if not is(barargs[7]) then barargs[7], barargs[8] = fillCols(1, barargs[8]) end if not is(barargs[9]) then barargs[9], barargs[10] = fillCols(2, barargs[10]) end if is(barargs[1]) then local e,f,g = pcall(function return mw.getLanguage('en'):formatDate('M',barargs[1]), mw.getLanguage('en'):formatDate('j',barargs[1]) end ) if e then months[#months+1] = end end barargs.prevDate = prevRow[1] rows[#rows + 1] = barargs prevRow = barargs end

for i=1,#rows do -- build rows rows[i].divisor = tonumber(args.divisor) and tonumber(args.divisor) or maxparam / (0.95 * args.barwidth) rows[i].numwidth = args.numwidth rows[i].collapsible = args.collapsible rows[i].rowsToEnd = #rows - i rows[i].rowheight = args.rowheight rows[i].duration = args.duration if #months>(args.duration or 0) then rows[i].nooverlap = args.nooverlap end bars[i] = p._row(rows[i]) end return table.concat(bars), monthsend

function p._monthToggleButton(args) local month = mw.ustring.lower(mw.ustring.sub(args.month[1] or ,1,3)) local outString = local newline = (args.nonewline or false) and or '\n' if is(month) then local collapsed = (args.active

) and or ' mw-collapsed' local uncollapsed = (args.active

) and ' mw-collapsed' or if args.nooverlap then outString = '

%s\n' .. '%s' .. newline if mw.ustring.sub(month,1,1)

'l' and tonumber(mw.ustring.sub(month,2))

args.duration then --"Last ## days" local lastDays = mw.ustring.format(i18n.lastDays, args.duration) outString = mw.ustring.format(outString, collapsed, lastDays, uncollapsed, lastDays) else if i18n.m[month] then if is(args.month[2]) and is(args.month[3]) then if (args.month[2] ~= args.month[3]) then -- "Mmm ##–##" month = mw.ustring.gsub(i18n.toggleRange,'$.',) else -- "Mmm ##"" month = mw.ustring.gsub(i18n.toggleSingleDate,'$.',) end else --"Mmm" month = i18n.m[month] end end outString = mw.ustring.format(outString, uncollapsed, month, collapsed, month) end elseif mw.ustring.sub(month,1,1)

'l' and tonumber(mw.ustring.sub(month,2))

args.duration then local customtoggles = for k in pairs(i18n.m) do --list of months customtoggles[#customtoggles + 1] = ' mw-customtoggle-' .. k .. '-l' .. args.duration end local lastDays = mw.ustring.format(i18n.lastDays, args.duration) outString = '

' .. lastDays .. '\n' .. '' .. lastDays .. '' .. newline else local customtoggles = ' mw-customtoggle-' .. month .. ' mw-customtoggle-' .. month .. '-l' .. args.duration outString = '' .. (i18n.m[month] or month) .. '\n' .. '' .. (i18n.m[month] or month) .. '' .. newline end end return outStringend

function p._chart(args)

local barargs =

-- mappings and defaults moved to _getChartPams local numwidth = args.numwidthwidth local right1 = args.right1width local right2 = args.right2width local barwidth = args.barwidth barargs.width = args.sTotalWidth barargs.barwidth = args.sBarWidth barargs.float = args.float local duration = args.nDuration

local months, togglesbar, lastdate =, , nil if args.rows then barargs.bars = args.rows togglesbar = yesno(args.collapsible) and (args.togglesbar or ) or nil elseif is(args.data) or is(args.datapage) then local buildargs = local nooverlap = yesno(args.nooverlap) buildargs.barwidth = tonumber(barwidth) or 280 buildargs.data = is(args.datapage) and require('Module:Medical cases chart/data')._externalData(args) or args.data buildargs.divisor = args.divisor buildargs.numwidth = args.numwidth buildargs.collapsible = args.collapsible buildargs.right1data = args.right1data or -- if no right1data and right1 title is default, use 3rd classification not args.right1 and 3 buildargs.right2data = args.right2data or -- if no right2data and right2 title is deaths, use 1st classification (args.right2

i18n.noOfDeaths or args.right2

i18n.noOfDeaths2) and 1 --buildargs.changetype1 = mw.ustring.sub(args.changetype1 or (args.changetype or ),1,1) -- 1st letter --buildargs.changetype2 = mw.ustring.sub(args.changetype2 or (args.changetype or ),1,1) -- 1st letter buildargs.changetype1 = args.changetype1 buildargs.changetype2 = args.changetype2 buildargs.rowheight = args.rowheight buildargs.duration = duration if is(args.togglesbar) then buildargs.nooverlap = false else buildargs.nooverlap = nooverlap end barargs.bars, monthList = p._buildBars(buildargs) local lastRow = #monthList if nooverlap

true then if #monthList <= duration then nooverlap = false else lastRow = #monthList - duration end end

for i=1,(lastRow) do -- deduplicate months if monthList[i][1] ~= months[#months][1] then --new month if #months > 1 and i > 1 then months[#months][3] = monthList[i-1][2] --store end of previous month end months[#months+1] = monthList[i] --store start of this month end end months[#months][3] = monthList[lastRow][2] --store end of final month -- automatically generate toggles if yesno(args.collapsible)

true then if is(args.togglesbar) then togglesbar = args.togglesbar else local toggles = for i=1,#months do toggles[#toggles+1] = p._monthToggleButton end toggles[#toggles+1] = p._monthToggleButton togglesbar = '

\n' .. table.concat(toggles) .. '

' end end end 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 .. '/' or ) .. (args.location2 and args.location2 .. '/' or ) .. location .. ' medical cases chart'

local title = title[1] = (args.pretitle and args.pretitle .. ' ' or ) .. args.disease .. ' ' .. i18n.casesIn .. ' ' .. args.location .. (args.location2 and ', ' .. args.location2 or ) .. (args.location3 and ', ' .. args.location3 or ) .. (args.posttitle and ' ' .. args.posttitle or ) .. '

  (' .. navbar .. ')
' title[2] = p._legend0 if args.recoveries then title[3] = '   ' .. p._legend0 else title[3] = end title[4] = '   ' .. p._legend0 if args.altlbl2 then title[5] = '   ' .. p._legend0 else title[5] = end if args.altlbl3 then title[6] = '   ' .. p._legend0 ..'\n' else title[6] = '\n' end title[7] = togglesbar barargs.title = table.concat(title)

barargs.left1 = '

' .. -- 85-8 because of padding "" .. i18n.date .. "" .. '

'

barargs.right1 = '

' .. "" .. (args.right1 or i18n.noOfCases) .. "" .. '

'

if args.right2 then barargs.right2 = '

' .. "" .. args.right2 .. "" .. '

' end

barargs.caption = args.caption barargs.css = 'Template:Medical_cases_chart/styles.css' return barBox._box(barargs)end

function p.chart(frame) local tRawPams = getArgs(frame)

local tChartPams = p._getChartPams(tRawPams) --local tRawRows, tChartPams = p._getChartPams(tRawPams) --local tBoxPams = p._getBoxPams(tRawPams)

--tBoxPams.bars = p._getDataRows(tRawRows, tChartPams)

--return barBox._box(tBoxPams)

return p._chart(tChartPams)end

function p._getChartPams(tRawPams) --local tRawRows = tRawPams.data

--local tChartPams =

-- numwidth local sNumwidthLower = tRawPams.numwidth and mw.ustring.lower(tRawPams.numwidth) or nil local tNumWidthMap = local function _numwidth(p) local nw = mw.ustring.sub(sNumwidthLower or ,p,p) return tNumWidthMap[nw] or 0 end local numwidth = 120 local right1 = numwidth - 8 -- -8 because of padding if sNumwidthLower then numwidth = _numwidth(1) + 10 + _numwidth(2) if mw.ustring.len(sNumwidthLower)

4 then numwidth = numwidth + _numwidth(3) + _numwidth(4) if mw.ustring.sub(sNumwidthLower,3,3)

'n' then numwidth = numwidth + 6 else numwidth = numwidth + 10 end end right1 = _numwidth(1) + 2 + _numwidth(2) if not tRawPams.right2 and mw.ustring.len(sNumwidthLower)

4 then right1 = right1 + _numwidth(3) + _numwidth(4) if mw.ustring.sub(sNumwidthLower,3,3)

'n' then numwidth = numwidth + 6 else numwidth = numwidth + 10 end end end tRawPams.numwidthwidth = numwidth tRawPams.right1width = right1 if tRawPams.right2 then local right2 = _numwidth(3) + _numwidth(4) if mw.ustring.sub(tRawPams.numwidth,3,3)

'n' then right2 = right2 - 2 else right2 = right2 + 2 end tRawPams.right2width = right2 end -- barwidth and width local tBarWidthMap = local sBarWidthLower = tRawPams['barwidth'] and mw.ustring.lower(tRawPams['barwidth']) or nil tRawPams.barwidth = tBarWidthMap[sBarWidthLower] or 280 if tonumber(tRawPams.barwidth) then tRawPams.sTotalWidth = (85 + tRawPams.barwidth + numwidth) .. 'px' tRawPams.sBarWidth = tRawPams.barwidth .. 'px' else tRawPams.sTotalWidth = 'auto' tRawPams.sBarWidth = 'auto' end

-- recoveries, defaults to true for undefined or unrecognised values tRawPams.recoveries = not (yesno(tRawPams.recoveries)

false)

-- changetype[|1|2] local sChangeType = tRawPams.changetype and mw.ustring.lower(mw.ustring.sub(tRawPams.changetype, 1, 1)) or tRawPams.changetype1 = tRawPams.changetype1 and mw.ustring.lower(mw.ustring.sub(tRawPams.changetype1, 1, 1)) or sChangeType tRawPams.changetype2 = tRawPams.changetype2 and mw.ustring.lower(mw.ustring.sub(tRawPams.changetype2, 1, 1)) or sChangeType

-- float tRawPams.float = tRawPams.float or 'right'

-- duration (togglesbar) local bCanSetDur = yesno(tRawPams.collapsible) and not tRawPams.togglesbar tRawPams.nDuration = bCanSetDur and tonumber(tRawPams.duration) or 15

--return tRawRows, tChartPams return tRawPamsend

function p.barColors(frame) return p._barColors(tonumber(frame:getParent.args[1]))end

function p.buildBars(frame) local bars = p._buildBars(frame.args) return barsend

function p.monthToggleButton(frame) local args = args.month = args.active = frame:getParent.args.active or 'true' args.duration = frame:getParent.args.duration or 15 args.nonewline = true return p._monthToggleButton(args)end

return p