Module:Country population/sandbox
Appearance
This is the module sandbox page for Module:Country population (diff). |
require('strict')
local fn = require('Module:Formatnum')
local mm = require('Module:Math')
local p ={}
local pargs ={}
local args={}
local data={}
p.main = function(frame) -- called from template
pargs = frame:getParent().args
local output
if output then
return frame:preprocess(output)
end
return p.errormsg("No valid options")
end
function p.getPattern(section)
local pattern = '<section begin=' .. section ..'[ ]*/>(.-)<section end='..section..'[ ]*/>'
return pattern
end
function p.getPopulationData(frame)
local page = "List of countries by population (United Nations)"
data['dates'] = {}
data['total'] = {}
data.total['latest'] = 0
data.total['previous'] = 0
data.total['projected'] = 0
--local total = 0
--local totalProjected = 0
local count = 0
local title = mw.title.new( page) -- , ns) -- creates object if page doesn't exist (and valid page name)
--TODO: could use mw.title.makeTitle(), but that needs ns
local content = title:getContent()
-- get dates
for value in string.gmatch( content , p.getPattern("date_1") ) do
data.dates['latest']=value -- date of latest data
end
for value in string.gmatch( content , p.getPattern("date") ) do
data.dates['previous']=mw.getContentLanguage():formatDate('j F Y', value)
end
data.dates['today'] = mw.getContentLanguage():formatDate('j F Y') -- today's date (for formatting see https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time)
-- get population data for each country passes as parameter
for k,v in pairs(args) do
local country = mw.text.trim(v)
-- get population data from section
local section = country .. "_1"
for value in string.gmatch( content , p.getPattern(section) ) do
count=count+1
data[count] = {}
data[count]['country'] = country
data[count]['populationString'] = frame:preprocess(value)
local raw = string.gsub(data[count]['populationString'], ",", "") -- strip formatting from number string
data[count]['populationNumber'] = tonumber(raw)
data.total['latest'] = data.total['latest'] + data[count]['populationNumber']
local section = country .. "_0"
for value2 in string.gmatch( content , p.getPattern(section) ) do
data[count]['populationString2'] = frame:preprocess(value2)
local raw = string.gsub(data[count]['populationString2'], ",", "") -- strip formatting from number string
data[count]['populationNumber2'] = tonumber(raw)
data.total['previous'] = data.total['previous'] + data[count]['populationNumber2']
data[count]['populationIncrement']=data[count]['populationNumber'] - data[count]['populationNumber2']
data[count]['populationGrowth'] =data[count]['populationIncrement']/data[count]['populationNumber2']
data[count]['populationDouble'] = p.getPopulationDoubleTime(data[count]['populationNumber'],data[count]['populationNumber2'])
data[count]['populationProjected'] = p.getPopulationProjection(data[count]['populationNumber'],data[count]['populationNumber2'])
data.total['projected'] = data.total['projected'] + data[count]['populationProjected']
end
end
end
return true
end
-- estimate time to double population based on latest growth rate
function p.getPopulationDoubleTime(latest,previous)
local growth = (latest - previous) / previous
local doubleTime = math.log( 2 ) / math.log(1 + growth)
return doubleTime
end
-- estimate today's population based on latest growth rate
function p.getPopulationProjection(latest,previous)
local ay = ( mw.getCurrentFrame():callParserFunction{ name = '#time', args = { "U", data.dates['today'] } }
- mw.getCurrentFrame():callParserFunction{ name = '#time', args = { "U", data.dates['previous'] } })
/60/60/24/365.2425 -- number of years since first date until today
local projected = math.pow(previous, 1 - ay ) * math.pow(latest, ay)
return projected
end
--[[ sort rows by population (defaults to latest population)
TODO add options for sorting columns
]]
function p.sortPopulationData(ByColumn)
local sort_function = function( a,b )
if (tonumber(a.populationNumber) > tonumber(b.populationNumber)) then -- primary sort on 'population' -> a before b
return true
end
end
table.sort(data, sort_function)
end
--[[ Function to get flag icon and handle special cases
1. There is an issue of non-standard sizes when used with static rank column
The three countries with extra height (and the required size parameter) are
Nepal/NPL (size=12px), Switzerland/CHE (size=15px), Vatican/VAT (size=15px)
a few have lower default heights so it doesn't matter (Poland, New Caledonia)
2. Alias, e.g. NEP->NPL, TRI->TTO
]]
function p.getFlagLabel(countryCode)
local output
local templateArgs = { countryCode }
local size
if countryCode == "CHE" or countryCode == "VAT" then
size="15px"
elseif countryCode == "NPL" then
size="12px"
end
if size then templateArgs['size'] = size end
-- simple version
--output = mw.getCurrentFrame():expandTemplate{ title = "flaglist", args = templateArgs }
-- method with fixed-height div and overflow
output = '<div style="height:15px;overflow:visible;" >'
.. mw.getCurrentFrame():expandTemplate{ title = "flaglist", args = templateArgs }
.. '</div>'
return output
end
--[[ output table of data as Wikitext table
]]
function p.tabulateDataWikitext(frame)
local output
local i = 1
-- output table
output = '{| class="wikitable sortable mw-datatable" style="text-align:right;" ' -- table
output = '|-class=wrap'
output = output --headers (top row)
.. '\n!rowspan=2|#'
.. '\n!rowspan=2|Country'
.. '\n!rowspan=2|Projected population<br/>(' .. data['dates']['today'] .. ')'
.. '\n!rowspan=2|Pct of<br>total'
.. '\n!colspan=2|UN Population estimates'
.. '\n!colspan=2|Annual growth'
.. '\n!rowspan=2|Doubling time<br/>(years)'
.. '\n|-' -- headers (second row)
.. '\n!' .. data.dates['latest']
.. '\n!' .. data.dates['previous']
.. '\n!Increment'
.. '\n!Rate'
while (data[i]) do -- add rows
output = output .. '\n|-\n|' .. i
output = output .. '\n|style="text-align:left;" |' .. frame:expandTemplate{ title = "flaglist", args = {data[i]['country'] } }
output = output .. '\n| ' .. mm._precision_format(data[i]['populationProjected'],0)
output = output .. '\n| ' .. mm._precision_format(data[i]['populationProjected']/data.total['projected']*100,2) .. "%" -- projected
output = output .. '\n| ' .. data[i]['populationString']
output = output .. '\n| ' .. data[i]['populationString2']
output = output .. '\n| ' .. mm._precision_format(data[i]['populationIncrement'],0)
output = output .. '\n| ' .. mm._round(data[i]['populationGrowth']*100,2) .. "%"
output = output .. '\n| ' .. mm._round(data[i]['populationDouble'],0)
i=i+1
end
local newcell = '\n! style="text-align:right;" | '
output = output .. '\n|-' -- totals row
.. '\n! !! Total'
.. newcell .. fn.formatNum(mm._round(data.total['projected'],0),"en",0)
.. newcell .. '100%'
.. newcell .. fn.formatNum(data.total['latest'],"en",0)
.. newcell .. fn.formatNum(data.total['previous'],"en",0)
.. newcell .. fn.formatNum(data.total['latest']-data.total['previous'],"en",0)
.. newcell .. fn.formatNum((data.total['latest']-data.total['previous'])/data.total['previous']*100,"en",2).."%"
.. newcell .. mm._precision_format(p.getPopulationDoubleTime(data.total['latest'],data.total['previous']),0)
output = output .. '\n|}'
return output
end
--[[ output table of data as use Lua HTML Library
]]
function p.tabulateData(frame)
local hideYearsCols = frame.args['hide_years'] or false
local doublingFootnote = frame.args['doubling_note'] or ""
local growthFootnote = frame.args['growth_note'] or ""
local i = 1
local static = mw.html.create('table'):addClass('wikitable')
static:tag('tr'):tag('th'):attr('rowspan', 1):wikitext('<br/>'):cssText('border-bottom-color:#eaecf0;')
static:tag('tr'):tag('th'):wikitext('<br/>'):cssText('border-top-color:#eaecf0;')
while (data[i]) do -- add rows
static:tag('tr'):tag('td'):wikitext(i)
i=i+1
end
static:tag('tr'):tag('th'):wikitext('<br/>')
local numRows=i-1
local tbl = mw.html.create('table'):addClass('wikitable') -- start table
:addClass('sortable')
:addClass('mw-datatable')
:addClass('static-row-numbers') -- prefix with row numbers using css and templatestyles
:addClass('nowrap')
:css('text-align','right')
local row = tbl:tag('tr') -- header row
:addClass('static-row-header') -- for templatestyles in some skins
--:tag('th'):attr('rowspan', 2):wikitext('#')
row :tag('th'):attr('rowspan', 2):wikitext('Country')
:tag('th'):attr('rowspan', 2):wikitext('Projected population<br/>(' .. data['dates']['today'] .. ')' )
:tag('th'):attr('rowspan', 2):wikitext('Pct of<br>total')
if not hideYearsCols then
row :tag('th'):attr('colspan', 2):wikitext('UN Population estimates')
end
row :tag('th'):attr('colspan', 2):wikitext('Annual growth'..growthFootnote)
:tag('th'):attr('rowspan', 2):wikitext('Doubling time<br/>(years)'..doublingFootnote)
row = tbl:tag('tr') -- headers (second row)
:addClass('static-row-header') -- for templatestyles in some skins
if not hideYearsCols then
row :tag('th'):wikitext(data.dates['latest'] )
:tag('th'):wikitext(data.dates['previous'] )
end
row :tag('th'):wikitext('Increment')
:tag('th'):wikitext('Rate')
i = 1
while (data[i]) do -- add country rows
local row=tbl:tag('tr')
--row :tag('td'):wikitext(i)
row :tag('td'):cssText("text-align:left;")
:wikitext( p.getFlagLabel(data[i]['country']) )
:tag('td'):wikitext( mm._precision_format(data[i]['populationProjected'],0) )
:tag('td'):wikitext( mm._precision_format(data[i]['populationProjected']/data.total['projected']*100,2) .. "%" ) -- % of projected
if not hideYearsCols then
row :tag('td'):wikitext( data[i]['populationString'] )
:tag('td'):wikitext( data[i]['populationString2'] )
end
row :tag('td'):wikitext( mm._precision_format(data[i]['populationIncrement'],0) )
:tag('td'):wikitext( mm._precision_format(data[i]['populationGrowth']*100,2) .. "%" )
:tag('td'):wikitext( mm._precision_format(data[i]['populationDouble'],0) )
i=i+1
end
local style = { ['text-align']='right' }
row = tbl:tag('tr') -- totals row
:addClass('static-row-header') -- for templatestyles in some skins
--:tag('th') :wikitext()
row :tag('th') :wikitext('Total')
:tag('th'):css(style):wikitext( fn.formatNum(mm._round(data.total['projected'],0),"en",0) )
:tag('th'):css(style):wikitext( '100%' )
if not hideYearsCols then
row :tag('th'):css(style):wikitext( fn.formatNum(data.total['latest'], "en",0) )
:tag('th'):css(style):wikitext( fn.formatNum(data.total['previous'],"en",0) )
end
row :tag('th'):css(style):wikitext( fn.formatNum(data.total['latest'] - data.total['previous'],"en",0) )
:tag('th'):css(style):wikitext( fn.formatNum((data.total['latest'] - data.total['previous']) / data.total['previous'] * 100,"en",2).."%" )
:tag('th'):css(style):wikitext( mm._precision_format(p.getPopulationDoubleTime(data.total['latest'],data.total['previous']),0) )
-- return tostring(tbl) -- return table without row numbers
-- use separate column for static row numbers
-- return '{|\n|style="vertical-align:top;" |' .. tostring(static) .. '\n|' .. tostring(tbl) .. '\n|}'
-- use css method and templatestyles to prefix row numbers
return p.templateStyle( frame, "Template:Static_row_numbers/styles.css" ) .. tostring(tbl)
end
--[[ currently the main entry function
takes list of country codes
gets population data from "List of countries by population (United Nations)"
outputs sorted table
]]
function p.populations(frame)
args = frame.args --TODO handle parent args for template
local page = "List of countries by population (United Nations)"
local title = mw.title.new( page) -- , ns) -- creates object if page doesn't exist (and valid page name)
--TODO: could use mw.title.makeTitle(), but that needs ns
local output = ""
if title and title.exists then
local content = title:getContent()
if not p.getPopulationData(frame) then
return p.errormsg("Error retrieving data.")
end
p.sortPopulationData("latest")
--output = p.tabulateDataWikitext(frame) -- version building table with Wikitext
output = p.tabulateData(frame) -- version building table with mw.html library
else
return '<span class="error">No page title found</span>'
end
local test = "test: "
local number=5435.12345
test= fn.formatNum(5435.12345,"en",0)
--test= frame:expandTemplate{ title = "formatnum", args = { totalProjected ,"en",0 } }
--test=frame:callParserFunction{ name = 'formatnum', args = { totalProjected, decs=2 } }
return output --.. test
end
-- function for pie chart
function p.piechart(frame)
args = frame.args --TODO handle parent args for template
local page = "List of countries by population (United Nations)"
local title = mw.title.new( page) -- , ns) -- creates object if page doesn't exist (and valid page name)
--TODO: could use mw.title.makeTitle(), but that needs ns
local output = ""
if title and title.exists then
local content = title:getContent()
if not p.getPopulationData(frame) then
return p.errormsg("Error retrieving data.")
end
p.sortPopulationData("latest")
--output = p.tabulateDataWikitext(frame) -- version building table with Wikitext
output = p.makePieChart(frame) -- version building table with mw.html library
else
return '<span class="error">No page title found</span>'
end
return output --.. test
end
function p.makePieChart(frame)
--local args=frame.args
local templateArgs = {}
templateArgs['caption'] = args['caption'] or "" --'South American population by country' --.. ' (top 8)'
templateArgs['thumb'] = args['thumb'] or "right"
templateArgs['other'] = args['other'] or nil
local maxSlices = tonumber(args['slices']) -- nil if not a number
if type(maxSlices) ~= "number" or maxSlices > 30 or maxSlices < 1 then
maxSlices = 30 -- limit of template -- get number from data
end
local i=1
while data[i] and i <= maxSlices do
--templateArgs['label'..i] = data[i]['country']
templateArgs['label'..i] = mw.getCurrentFrame():expandTemplate{ title = "getalias", args = { data[i]['country'], raw='y' } }
templateArgs['value'..i] = mm._round( data[i]['populationNumber']/data.total['latest']*100,1)
templateArgs['color'..i] = args['color'..i] or nil
i=i+1
end
--[[{{Pie chart
|caption= South American population by country (top 8)
|other = yes
|label1 = {{getalias|BRA}}
|value1 = {{#expr: {{country population|BRA|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label2 = {{getalias|COL}}
|value2 = {{#expr: {{country population|COL|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label3 = {{getalias|ARG}}
|value3 = {{#expr: {{country population|ARG|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label4 = {{getalias|PER}}
|value4 = {{#expr: {{country population|PER|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label5 = {{getalias|VEN}}
|value5 = {{#expr: {{country population|VEN|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label6 = {{getalias|CHL}}
|value6 = {{#expr: {{country population|CHL|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label7 = {{getalias|ECU}}
|value7 = {{#expr: {{country population|ECU|raw=y}} / {{xyz|Total}} * 100 round 1}}
|label8 = {{getalias|BOL}}
|value8 = {{#expr: {{country population|BOL|raw=y}} / {{xyz|Total}} * 100 round 1}}
}} ]]
local chart = mw.getCurrentFrame():expandTemplate{ title = "Pie chart", args = templateArgs }
return chart
end
function p.firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
p.errormsg = function (message)
return '<span class="error">' .. message .. '</span>'
end
-- Test why was the sort being applied to the wrong level? Fixed
function p.test(frame) -- meant test()
local tbl = mw.html.create('table'):addClass('wikitable'):addClass('sortable'):addClass('mw-datatable')
:css('text-align','right')
tbl:tag('tr') -- header row
:tag('th'):attr('rowspan', 2):wikitext('#')
:tag('th'):attr('rowspan', 2):wikitext('A')
:tag('th'):attr('rowspan', 2):wikitext('B')
:tag('th'):attr('colspan', 2):wikitext('C+D'):addClass('unsortable')
:tag('th'):attr('colspan', 2):wikitext('E+F'):addClass('unsortable')
:tag('th'):attr('rowspan', 2):wikitext('G')
tbl:tag('tr') -- headers (second row)
:tag('th'):wikitext('C'):addClass('sortable')
:tag('th'):wikitext('D'):addClass('sortable')
:tag('th'):wikitext('E'):addClass('sortable')
:tag('th'):wikitext('F') :addClass('sortable')
local i = 1
while (i<5) do -- add rows
tbl:tag('tr')
:tag('td'):wikitext(i)
:tag('td'):wikitext("A"..i)
:tag('td'):wikitext("B"..i)
:tag('td'):wikitext(tostring(math.fmod(5-i,2)) .. 'C' .. i )
:tag('td'):wikitext("D"..i)
:tag('td'):wikitext("E"..i)
:tag('td'):wikitext(tostring(math.fmod(5-i,2)) .. 'F' .. i )
:tag('td'):wikitext("G"..i)
i=i+1
end
local output = '{| class="wikitable sortable mw-datatable" style="text-align:right;" ' -- table
output = output
.. '\n!rowspan=2|#'
.. '\n!rowspan=2|A'
.. '\n!rowspan=2|B'
.. '\n!colspan=2|C+D'
.. '\n!colspan=2|E+F'
.. '\n!rowspan=2|G'
.. '\n|-' -- headers (second row)
.. '\n!C'
.. '\n!D'
.. '\n!E'
.. '\n!F'
i=1
while (i<5) do -- add rows
output = output .. '\n|-\n|' .. i
output = output .. '\n|A' .. i
output = output .. '\n|B' .. i
output = output .. '\n|' .. tostring(math.fmod(5-i,2)) .. 'C' .. i
output = output .. '\n|D' .. i
output = output .. '\n|E' .. i
output = output .. '\n|' .. tostring(math.fmod(5-i,2)) .. 'F' .. i
output = output .. '\n|G' .. i
i=i+1
end
output = output .. '\n|}'
return output .. tostring(tbl)
end
-- function for static rank column
function p.rank(frame)
--args = frame.args -- for module TODO allow invoke to work
args = frame:getParent().args -- parent arguments for template
args = frame.args -- invoke arguments for template
local caption = args['caption']
local valign = args['valign'] or "top"
local rowHeader = args['row-header']
local headerPadding = args['header-padding'] or "0px"
local textAlign = args['text-align'] or "right"
local style = args['style'] or ""
local headerHeight = args['header-height'] or ""
local headerLines = args['header-lines'] or 1
local headerText = args['header-text'] or ""
local rows = tonumber(args['rows']) or 0
local rowHeader = args['row-header']
local rowHeight = args['row-height']
local marginRight = "0px"
if rowHeader then marginRight = "-8px" end
local headerValign = "bottom"
if rowHeader then headerValign = "center" end -- copied from template; should be middle?
local linebreaks = ""
if headerLines then
local i=0
while i<tonumber(headerLines) do
linebreaks = linebreaks .. "<br />"
i=i+1
end
end
--[[
{|
|+'''{{{caption| }}}'''
| valign={{{valign|top}}} |
{| class="wikitable" style="margin-right:{{#if:{{{row-header|}}}|-8px|0px}}; padding:{{{header-padding|0px}}}; text-align:{{{text-align|right}}};{{{style|}}}"
! style=height:{{{header-height|}}} valign={{{header-valign|{{#if: {{{row-header|}}} | center | bottom}}}}} | {{#if:{{{header-lines|}}}|{{repeat|{{#expr:{{{header-lines}}}-1}}|<br>}}}}{{{header-text|}}}
]]
local heightClass = "static-rank-col"
if rowHeight and rowHeight == "large" then
heightClass = "static-rank-col-large"
end
local output = '\n{| class="'..heightClass..'"' --start static rank table
if caption then
output = output .. "\n|+'''" .. caption .. "'''"
end
output = output .. '\n|valign=' .. valign .. ' |'
.. '\n{| class="wikitable" style="margin-right:'..marginRight
..'; padding:'..headerPadding
..'; text-align:'..textAlign
..';'.. style
.. '\n! style="height:'..headerHeight..';" valign="'..headerValign ..';" | '
.. linebreaks .. headerText
--[[ {{#ifexpr:{{{rows}}}=0|<br />
{{end}}}}{{#ifexpr:{{{rows}}}>=1|{{Static column row |row-height={{{row-height|}}} |number=1 |row-header={{{row-header| }}} }}}}{{#ifexpr:{{{rows}}}=1|<br />
{{end}}}}{{#ifexpr:{{{rows}}}>=2|{{Static column row |row-height={{{row-height|}}} |number=2 |row-header={{{row-header| }}} }}}}{{#ifexpr:{{{rows}}}=2|<br />
{{end}}}
]]
local i=0
while i<rows do
i=i+1
--output = output .. '\n|-\n|' .. tostring(i) -- simple unformatted version
--[[ <br />
|- {{#if: {{{row-height|}}}|style="height:{{{row-height|}}}"|}}
{{#if: {{{row-header|}}} | ! | {{!}} }} {{{number}}}
]]
-- version emulating Template:Static column row
local rowStyle = ""
if rowHeight then rowStyle = 'style="height:'..rowHeight..';" |' end
local cellType = "|"
if rowHeader then cellType = "!" end
output = output .. '\n|-' .. rowStyle
.. '\n' .. cellType .. tostring(i) .. '<br />'
end
output = output .. '\n|}' -- close the static rank table
output = output .. '\n|' -- new cell for the main table
--output = output .. '\n|}' -- unnecessary: the table will be closed with an {{end}} template
return p.templateStyle( frame, "Static column begin/styles.css" ) .. output
end
function p.templateStyle( frame, src )
return frame:extensionTag( 'templatestyles', '', { src = src } );
end
return p