If you are using the Vector 2022 skin, dark mode has moved to Preferences > Appearance.
Module:Jam series detailed stats
This module operates all the mechanics behind Special:RunQuery/Jam series detailed stats, including Template:Jam series detailed stats, Visual novel jam statistics/entryHelper, Visual novel jam statistics/particHelper, and the "Show more detailed stats" links which appear on List of ongoing visual novel jams and List of discontinued visual novel jams.
For more information about how this system works at the highest level, see Template:Jam series detailed stats/doc.
Histograms
Special:RunQuery/Jam series detailed stats (and the template it is based on) contains two histograms (frequency charts): one for participants and one for entries. These histograms summarize all jam occurrences across all series, and are the same regardless of the jam series selected. Due to limitations in Semantic MediaWiki/Semantic Result Formats, a helper page is needed to be able to display these histograms.
Visual novel jam statistics/entryHelper and Visual novel jam statistics/particHelper serve as the helper pages. They hold SMW subobjects representing the different buckets in the histograms, which are then queried using SMW to display the final graph on the detailed stats page.
To generate these helper pages, this module contains the prepHistData
and prepHistDataCustom
functions. Each function will generate a set of subobjects which can be queried to generate a histogram.
The prepHistData
function accepts just two arguments - statType
(which can be either "entries" or "participants"), and width
, which is an integer representing the width of each histogram bucket. It will generate a bucket for 0, then begin at 1 and generate buckets from 1 to width, width+1 to 2*width, and so on until it reaches the bucket which contains the highest data point for that statType
.
The prepHistDataCustom
function accepts any number of arguments. The first argument is statType
, which can be either "entries" or "participants". The remaining arguments should be the limits of histogram buckets, formatted similarly to 1-10
, where 1 is the lower limit and 10 is the upper limit.
Example usage:
{{#invoke:Jam series detailed stats | prepHistData | statType=entries | width=10 }} {{#invoke:Jam series detailed stats | prepHistDataCustom | statType=entries |1-10|11-25|26-30|31-40|41-300 }}
statDesc
The statDesc
function generates the text description of the average, largest, and most recent occurrence statistics for a given jam. It is used in Template:Jam series detailed stats and has limited applications elsewhere.
It takes two arguments: statType
(either entries or participants) and seriesName
, which must be the official SMW-compatible name of a jam series.
shouldShow functions
The detailed jam stats page is intentionally restricted to jams that have at least one complete occurrence, and each statType (entries and participants) will only be shown if there is at least one complete occurrence where that statistic is recorded. The shouldShowDetailsButton
and shouldShowDetailsCat
functions control this behavior, and both functions rely on a helper function, shouldShowStat
, which is not accessible via invoke
.
The shouldShowDetailsButton
function determines whether the button that links to more detailed stats should be shown on List of ongoing visual novel jams or List of discontinued visual novel jams (as appropriate) for a given jam series. It accepts a single argument, seriesName
, and is invoked in Template:Jam series occurrence stats. If the button should be shown, it returns the string yes
. Otherwise, it returns a blank string (nothing).
The shouldShowDetailsCat
function determines whether the entries or participants section of the detailed stats page should be shown. It accepts two arguments: seriesName
and statType
. statType
can be either "entries" or "participants". If the details for the given statType and seriesName should be shown, it returns the string yes
. Otherwise, it returns a blank string (nothing).
Code
local p = {}
function p.prepHistData( frame )
local statType = frame.args['statType']
local width = frame.args['width']
local q = { '[[Jam occurrence:+]]', '?Has ' .. statType .. '=count' }
q.sort = 'Has ' .. statType
q.order = 'descending'
q.limit = 1
q.mainheader = '-'
local maxCount = mw.smw.ask( q )
local numIntervals = math.ceil( maxCount[1].count / width )
local zeroSubQ = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::0]]' }
zeroSubQ.format = 'count'
local zeroCount = mw.smw.ask( zeroSubQ )
local zeroCol = { 'Has sort order=0', 'Has label=None', 'Has count=' .. mw.smw.ask( zeroSubQ ) }
local resultZero = mw.smw.subobject( zeroCol )
local resultFinal = false
if resultZero == true then resultFinal = true end
for i=1,numIntervals do
local subQ = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::>' .. (width * (i-1)) + 1 .. ']]', '[[Has ' .. statType .. '::<' .. width * i ..']]' }
subQ.format = 'count'
local resultI = mw.smw.subobject ( { 'Has sort order=' .. i, 'Has label=' .. (width * (i-1)) + 1 .. '-' .. width * i, 'Has count=' .. mw.smw.ask( subQ ) } )
if resultI ~= true then resultFinal = false end
end
return resultFinal
end
function p.prepHistDataCustom( frame )
local statType = frame.args['statType']
local groups = {}
local toRet = ''
for i,arg in pairs( frame.args ) do
if arg ~= statType then
--toRet = toRet .. arg .. '\n'
table.insert(groups, mw.text.split(arg, '-', true ) )
end
end
--return toRet
local zeroSubQ = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::0]]' }
zeroSubQ.format = 'count'
local zeroCount = mw.smw.ask( zeroSubQ )
local zeroCol = { 'Has sort order=0', 'Has label=None', 'Has count=' .. mw.smw.ask( zeroSubQ ) }
local resultZero = mw.smw.subobject( zeroCol )
local resultFinal = false
if resultZero == true then resultFinal = true end
for i=1,#groups do
if groups[i] ~= nil then
local subQ = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::>' .. groups[i][1] .. ']]', '[[Has ' .. statType .. '::<' .. groups[i][2] ..']]' }
subQ.format = 'count'
local resultI = mw.smw.subobject ( { 'Has sort order=' .. groups[i][1], 'Has label=' .. groups[i][1] .. '-' .. groups[i][2], 'Has count=' .. mw.smw.ask( subQ ) } )
if resultI ~= true then resultFinal = false end
end
end
return resultFinal
end
function p.statDesc( frame )
local statType = 'entries'
if frame.args['statType'] ~= nil then statType = frame.args['statType'] end
local seriesName = 'Spooktober Visual Novel Jam'
if frame.args['seriesName'] ~= nil then seriesName = frame.args['seriesName'] end
local occurQuery = { '[[Jam occurrence:+]]', '[[Is occurrence of::' .. seriesName .. ']]', '[[Has end date::<' .. mw.getContentLanguage():formatDate('M j Y') .. ']]', '[[Has ' .. statType .. '::+]]', '?Has short name=shortName', '?Has ' .. statType .. '#-=count', '?Has end date#-F[U]=endDate' }
local occur = mw.smw.ask(occurQuery)
if type( occur ) ~= "table" then return 'ERROR' end
local numOccur = 0
local sumCount = 0
local largestCount = 0
local largestName = 'ERROR'
local newestCount = 0
local newestDate = 0
local newestName = 'ERROR'
for i=1, #occur do
if occur[i] ~= nil then
numOccur = numOccur + 1
sumCount = sumCount + occur[i].count
if occur[i].count > largestCount then
largestCount = occur[i].count
largestName = occur[i].shortName
end
if tonumber(occur[i].endDate) > newestDate then
newestCount = occur[i].count
newestDate = tonumber(occur[i].endDate)
newestName = occur[i].shortName
end
end
end
local avgCount = p.round(sumCount / numOccur, 2)
local toRet = ''
toRet = toRet .. seriesName .. ' has had ' .. avgCount .. ' ' .. statType .. ' on average, across ' .. numOccur .. ' occurrences. This is '
toRet = toRet .. p.prText( avgCount, statType ) .. ' of jam occurrences. \n\n'
toRet = toRet .. 'The largest occurrence by ' .. statType .. ', which was ' .. largestName .. ', had ' .. largestCount .. ' ' .. statType .. '. This is '
toRet = toRet .. p.prText( largestCount, statType ) .. ' of jam occurrences. \n\n'
toRet = toRet .. 'The most recent occurrence, which was ' .. newestName .. ', had ' .. newestCount .. ' ' .. statType .. '. This is '
toRet = toRet .. p.prText( newestCount, statType ) .. ' of jam occurrences.'
return toRet
end
function p.percentileRank( count, statType )
local cfQuery = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::<' .. count .. ']]' }
cfQuery.format = 'count'
local fQuery = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::' .. count .. ']]' }
fQuery.format = 'count'
local nQuery = { '[[Jam occurrence:+]]', '[[Has ' .. statType .. '::+]]' }
nQuery.format = 'count'
local cf = mw.smw.ask(cfQuery)
local f = mw.smw.ask(fQuery)
local n = mw.smw.ask(nQuery)
local pr = 0.5 * f
pr = cf - pr
pr = pr / n
pr = pr * 100
return p.round(pr, 0)
end
function p.prText( count, statType )
local text = 'higher'
local prNum = p.percentileRank( count, statType )
if prNum <= 50 then
text = 'lower'
prNum = 100 - prNum
end
return text .. ' than ' .. prNum .. '%'
end
function p.round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
function p.shouldShowDetailsButton( frame )
local seriesName = 'Spooktober Visual Novel Jam'
if frame.args['seriesName'] ~= nil then seriesName = frame.args['seriesName'] end
local toRet = ''
if p.shouldShowStat('entries', seriesName) == 'yes' or p.shouldShowStat('participants', seriesName) == 'yes' then toRet = 'yes' end
return toRet
end
function p.shouldShowDetailsCat( frame )
return p.shouldShowStat(frame.args['statType'], frame.args['seriesName'])
end
function p.shouldShowStat(statType, seriesName)
local occurQuery = { '[[Jam occurrence:+]]', '[[Is occurrence of::' .. seriesName .. ']]', '[[Has end date::<' .. mw.getContentLanguage():formatDate('M j Y') .. ']]', '[[Has ' .. statType .. '::+]]', '?Has short name=shortName', '?Has ' .. statType .. '#-=count', '?Has end date#-F[U]=endDate' }
local occur = mw.smw.ask(occurQuery)
if type( occur ) ~= "table" then return '' end
if #occur == nil or #occur == 0 then return '' end
local sumCount = 0
for i=1, #occur do
if occur[i] ~= nil then
if occur[i].count ~= nil then sumCount = sumCount + occur[i].count end
end
end
if sumCount == 0 then return '' end
return 'yes'
end
return p