|
|
| Line 1: |
Line 1: |
| -- HELPER FUNCTIONS --
| | return require [[Module:Xviews]] |
| local function notblank(v) return (v or '' ) ~= '' end
| |
| local function ifblank(v, alt1, alt2) return notblank(v) and v or (notblank(alt1) and alt1 or alt2) end
| |
| local function red(v) return '<span style="color: red;">' .. v .. '</span>' end
| |
| local function getContent(page) return string.gsub(mw.title.new(page).content, '^Date,"[^"]+"', '') end
| |
| local function diffDays(dargs)
| |
| return require('Module:diff days')['diff_days'](
| |
| mw.getCurrentFrame():newChild{ title = 'Template:Diff_days', args = dargs }
| |
| )
| |
| end
| |
| local showTimeLine = require('Module:Horizontal timeline')._showTimeLine
| |
| local Text = require('Module:Text').Text()
| |
| | |
| -- EXPORTED CONSTANTS --
| |
| local p = {
| |
| gAGING = function() return 7 end, --global for representing the age in days after which a pageviews file, and any chart based on it, is no longer fresh and should be updated. Charts are still displayed for articles with aging pageviews files; when the pageviews file reaches gSTALE, the chart is no longer displayed.
| |
| gCOUNT = function() return 75 end, --global for representing the default number of days to display in the chart; overridable with param {{para|ct}}.
| |
| gSTALE = function() return 30 end, --global for representing the age in days after which a pageviews file, and any chart based on it, becomes stale and no longer appropriate for display on a Talk page. Stale pages should have their pageviews file updated.
| |
| }
| |
| | |
| -- TEMPLATE FUNCTIONS --
| |
| function p._xviews(args) -- {{xviews}}
| |
| -- args contains stale, days, ct, log, and mode
| |
| local page = ifblank(args[1], mw.title.getCurrentTitle().fullText .. '/pageviews')
| |
| if mw.title.new(page).exists then
| |
| local stale = tonumber(ifblank(args.stale, p.gSTALE()))
| |
| if tonumber(p._age(page)) > stale then --stale:
| |
| return red(mw.ustring.format(
| |
| 'The pageviews file [[%s]] is more than %d days old; please see [[Template:Xviews#Instructions|Instructions]].',
| |
| page, stale
| |
| ))
| |
| else --not stale:
| |
| return p._xvmain(page, {ct = ifblank(args.days, args.ct, p.gCOUNT())}) ..
| |
| ((notblank(args.log) or notblank(args.mode)) and '' or p._xAxis(page, {})) -- p._xAxis() doesn't handle log values or vertical bars yet
| |
| end
| |
| else
| |
| return red('Missing required pageviews file.') ..
| |
| ' See [[Template:Xviews#Instructions|Instructions]].'
| |
| end
| |
|
| |
| end
| |
| function p.xviews(frame)
| |
| return p._xviews(frame.args)
| |
| end
| |
|
| |
| function p._age(page) -- {{xviews/age}}
| |
| local nthview = p._nthView(page, p._viewCount(page))
| |
| nthview = Text.split(nthview, ':', true)[1]
| |
| return diffDays({nthview, mw.getContentLanguage():formatDate('j F Y'), precision=0})
| |
| end
| |
| function p.age(frame)
| |
| return p._age(ifblank(frame.args[1] or 'Talk:Liberation of France/pageviews'))
| |
| end
| |
| | |
| function p._latest(page) -- {{xviews/age}}
| |
| return Text.split(p._nthView(page, p._viewCount(page)), ':', true)[2]
| |
| end
| |
| function p.latest(frame)
| |
| return p._latest(ifblank(frame.args[1] or 'Talk:Liberation of France/pageviews'))
| |
| end
| |
| | |
| function p._nthView(page, index) -- {{xviews/nth view}}
| |
| local content = string.gsub(getContent(page), '.?(202%d%-%d%d%-%d%d)%,?(%d+)', '%1:%2,')
| |
| content = string.gsub(content, "^%s*,%s*(.-)%s*,$", "%1")
| |
| local items = {}
| |
| for item in string.gmatch(content, "([^,]+)") do
| |
| table.insert(items, mw.text.trim(item))
| |
| end
| |
| index = tonumber(index)
| |
| if index < 0 then
| |
| index = #items + index + 1
| |
| end
| |
| return items[index]
| |
| end
| |
| function p.nthView(frame)
| |
| return p._nthView(
| |
| ifblank(frame.args[1] or 'Talk:Liberation of France/pageviews'),
| |
| ifblank(frame.args[2] or 3)
| |
| )
| |
| end
| |
| | |
| function p._viewCount(page) -- {{xviews/view count}}
| |
| local str = string.gsub(getContent(page), '.?202%d%-%d%d%-%d%d', '')
| |
| str = string.gsub(str, ',', '', 1)
| |
| -- Remove leading and trailing delimiters (along with any surrounding whitespace)
| |
| str = str:gsub("^%s*,%s*", ""):gsub("%s*,%s*$", "")
| |
| -- Normalize internal consecutive delimiters to a single delimiter (replace ",," with ",")
| |
| str = str:gsub("%s*,%s*,%s*", ',')
| |
| return select(2, str:gsub(',', '')) + 1
| |
| end
| |
| function p.viewCount(frame)
| |
| return p._viewCount(ifblank(frame.args[1], 'Talk:Liberation of France/pageviews'))
| |
| end
| |
| | |
| function p._xvmain(page, xargs) -- {{xviews/xvmain}}
| |
| -- xargs contains ct, log, mode, and debug
| |
| local output = {}
| |
| local stop = p._viewCount(page)
| |
| local start = stop - math.min(152, ifblank(xargs.ct, 93)) + 1
| |
| local iargs = {
| |
| page = page,
| |
| max = p._maxViews(page),
| |
| log = xargs.log, -- logarithmic scale (not implemented yet)
| |
| mode = xargs.mode, -- bar display orientation (mode=vert not implemented yet)
| |
| debug = xargs.debug, -- set to non-empty for loop debug output emitted by p._itemBar()
| |
| }
| |
| for n = start, stop do
| |
| iargs.n = n
| |
| table.insert(output, p._itemBar(iargs))
| |
| end
| |
| return table.concat(output)
| |
| end
| |
| function p.xvmain(frame)
| |
| local xargs = frame.args
| |
| xargs.ct = ifblank(xargs.ct, 93)
| |
| return p._xvmain(ifblank(xargs[1], 'Talk:Liberation of France/pageviews'), xargs)
| |
| end
| |
| | |
| function p._itemBar(iargs) -- {{xviews/item bar}}
| |
| -- iargs contains n, page, max, log, mode, and debug
| |
| if iargs.debug then
| |
| return mw.ustring.format("page=%s, n=%s, label=%s",
| |
| ifblank(iargs.page, 'missing'),
| |
| ifblank(iargs.n, 'missing'),
| |
| ifblank(iargs.label, 'missing')
| |
| )
| |
| end
| |
| local nthview = p._nthView(iargs.page, ifblank(iargs.n, 1))
| |
| if not nthview then
| |
| error("Error generating item bar for item #" .. ifblank(iargs.n, 1) .. ". Range may be invalid")
| |
| end
| |
| local val = Text.split(nthview, ':', true)[2]
| |
| local width = val * 100 * 0.88 / p._maxViews(iargs.page)
| |
| width = tonumber(string.format("%.2f", width))
| |
| local label = p._xlabel(Text.split(nthview, ':', true)[1], 'y')
| |
| return p._vbar(width, val, {label = label})
| |
| end
| |
| function p.itemBar(frame)
| |
| local iargs = frame.args
| |
| iargs.page = ifblank(iargs.page, 'Talk:House of the Dragon/pageviews')
| |
| iargs.n = ifblank(iargs.n, 1)
| |
| return p._itemBar(iargs)
| |
| end
| |
| | |
| function p._vbar(width, val, vargs) -- {{xviews/vbar}}
| |
| -- vargs contains label, label-style, thick, style, val, and val-style
| |
| local ts = mw.getCurrentFrame():extensionTag{
| |
| name = 'templatestyles',
| |
| args = {src = 'Template:Xviews/vbar/styles.css'}
| |
| }
| |
| local div = mw.html.create('div'):addClass('vbar')
| |
| if notblank(vargs.label) then
| |
| div:tag('span')
| |
| :addClass('vbar-labelh')
| |
| :cssText(ifblank(vargs['label-style']))
| |
| :wikitext(vargs.label)
| |
| end
| |
| div:tag('span')
| |
| :addClass('vbar-h')
| |
| :attr('alt', 'Page views for (date) = (views).')
| |
| :css('height', ifblank(vargs['thick']))
| |
| :css('width', width .. '%')
| |
| :cssText(ifblank(vargs.style))
| |
| if notblank(val) then
| |
| div:tag('span')
| |
| :addClass('vbar-valh')
| |
| :cssText(ifblank(vargs['val-style']))
| |
| :wikitext(val)
| |
| end
| |
| return ts .. tostring(div)
| |
| end
| |
| function p.vbar(frame)
| |
| local vargs = frame.args
| |
| return p._vbar(ifblank(vargs[1], 75), ifblank(vargs[2], vargs.val, 2136), vargs)
| |
| end
| |
| | |
| function p._xlabel(date, bold01) -- {{xviews/xlabel}}
| |
| local replace = ((date:sub(-3) == '-01') and notblank(bold01)) and "'''%1'''" or "%1"
| |
| return string.gsub(date, '20[23]%d%-(%d%d%-%d%d)', replace)
| |
| end
| |
| function p.xlabel(frame)
| |
| return p._xlabel(ifblank(frame.date, '2024-06-01'), frame.bold01)
| |
| end
| |
| | |
| function p._minViews(page) -- {{xviews/min views}}
| |
| local views = nil
| |
| local content = string.gsub(getContent(page), '.?202%d%-%d%d%-%d%d', '')
| |
| for v in Text.gsplit(content, ',', true) do
| |
| v = tonumber(v)
| |
| if views == nil or (v and v < views) then
| |
| views = v
| |
| end
| |
| end
| |
| return views
| |
| end
| |
| function p.minViews(frame)
| |
| return p._minViews(ifblank(frame.args[1], 'Talk:Liberation of France/pageviews'))
| |
| end
| |
| | |
| function p._maxViews(page) -- {{xviews/max views}}
| |
| local views = 0
| |
| local content = string.gsub(getContent(page), '.?202%d%-%d%d%-%d%d', '')
| |
| for v in Text.gsplit(content, ',', true) do
| |
| v = tonumber(v)
| |
| if (v and v > views) then views = v end
| |
| end
| |
| return views
| |
| end
| |
| function p.maxViews(frame)
| |
| return p._maxViews(ifblank(frame.args[1], 'Talk:Liberation of France/pageviews'))
| |
| end
| |
| | |
| function p._xAxis(page, xargs) -- {{xviews/x-axis}}
| |
| -- xargs contains shift, caption, ff
| |
| local div = mw.html.create('div')
| |
| :css('margin', '-22px 0 0 ' .. ifblank(xargs.shift, '16px'))
| |
| :wikitext(
| |
| showTimeLine({
| |
| caption = ifblank(xargs.caption, 'page views for ' .. mw.title.new(page).rootText),
| |
| border = 'none',
| |
| from = 0,
| |
| to = p._maxViews(page),
| |
| inc = tonumber(string.format("%.0f", ifblank(xargs.ff, 1.0) * p._maxViews(page)/100)) * 10,
| |
| row1 = 'scale',
| |
| ['axis-nudge'] = '-0.8em',
| |
| ['axis-0'] = '<span style="padding-left:10px">0</span>'
| |
| })
| |
| )
| |
| return tostring(div)
| |
| end
| |
| function p.xAxis(frame)
| |
| return p._xArgs(ifblank(frame.args[1], 'Talk:Liberation of France/pageviews'), frame.args)
| |
| end
| |
| | |
| function p._summary(page, sargs) -- {{Xviews/summary}}
| |
| -- sargs contains days
| |
| page = ifblank(page, mw.title.getCurrentTitle().fullText .. '/pageviews')
| |
| if not mw.title.new(page).exists then return '' end
| |
| return 'Pageviews [[Template:Xviews/summary#Description|summary]]: ' ..
| |
| 'size=' .. p._viewCount(page) ..
| |
| ', age=' .. p._age(page) ..
| |
| ', days=' .. ifblank(sargs.days, p.gCOUNT()) ..
| |
| ', min=' .. p._minViews(page) ..
| |
| ', max=' .. p._maxViews(page) ..
| |
| ', latest=' .. p._latest(page)
| |
| end
| |
| function p.summary(frame)
| |
| return p._summary(frame.args[1], frame.args)
| |
| end
| |
| | |
| function p._isStale(page, days) -- {{xviews/is stale}}
| |
| page = ifblank(page, mw.title.getCurrentTitle().fullText .. '/pageviews')
| |
| if not mw.title.new(page).exists then return 'missing' end
| |
| local age = tonumber(p._age(page))
| |
| if age > tonumber(ifblank(days, p.gSTALE())) then return 'stale' end
| |
| if age > tonumber(ifblank(days, p.gAGING())) then return 'aging' end
| |
| return ''
| |
| end
| |
| function p.isStale(frame)
| |
| return p._isStale(frame.args[1], frame.args[2])
| |
| end
| |
| | |
| return p
| |