local getArgs = require('Modül:Bağımsız değişkenler').getArgs local yesno = require('Modül:Evethayır') local barBox = require('Modül:Sütun kutusu') local lang = mw.getContentLanguage local language = lang:getCode local i18n = require("Modül:Tıbbi vaka tablosu/i18n")[language] assert(i18n, 'no chart translations to: ' .. mw.language.fetchLanguageName(language, 'tr')) local monthAbbrs = {} for i = 1, 12 do monthAbbrs = lang:formatDate('F', '2020-' .. ('%02d'):format(i)) end local p = {} function p._toggleButton(active, customtoggles, id, label) local on = active and or ' mw-collapsed' local off = active and ' mw-collapsed' or local outString = .. .. .. return outString end function p._yearToggleButton(year) return p._toggleButton(year.l, ' mw-customtoggle-' .. year.year, year.year, year.year) end function p._monthToggleButton(year, month) local lmon, label = lang:lc(month.mon), month.mon local id = (year or ) .. lmon local customtoggles = ' mw-customtoggle-' .. id if month.s then label = label .. .. month.s -- "Mmm ##" if month.s ~= month.e then -- "Mmm ##–##" label = label .. '–' .. month.e end else customtoggles = customtoggles .. (month.l and customtoggles .. month.l or ) end for i, combination in ipairs(month.combinations) do customtoggles = customtoggles .. ' mw-customtoggle-' .. combination -- up to 2 combinations per month so no need to table.concat end return p._toggleButton(false, customtoggles, id, label) end function p._lastXToggleButton(years, duration, combinationsL) local months, id = years[#years].months, 'l' .. duration local i, customtoggles = #months, {' mw-customtoggle-' .. id} if #years > 1 then local year = years[#years].year while months.l do customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. year .. lang:lc(months.mon) .. '-' .. id if i 1 then if year years[#years].year then year = years[#years-1].year months = years[#years-1].months i = #months else -- either first month is also lastX month or lastX spans more than 2 years, which is not intended yet break end else i = i - 1 end end else while i > 0 and months.l do customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. lang:lc(months.mon) .. '-' .. id i = i - 1 end end for i, combinationL in ipairs(combinationsL) do customtoggles[#customtoggles+1] = ' mw-customtoggle-' .. combinationL -- up to 3 combinationsL in 90 days end return p._toggleButton(true, table.concat(customtoggles), id, 'Son' .. duration .. 'gün') end function p._buildTogglesBar(dateList, duration, nooverlap) local years = } local months, combinationsL = years[1].months, {} local function addMonth(month) if month.mon ~= months[#months].mon then -- new month if month.year ~= years[#years].year then -- new year years[#years+1] = {year=month.year, months={}} months = years[#years].months -- switch months list end months[#months+1] = {mon=month.mon, combinations={}} end end for i = 2, #dateList do -- deduplicate years and months if #dateList 0 then -- specific date addMonth(dateList) months[#months].l months[#months].l or dateList.l -- so that both ...-mon and ...-mon-lX classes are created elseif #dateList 1 then -- interval within month addMonth(dateList[1]) months[#months].l = months[#months].l or dateList.l else -- multimonth interval for j, month in ipairs(dateList) do addMonth(month) months[#months].combinations[#months[#months].combinations+1] = dateList.id end combinationsL[#combinationsL+1] = dateList.id:find('-l%d+$') and dateList.id end end if nooverlap then local lastDate = dateList[#dateList] months[#months].e = tonumber(os.date('%d', lastDate.nDate or lastDate.nEndDate or lastDate.nAltEndDate)) -- end of final month local i = #dateList repeat i = i - 1 until i 0 or (dateList.mon or dateList[1].mon) ~ months[#months].mon if i 0 then -- start of first and final month months[#months].s = tonumber(os.date('%d', dateList[1].nDate)) else months[#months].s = 1 end end years[#years].l = true -- to activate toggle and respective months bar local monthToggles, divs = {}, nil if #years > 1 then local yearToggles, monthsDivs = {}, {} for i, year in ipairs(years) do yearToggles[#yearToggles+1] = p._yearToggleButton(year) monthToggles = {} months = year.months for j, month in ipairs(months) do monthToggles[#monthToggles+1] = p._monthToggleButton(year.year, month) end monthsDivs[#monthsDivs+1] = end divs = .. table.concat(monthsDivs) else for i, month in ipairs(months) do monthToggles[#monthToggles+1] = p._monthToggleButton(nil, month) end divs = end divs = divs .. return end function p._barColors local colors = { 'Black', --deaths 'SkyBlue', --recoveries 'Tomato', --cases or altlbl1 'MidnightBlue', --altlbl2 'SlateBlue' --altlbl3 } return colors[n] end function p._customBarStacked(args) local barargs = {} barargs[1] = args[1] local function _numwidth(p) local nw = args.numwidth:sub(p, p) if nw 'n' then return 0 elseif nw 't' then return 2.45 elseif nw 'm' or nw 'd' then return 3.5 elseif nw 'w' then return 4.55 elseif nw 'x' then return 5.6 end end barargs[2] = .. if #args.numwidth 4 then local pad args.numwidth:sub(3, 3) 'n' and '0' or '0.3em' barargs.note2 = .. end for i = 1, 5 do barargs[2*i + 1] = p._barColors(i) barargs[2*i + 2] = args[i+1] / args.divisor barargs['title' .. i] = args[i+1] end barargs.align = 'cdcc' barargs.collapsed = args.collapsed barargs.id = args.id barargs.rowstyle = 'line-height:'.. args.rowheight ..';' return barBox._stacked(barargs) end function p._row(args) local barargs = {} barargs[1] = (args[1] or '⋮') .. (args.note0 or ) barargs[2] = args[2] or 0 barargs[3] = args[3] or 0 if args['alttot1'] then barargs[4] = args['alttot1'] elseif args[4] then barargs[4] = (args[4] or 0) - (barargs[2] or 0) - (barargs[3] or 0) else barargs[4] = 0 end barargs[5] = args[5] or 0 if args['alttot2'] then barargs[6] = args['alttot2'] elseif args[6] then barargs[6] = (args[6] or 0) - (barargs[2] or 0) - (barargs[3] or 0) else barargs[6] = 0 end barargs[7] = args[7] local function changeArg(firstright, valuecol, changecol) local change = if args['firstright' .. firstright] then change = '(' .. i18n.na .. ')' elseif not args[1] and args[valuecol] then change = '(=)' else change = args[changecol] and '(' .. args[changecol] .. ')' or end change = change .. (args['note' .. firstright] or ) return change end barargs[8] = changeArg(1, 7, 8) barargs[9] = args[9] barargs[10] = changeArg(2, 9, 10) barargs.divisor = args.divisor barargs.numwidth = args.numwidth barargs.rowheight = args.rowheight local dates if args.collapsible then local duration = args.duration if args.daysToEnd >= duration then barargs.collapsed = 'y' else barargs.collapsed = end if args.nooverlap and args.daysToEnd < duration then barargs.id = 'l' .. duration elseif args.nDate then dates = {year=tonumber(os.date('%Y', args.nDate)), mon=lang:formatDate('F', os.date('%Y-%m', args.nDate)), l=args.daysToEnd < duration and '-l' .. duration, nDate=args.nDate} barargs.id = (args.multiyear and dates.year or ) .. lang:lc(dates.mon) .. (dates.l or ) else local id, y, m, ey, em = {}, tonumber(os.date('%Y', args.nStartDate or args.nAltStartDate)), tonumber(os.date('%m', args.nStartDate or args.nAltStartDate)), tonumber(os.date('%Y', args.nEndDate or args.nAltEndDate )), tonumber(os.date('%m', args.nEndDate or args.nAltEndDate )) dates = {nStartDate=args.nStartDate, nAltStartDate=args.nAltStartDate, nEndDate=args.nEndDate, nAltEndDate=args.nAltEndDate} repeat id[#id+1] = (args.multiyear and y or ) .. lang:lc(monthAbbrs[m]) dates[#dates+1] = {year=y, mon=monthAbbrs[m]} y = y + math.floor(m / 12) m = m % 12 + 1 until y == ey and m > em or y > ey dates.l = args.daysToEnd < duration and '-l' .. duration id = table.concat(id, '-') .. (dates.l or ) barargs.id = id dates.id = id end else barargs.collapsed = barargs.id = end return p._customBarStacked(barargs), dates end function p._buildBars(args) local frame = mw.getCurrentFrame local updatePeriod = 86400 -- temporary implementation only supports daily updates local rows, prevRow = {}, {} for line in mw.text.gsplit(args.data, '\n') do local i, barargs, extrabarargs = 1, {}, nil -- parameter parsing, basic type/missing value handling for parameter in mw.text.gsplit(line, ';') do if parameter:find('^%s*%a') then parameter = mw.text.split(parameter, '=') parameter[1] = mw.text.trim(parameter[1]) if parameter[1]:find('^alttot') then parameter[2] = tonumber(frame:callParserFunction('#expr', parameter[2])) else parameter[2] = mw.text.trim(parameter[2]) if parameter[1]:find('^firstright') then parameter[2] = yesno(parameter[2]) elseif parameter[2] then parameter[2] nil end end barargs[parameter[1]] parameter[2] else parameter mw.text.trim(parameter) if parameter ~ then if i > 2 and i < 6 then parameter tonumber(frame:callParserFunction('#expr', parameter)) assert(parameter, 'data parameters 2 to 6 must not be formatted') end barargs parameter end i i + 1 end end -- get relevant date info based on previous row if barargs[1] then barargs.nDate lang:formatDate('U', barargs[1]) if prevRow.nDate or prevRow.nEndDate then if barargs.nDate - (prevRow.nDate or prevRow.nEndDate) > updatePeriod then extrabarargs {nStartDate (prevRow.nDate or prevRow.nEndDate)+updatePeriod, nEndDate barargs.nDate-updatePeriod, noData true} rows[#rows+1] extrabarargs prevRow extrabarargs end else prevRow.nEndDate barargs.nDate - updatePeriod end else if barargs.enddate then barargs.nEndDate lang:formatDate('U', barargs.enddate) end if prevRow.nDate or prevRow.nEndDate then barargs.nStartDate (prevRow.nDate or prevRow.nEndDate) + updatePeriod else prevRow.nAltEndDate (prevRow.nStartDate or prevRow.nAltStartDate) + (prevRow.noData and 0 or 1)*updatePeriod barargs.nAltStartDate prevRow.nAltEndDate + updatePeriod end if not barargs[2] and not barargs[3] and not barargs[4] and not barargs[5] and not barargs[6] then barargs.noData true 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 = barargs.alttot1 or barargs[4] prevnum = prevRow.alttot1 or prevRow[4] elseif data 'alttot2' then num barargs.alttot2 or barargs[6] prevnum prevRow.alttot2 or prevRow[6] elseif data then num barargs[data+1] prevnum prevRow[data+1] end if 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 not barargs['firstright' .. col] 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(rounding:format(percent)) -- round to two sigfigs if percent > 0 then change = '+' .. lang:formatNum(percent) .. '%' elseif percent < 0 then change = lang:formatNum(percent) .. '%' else change = '=' end end else -- data has not changed since previous row change = '=' end else -- no data on previous row barargs['firstright' .. col] = true -- set to (n.a.) end end end return value, change end if not barargs[7] then barargs[7], barargs[8] = fillCols(1, barargs[8]) end if not barargs[9] then barargs[9], barargs[10] = fillCols(2, barargs[10]) end rows[#rows+1] = barargs prevRow = barargs end -- calculate and pass repetitive (except daysToEnd) parameters to each row local lastRow = rows[#rows] local total = (lastRow[2] or 0) + (lastRow[3] or 0) + (lastRow[5] or 0) + (lastRow.alttot1 or lastRow[4] and lastRow[4] - (lastRow[2] or 0) - (lastRow[3] or 0) or 0) + (lastRow.alttot2 or lastRow[6] and lastRow[6] - (lastRow[2] or 0) - (lastRow[3] or 0) or 0) local divisor = total / (0.95 * args.barwidth) local firstDate, lastDate = rows[1].nDate, lastRow.nDate or lastRow.nEndDate local multiyear = os.date('%Y', firstDate) ~= os.date('%Y', lastDate - (args.nooverlap and args.duration * 86400 or 0)) if args.collapsible ~= false then args.collapsible = (lastDate - firstDate) / 86400 >= args.duration end local bars, dateList = {}, {} for i, row in ipairs(rows) do -- build rows row.divisor = divisor row.numwidth = args.numwidth row.rowheight = args.rowheight row.collapsible = args.collapsible row.duration = args.duration row.nooverlap = args.nooverlap row.daysToEnd = (lastDate - (row.nDate or row.nEndDate or row.nAltEndDate)) / 86400 row.multiyear = multiyear bars[#bars+1], dateList[#dateList+1] = p._row(row) end return table.concat(bars), dateList end function p._legend0(args) return end function p._chart(args) for key, value in pairs(args) do if ({float=1, barwidth=1, numwidth=1, changetype=1})[key:gsub('%d', )] then args[key] = value:lower end end local barargs = {} barargs.css = 'Şablon:Tıbbi vaka tablosu/styles.css' barargs.float = args.float or 'right' args.barwidth = args.barwidth or 'medium' local barwidth if args.barwidth 'thin' then barwidth 120 elseif args.barwidth 'medium' then barwidth = 280 elseif args.barwidth 'wide' then barwidth 400 elseif args.barwidth 'auto' then barwidth = 'auto' else error('unrecognized barwidth') end local function _numwidth(p) local nw = args.numwidth:sub(p, p) if nw 'n' then return 0 elseif nw 't' then return 40 elseif nw 'm' or nw 'd' then return 55 elseif nw 'w' then return 70 elseif nw 'x' then return 85 else error('unrecognized numwidth[' .. p .. ']') end end args.numwidth = args.numwidth or 'mm' local numwidth = _numwidth(1) + 10 + _numwidth(2) local right1width = numwidth if #args.numwidth 4 then numwidth numwidth + _numwidth(3) + _numwidth(4) if args.numwidth:sub(3, 3) 'n' then numwidth = numwidth + 6 else numwidth = numwidth + 10 end if not args.right2 then right1width = numwidth end end right1width = right1width - 8 -- -8 because of padding if tonumber(barwidth) then barargs.width = 85 + barwidth + numwidth .. 'px' barargs.barwidth = barwidth .. 'px' else barargs.width = 'auto' barargs.barwidth = 'auto' end local title = {} local function spaces local nbsp = return end local location = lang:ucfirst(mw.ustring.gsub(args.location, i18n.the_, )) local navbartitle = args.outbreak .. i18n._data .. '/' .. (args.location3 and args.location3 .. '/' or ) .. (args.location2 and args.location2 .. '/' or ) .. location .. i18n._medicalCasesChart local navbar = require('Modülolçub')._navbar title[1] = (args.pretitle and args.pretitle .. ' ' or ) .. args.location .. ' ' .. args.disease .. ' ' .. i18n.casesIn .. (args.location2 and ', ' .. args.location2 or ) .. (args.location3 and ', ' .. args.location3 or ) .. (args.posttitle and ' ' .. args.posttitle or ) .. spaces(2) ..'(' .. navbar({[1]=navbartitle, titleArg=':' .. mw.getCurrentFrame:getParent:getTitle, mini=1, nodiv=1}) .. ')' title[2] = p._legend0({[1]=p._barColors(1), [2]=i18n.deaths}) args.recoveries = args.recoveries nil and true or args.recoveries title[3] args.recoveries and spaces(3) .. p._legend0({[1] p._barColors(2), [2] args.reclbl or i18n.recoveries}) or title[4] spaces(3) .. p._legend0({[1] p._barColors(3), [2] args.altlbl1 or i18n.activeCases}) title[5] args.altlbl2 and spaces(3) .. p._legend0({[1] p._barColors(4), [2] args.altlbl2}) or title[6] args.altlbl3 and spaces(3) .. p._legend0({[1] p._barColors(5), [2] args.altlbl3}) or local togglesbar, buildargs nil, {} args.right1 args.right1 or i18n.noOfCases args.duration args.duration or 15 args.nooverlap args.nooverlap or false buildargs.barwidth tonumber(barwidth) or 280 buildargs.numwidth args.numwidth buildargs.rowheight args.rowheight or 1.6 if args.datapage then local externalData require('Modül:Tıbbi vaka tablosu/data')._externalData buildargs.data externalData(args) else buildargs.data args.data end -- if no right1data and right1 title is cases, use 3rd classification buildargs.right1data args.right1data or args.right1 i18n.noOfCases and 3 -- if no right2data and right2 title is deaths, use 1st classification buildargs.right2data = args.right2data or (args.right2 i18n.noOfDeaths or args.right2 i18n.noOfDeaths2) and 1 buildargs.changetype1 = (args.changetype1 or args.changetype or ):sub(1, 1) -- 1st letter buildargs.changetype2 = (args.changetype2 or args.changetype or ):sub(1, 1) -- 1st letter buildargs.collapsible = args.collapsible buildargs.duration = args.duration buildargs.nooverlap = args.nooverlap local dateList barargs.bars, dateList = p._buildBars(buildargs) if buildargs.collapsible then togglesbar = p._buildTogglesBar(dateList, args.duration, args.nooverlap) end title[7] = togglesbar and .. togglesbar or barargs.title = table.concat(title) barargs.left1 = barargs.right1 = if args.right2 then local right2width = numwidth - right1width - 16 -- -8-8... barargs.right2 = end barargs.caption = args.caption return barBox._box(barargs) end function p.chart(frame) local getArgs = require('Modül:Bağımsız değişkenler').getArgs local args = getArgs(frame, { valueFunc = function (key, value) if value and value ~= then key = key:gsub('%d', '') if ({rowheight=1, duration=1, rightdata=1})[key] then -- if key in {...} return tonumber(value) or value end if ({recoveries=1, collapsible=1, nooverlap=1})[key] then return yesno(value) end return value end return nil end }) return p._chart(args) end return p