Module:Television ratings graph: Difference between revisions

Content deleted Content added
Accessibility fixes, plus new parameter |season_title=
oops typo
 
(100 intermediate revisions by 16 users not shown)
Line 1:
-- This module implements {{Television ratings graph}}.
 
local contrast_ratio = require('Module:Color contrast')._ratio
 
--------------------------------------------------------------------------------
Line 7 ⟶ 9:
 
local TVRG = {}
 
function TVRG.hex2rgb(hex)
hex = hex:gsub('#', '')
if #hex == 3 then
return tonumber("0x"..hex:sub(1,1)..hex:sub(1,1))/256, tonumber("0x"..hex:sub(2,2)..hex:sub(2,2))/256,
tonumber("0x"..hex:sub(3,3)..hex:sub(3,3))/256
else
return tonumber("0x"..hex:sub(1,2))/256, tonumber("0x"..hex:sub(3,4))/256, tonumber("0x"..hex:sub(5,6))/256
end
end
 
-- Allow usages of {{N/A}} cells
Line 25 ⟶ 17:
infoParam = frame:expandTemplate{title='N/A',args={text}}
-- Gather styles of {{N/A}} and assign to node variable
while true do
local a,b = string.match(infoParam,attrMatch)
Line 38 ⟶ 31:
end
 
-- Create the graph and table
function TVRG.new(frame,args)
args = args or {}
local categories = ''
local title = mw.title.getCurrentTitle()
-- Variables
local timelineCode = ''
local timeline = ''
local longestseason = -1
local average = args.average and 1 or 0
local season_title = args.season_title or 'Season'
Line 47 ⟶ 45:
:attr('align', 'center')
-- Create the timeline
timelineCode = timelineCode .. "ImageSize = width:" .. (args.width or 1000) .. " height:" .. (args.height or 300) .. "\n"
timelineCode = timelineCode .. "PlotArea = left:50 bottom:70 top:20 right:50\n"
timelineCode = timelineCode .. "AlignBars = justify\n"
-- Number of actual viewer numbers
timelineCode = timelineCode .. "Colors =\n"
local numberargs = 0
timelineCode = timelineCode .. " id:a value:gray(0.7)\n"
for k,v in pairs(args) do
if (string.lower(v) == 'n/a') or (not string.match(k,'[^%d]+') and not string.match(v,'[^%d%.]+')) then numberargs = numberargs + 1 end
local max_season = -1;
end
 
-- Determine number of seasons
local num_seasons = -1
for k,v in pairs(args) do
local season_numthisseason = tonumber(string.sub(k,6))
if string.sub(k,1,5) == 'color' and season_numthisseason > max_seasonnum_seasons then
max_seasonnum_seasons = season_numthisseason
end
end
if num_seasons < 1 then
num_seasons = 1
end
 
-- Determine number of episodes and subtract averages if included (they should be equal to the number of seasons)
local num_episodes
if average == 1 then
num_episodes = numberargs-num_seasons
else
num_episodes = numberargs
end
 
-- Bar and graph width
local barwidth
if args.bar_width then barwidth = args.bar_width
local season_num = 1
elseif num_episodes >= 120 then barwidth = 6
for season_num=1,max_season do
elseif num_episodes >= 100 then barwidth = 7
local r,g,b = TVRG.hex2rgb(args['color' .. season_num] or '#006600')
elseif num_episodes >= 80 then barwidth = 8
timelineCode = timelineCode .. " id:season" .. season_num .. " value:rgb("..r..","..g..","..b..") legend:" ..
elseif num_episodes >= 50 then barwidth = 9
string.gsub((args["legend" .. season_num] or "Season " .. season_num), ' ', '_') .. "\n"
elseif num_episodes >= 20 then barwidth = 10
season_num = season_num + 1
else barwidth = 11
end
 
-- Determine maximum viewer figure
local max_number = -1;
local maxviewers = -1
local multiple = 'millions'
for k,v in pairs(args) do
local num = tonumber(v)
if tonumber(k) ~= nil and num ~= nil and num > max_numbermaxviewers then
max_numbermaxviewers = num
end
end
if maxviewers <= 1.5 then
multiple = 'thousands'
maxviewers = maxviewers*1000
for k, v in pairs(args) do
local num = tonumber(v)
if tonumber(k) ~= nil and num ~= nil then args[k] = tostring(num*1000) end
end
end
local is_singular = num_episodes == 1
 
-- Basis parameters
timelineCode = timelineCode .. "DateFormat = x.y\n"
timeline = timeline .. "\nImageSize = width:" .. (is_singular and 200 or 'auto') .. " height:"..(args.height or 500).. (is_singular and '' or " barincrement:"..(barwidth+(num_episodes == 2 and 16 or 4))).."\n"
timelineCode = timelineCode .. "Period = from:0 till:" .. math.ceil(max_number) .. "\n"
timeline = timeline .. "\nPlotArea = left:50 bottom:50 top:10 right:100\n"
timelineCode = timelineCode .. "TimeAxis = orientation:vertical\n"
timeline = timeline .. "\nAlignbars = justify\n"
timelineCode = timelineCode .. "ScaleMajor = gridcolor:a increment:1 start:0\n"
timeline = timeline .. "\nPeriod = from:0 till:"..math.ceil(maxviewers).."\n"
timelineCode = timelineCode .. "Legend = orientation:horizontal\n"
timeline = timeline .. "\nTimeAxis = orientation:vertical format:y\n"
timelineCode = timelineCode .. "PlotData =\n"
timelineCode = timelineCode .. " width:7\n"
-- Colors
timeline = timeline .. "\nColors =\n"
for season = 1,num_seasons do
args["color" .. season] = args["color" .. season] or '#CCCCFF'
hex = args["color" .. season]:gsub("#","")
if #hex == 3 then
-- If it's 3 hex chars then make it six instead
hex = hex:sub(1,1) .. hex:sub(1,1) .. hex:sub(2,2) .. hex:sub(2,2) .. hex:sub(3,3) .. hex:sub(3,3)
end
rgbR = tonumber("0x"..hex:sub(1,2))/256
rgbG = tonumber("0x"..hex:sub(3,4))/256
rgbB = tonumber("0x"..hex:sub(5,6))/256
local legend = season_title.."_"..(args["legend" .. season] and args["legend" .. season] or season )
timeline = timeline .. "\n id:season"..season.." value:rgb("..rgbR..", "..rgbG..", "..rgbB..") legend:"..legend:gsub(" ", "_").."\n"
end
timeline = timeline .. "\n id:bars value:gray(0.95)"
timeline = timeline .. "\nLegend = orientation:vertical position:right\n"
timeline = timeline .. "\nBackgroundColors = bars:bars\n"
timeline = timeline .. "\nScaleMajor = unit:year increment:"..(multiple == 'thousands' and 100 or 1).." start:0\n"
timeline = timeline .. "\nScaleMinor = unit:year increment:"..(multiple == 'thousands' and 100 or 1).." start:0\n"
 
local x_intervals = args.x_intervals or (num_episodes >= 80 and 2 or 1)
 
timeline = timeline .. "\nBarData=\n"
for episode = 1,num_episodes do
episodetext = ((episode % x_intervals == 0) and episode or "")
timeline = timeline .. "\n bar:"..episode.." text:"..episodetext.."\n"
end
-- Add bars to timeline, one per viewer figure
local bar = 1
local season = 10
local max_episodesthisseason = -10
local this_episodescounted_episodes = 0
timeline = timeline .. "\nPlotData=\n"
for k,v in pairs(args) do
timeline = timeline .. "\n width:"..barwidth.." textcolor:black align:left anchor:from shift:(10,-4)\n"
if tonumber(k) ~= nil then
if v == '-' then
for k,v in ipairs(args) do
timelineCode = timelineCode .. " color:season" .. season .. "\n"
if string.lower(v) == 'n/a' then v = '' end
season = season + 1
if this_episodes > max_episodes then
if v == '-' then
max_episodes = this_episodes
-- Hyphen means new season
end
season = season + 1
this_episodes = 0
 
elseif average == 0 or (average == 1 and (args[k+1] == '-' or args[k+1] == nil) == false) then
-- Determine highest number of counted_episodes in a season
timelineCode = timelineCode .. " bar:" .. bar .. " from:start till:" .. (v ~= '' and v or 'start') .. "\n"
if thisseason > longestseason then
this_episodes = this_episodes + 1
barlongestseason = bar + 1thisseason
end
thisseason = 0
elseif average == 0 or (average == 1 and args[k+1] ~= '-' and args[k+1] ~= nil) then
-- Include bar for viewer figure, do not include if averages are included and the next parameter is a new season marker
timeline = timeline .. "\n bar:"..bar.." from:0 till:"..(v ~= '' and v or 0).." color:season"..season.."\n"
-- Increment tracking variables
counted_episodes = counted_episodes + 1
thisseason = thisseason + 1
bar = bar + 1
end
end
-- Determine highest number of episodes in a season after final season's bars
if thisseason > longestseason then
longestseason = thisseason
end
-- Axis labels
local timeline = frame:extensionTag('timeline', timelineCode)
local countryDisplayUS, countryDisplayUK, countryDisplayOther
root:node(timeline)
if args.country ~= nil and args.country ~= '' then
if args.country == "U.S." or args.country == "US" or args.country == "United States" then countryDisplayUS = 'U.S.'
elseif args.country == "U.K." or args.country == "UK" or args.country == "United Kingdom" then countryDisplayUK = 'UK'
else countryDisplayOther = args.country end
end
-- If there's a title, add it with the viewers caption, else just display the viewers caption by itself
local rtable = mw.html.create('table')
:addClass('wikitable')
:css('text-align', 'center')
if args.title ~= nil and args.title ~= '' then
rtable:tag('caption')root:wikitext("'''''" .. args.title .. "''" .. frame"&#8202;" .. ":expandTemplate{ title" =.. 'hsp'((countryDisplayUS }or countryDisplayUK or countryDisplayOther) or "") .. ((countryDisplayUS or countryDisplayUK or countryDisplayOther) and ": Viewersv" or "V") .. "iewers per episode (millions" .. multiple .. ")'''"):css('margin-top', '1em')
else
rtable:tag('caption')root:wikitext("'''Viewers per episode (millions" .. multiple .. ")'''"):css('margin-top', '1em')
end
root:tag('div'):css('clear','both')
-- Add timeline to div
local row = rtable:tag('tr')
if args.no_graph == nil then
row:tag('th'):wikitext(season_title)
if num_episodes > 100 then
root:tag('div'):wikitext("Too many episodes to display graph (maximum 100).")
for i=1,max_episodes do
if title.namespace == 0 then
row:tag('th')
categories = categories .. '[[Category:Articles using Template:Television ratings graph with excessive figures]]'
:attr('scope','col')
end
:wikitext(frame:expandTemplate{ title='abbr', args={ 'Ep.', 'Episode' } } .. ' ' .. i)
else
timelineBase = frame:preprocess("<timeline>"..timeline.."</timeline>")
root:node(timelineBase)
root:tag('div'):css('clear','both')
end
end
-- Create ratings table
if average == 1 then
if args.no_table == nil then
row:tag('th')
local rtable = mw.html.create('table')
:attr('scope','col')
:wikitextaddClass("'wikitable'Average''")
:css('text-align', 'center')
end
-- Create headers rows
local season = 1
local this_episodesrow = 0rtable:tag('tr')
row:tag('th'):wikitext(season_title)
:attr('scope','col')
for k,v in pairs(args) do
:attr('colspan','2')
if tonumber(k) ~= nil then
:attr('rowspan','2')
if v == '-' or (average == 1 and args[k+1] == nil) then
:css('padding-left', '.8em')
if season > 1 then
:css('padding-right', '.8em')
if this_episodes < max_episodes then
row:node(TVRG.NACell(frame,"N/A"):attr('colspan',max_episodes-this_episodes))
row:tag('th')
:attr('scope','colgroup')
:attr('colspan',longestseason)
:wikitext("Episode number")
:css('padding-left', '.8em')
:css('padding-right', '.8em')
-- Average column
if average == 1 then
row:tag('th')
:attr('scope','col')
:attr('rowspan','2')
:wikitext("Average")
:css('padding-left', '.8em')
:css('padding-right', '.8em')
end
 
local row = rtable:tag('tr')
for i = 1,longestseason do
row:tag('th')
:attr('scope','col')
:wikitext(i)
end
local season = 1
local thisseason = 0
-- Create table rows and cells
for k,v in pairs(args) do
if tonumber(k) ~= nil then
-- New season marker, or final episode rating
if v == '-' or (average == 1 and args[k+1] == nil) then
if season > 1 then
-- Spanning empty cells with {{N/A}}
if thisseason < longestseason then
row:node(TVRG.NACell(frame,"–"):attr('colspan',longestseason-thisseason))
end
if average == 1 then
-- If averages included, then set the averages cell with value or TBD
if v ~= '' then
row:tag('td'):wikitext(args[k+1] ~= nil and args[k-1] or v)
else
row:node(TVRG.NACell(frame,"TBD"))
end
thisseason = thisseason + 1
end
end
if-- averageNew ==season 1 thenmarker
if v ~== '-' then
-- New row with default or preset caption
row:tag('td'):wikitext(args[k+1] ~= nil and args[k-1] or v)
elserow = rtable:tag('tr')
row:nodetag(TVRG.NACell(frame,"TBD")'th')
:css('background-color', args['color' .. season])
end
:css('width','14px')
this_episodes = this_episodes + 1
:css('padding','0')
row:tag('th')
:attr('scope','row')
:wikitext(args["legend" .. season] and args["legend" .. season] or season)
thisseason = 0
season = season + 1
end
elseif average == 0 or (average == 1 and args[k+1] ~= '-' and args[k+1] ~= nil) then
-- Viewer figures, either as a number or TBD
if string.lower(v) == 'n/a' then
row:node(TVRG.NACell(frame,"N/A"))
elseif v ~= '' then
row:tag('td'):wikitext(v)
:css('width', '35px')
else
row:node(TVRG.NACell(frame,"TBD"))
end
thisseason = thisseason + 1
end
if v == '-' then
row = rtable:tag('tr')
row:tag('th')
:attr('scope','row')
:wikitext(args["legend" .. season] and "''"..args["legend" .. season].."''" or season_title .. ' ' .. season)
this_episodes = 0
season = season + 1
end
elseif average == 0 or (average == 1 and args[k+1] ~= '-' and args[k+1] ~= nil) then
if v ~= '' then
row:tag('td'):wikitext(v)
else
row:node(TVRG.NACell(frame,"TBD"))
end
this_episodes = this_episodes + 1
end
end
-- Finish by checking if final row needs {{N/A}} cells
if average == 0 and thisseason < longestseason then
row:node(TVRG.NACell(frame,"–"):attr('colspan',longestseason-thisseason))
end
-- Add table to div root and return
root:node(rtable)
root:tag('div'):css('clear','both')
end
local current_monthyear = os.date("%B %Y")
if this_episodes < max_episodes then
local span = mw.html.create('span'):wikitext(frame:expandTemplate{title='Citation needed', args={date=current_monthyear}})
row:node(TVRG.NACell(frame,"N/A"):attr('colspan',max_episodes-this_episodes))
if countryDisplayUS then
root:wikitext("<small>Audience measurement performed by [[Nielsen Media Research]]</small>" .. (args.refs ~= '' and args.refs or tostring(span)))
elseif countryDisplayUK then
root:wikitext("<small>Audience measurement performed by [[Broadcasters' Audience Research Board]]</small>" .. (args.refs ~= '' and args.refs or tostring(span)))
else
root:wikitext("<small>Source: </small>" .. (args.refs ~= '' and args.refs or tostring(span)))
end
return tostring(root) .. categories
root:node(rtable)
return tostring(root)
end