Jump to content

Module:CCI stats

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Chlod (talk | contribs) at 05:27, 6 February 2022 (bad values). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
local TableTools = require("Module:TableTools");
local getArgs  = require("Module:Arguments").getArgs;

local p = {}

--- Extracts case open and close dates from wikitext.
-- @param opens The table to append open dates to.
-- @param opens The table to append close dates to.
-- @param wikitext The wikitext to parse.
function p._extractCaseDates(opens, closes, wikitext)
    for openDate in mw.ustring.gmatch(wikitext, "data%-cci%-open=['\"](.-)['\"]") do
        table.insert(opens, tonumber(openDate))
    end
    for closeDate in mw.ustring.gmatch(wikitext, "data%-cci%-close=['\"](.-)['\"]") do
        table.insert(closes, tonumber(closeDate))
    end

    return opens, closes, wikitext
end

--- Returns a tuple containing case dates, the first being all the dates where a
-- case was opened, the second being all the dates where a case was closed. All
-- dates are in stats format (i.e. days since the first day of the UNIX epoch,
-- January 1, 1970).
-- @param noArchive `true` to exclude counting archives. `closes` will be empty.
function p._getCaseDates(noArchive)
    local frame = mw.getCurrentFrame()
    local opens = {}
    local closes = {}

    p._extractCaseDates(
        opens, closes, frame:expandTemplate{ title = "CCIlist" }
    )

    if not noArchive then
        p._extractCaseDates(
            opens, closes, frame:expandTemplate{ title = "Wikipedia:Contributor copyright investigations/Archive" }
        )
    end

    table.sort(opens)
    table.sort(closes)
    return opens, closes
end

--- Transforms a table of case opening and closing dates and returns a table
-- of overall case count change for each day. This function will does not return
-- any duplicate keys.
function p._caseDatesToCaseNet(opens, closes)
    local caseNet = {}

    for date in pairs(opens) do
        if not caseNet[date] then
            caseNet[date] = 1
        else
            caseNet[date] = caseNet[date] + 1
        end
    end

    for date in pairs(closes) do
        if not caseNet[date] then
            caseNet[date] = -1
        else
            caseNet[date] = caseNet[date] - 1
        end
    end

    return caseNet
end

--- Transforms a table of case net dates and returns a table with date:caseCount
-- pairs.
function p._caseNetToDateCaseCounts(caseNet)
    local dateCaseCounts = {}

    local lastValue = 0
    for date, net in TableTools.sortedPairs(caseNet) do
        dateCaseCounts[date] = lastValue + net
        lastValue = lastValue + net
    end

    return dateCaseCounts
end

--- Transforms date case counts to Vega data.
function p._dateCaseCountsToVega(dateCaseCounts)
    local values = {}

    for date, caseCount in TableTools.sortedPairs(dateCaseCounts) do
        table.insert(values, { 
            -- Multiply by seconds in a year to get UNIX timestamp.
            x = date * 31556952,
            y = caseCount
        })
    end

    return {
        data = {
            {
            	name = "cases",
            	values = values
            }
        },
        scales = {
            {
                name = "Date",
                type = "time",
                range = "width",
                domain = { data = "cases", field = "x" }
            },
            {
                name = "Open cases",
                range = "height",
                nice = true,
                domain = { data = "cases", field = "y" }
            }
        },
        axes = {
            { type = "x", scale = "Date" },
            { type = "y", scale = "Open cases" }
        },
        marks = {
            {
                type = "line",
                from = { data = "cases" },
                properties = {
                    enter = {
                        x = { scale = "Date", field = "x" },
                        y = { scale = "Open cases", field = "y" },
                        stroke = { value = "#000" }
                    }
                }
            }
        }
    }
end

--- Build a graph with the relevant data.
local function buildGraph(frame, args, settings)
    local graphSettings = {
        version = "2",
        width = args.width or 600,
        height = args.height or 300,
        padding = {
            top = args.paddingTop or 10,
            right = args.paddingTop or 10,
            bottom = args.paddingTop or 30,
            left = args.paddingTop or 30
        }
    }
    local graphContent = TableTools.deepCopy(graphSettings)
    for k,v in TableTools.sortedPairs(settings) do graphContent[k] = v end

    return frame:extensionTag{
        name = "graph",
        content = mw.text.jsonEncode(
            graphContent,
            mw.text.JSON_PRETTY
        )
    }
end

function p._graphCases(args)
    local opens, closes = p._getCaseDates(args.noArchive)
    local caseNet = p._caseDatesToCaseNet(opens, closes)
    local dateCaseCounts = p._caseNetToDateCaseCounts(caseNet)
    local vegaData = p._dateCaseCountsToVega(dateCaseCounts)

    return buildGraph(args.frame, args.args, vegaData)
end

function p.graphCases(frame)
    local args = getArgs(frame, {
		trim = true,
		removeBlanks = false
	})

    return p._graphCases{
        frame = frame,
        args = args,
        from = args.from,
        upto = args.upto
    }
end

return p