跳转到内容

模組:沙盒/Lopullinen

维基百科,自由的百科全书

这是Module:沙盒/Lopullinen当前版本,由For Each element In group ... Next留言 | 贡献编辑于2025年5月31日 (六) 20:24。这个网址是本页该版本的固定链接。

(差异) ←上一修订 | 最后版本 (差异) | 下一修订→ (差异)

require("strict")

local strf = mw.ustring.format

local getArgs = require('Module:Arguments').getArgs

--- @alias Username string
--- @alias Score number
--- @alias Level number
--- @alias RecordMapping table<Username, Score>
--- @alias Wikitext string

--- @class UserScore
--- @field user Username
--- @field score Score

--- @class UserScoreDelta
--- @field user Username
--- @field current Score
--- @field delta Score

--- For setting the score ranking thresholds.
--- @type Level[]
local LEVEL_NODES = { math.huge, 50, 40, 30, 20, 10, 5, 4, 3, 2, 1, 0 }
--- @type RecordMapping
local CURRENT_RECORD_MAPPING = mw.loadData("Module:ACGaward/list/old")

--- Return a normalized username or current page base name if nil.
--- @param user Username | nil @Optional username to be normalized.
--- @return Username @The normalized username or current page base name.
local function fetch_username(user)
    if user == nil then
        return mw.title.getCurrentTitle().baseText
    end
    local username = user  -- TODO: To be normalized.
    return username
end

--- Return name and recorded score from the given record mapping.
--- If the username is not recorded, the score will be 0.
--- @param mapping RecordMapping @Mapping of username to score.
--- @param user Username | nil @Optional username to look up.
--- @return Username, Score
local function get_record(mapping, user)
    local name = fetch_username(user)
    return name, (mapping[name] or 0)
end

--- Convert an internal score to the integer score.
--- @param score Score @The score might have a decimal part.
--- @return Score @The score as an integer.
local function calculate_public_score(score)
    return math.floor(score)
end

--- Convert a score to its corresponding ACG-award level.
--- @param score Score @The score to convert to its level.
--- @return Level @The level as an integer.
local function calculate_level(score)
    return math.floor(score / 10)
end

--- Get a normalized score for a user from record mapping.
--- @param mapping RecordMapping @Mapping of username to score.
--- @param user Username | nil @Optional username to look up.
--- @return Score
local function get_score(mapping, user)
    local _, score = get_record(mapping, user)
    return calculate_public_score(score)
end

--- Get a normalized level for a user from record mapping.
--- @param mapping RecordMapping @Mapping of username to score.
--- @param user Username | nil @Optional username to look up.
--- @return Level
local function get_level(mapping, user)
    local _, score = get_record(mapping, user)
    return calculate_level(score)
end

--- Get a list sorted by score in descending order.
--- @param mapping RecordMapping
--- @return UserScore[]
local function get_sorted_user_score_list(mapping)
    local result = {}
    for k, v in pairs(mapping) do
        local item = { user = k, score = calculate_public_score(v) }
        table.insert(result, item)
    end
    local sorter = function(a, b)
        if a.score == b.score then
            return a.user < b.user
        end
        return a.score > b.score
    end
    table.sort(result, sorter)
    return result
end

--- Create wikicode ranking for users within level bounds.
--- @param list UserScore[] @Sequence of UserScore tuples to include.
--- @param lbound Level @The lower bound of level (included).
--- @param ubound Level @The upper bound of level (excluded).
--- @param start number @Starting number for the ordered list.
--- @return Wikitext @Formatted wikicode for the subset ranking.
local function create_subset_ranking(list, lbound, ubound, start)
    if #list == 0 then
        return
    end
    local lines = {}
    -- header
    local header
    if ubound == math.huge then
        header = tostring(lbound) .. "級或以上維基ACG獎"
    elseif ubound - lbound == 1 then
        header = tostring(lbound) .. "級維基ACG獎"
    else
        header = tostring(lbound) .. "至" .. tostring(ubound - 1) .. "級維基ACG獎"
    end
    header = "<b>" .. header .. "</b>"
    table.insert(lines, header)
    -- list body
    local line = strf('<ol start="%s">', start)
    table.insert(lines, line)
    for _, v in ipairs(list) do
        line = strf("<li>[[User:%s|%s]]:%s分", v.user, v.user, v.score)
        table.insert(lines, line)
    end
    table.insert(lines, "</ol>")
    -- return
    return table.concat(lines, '\n')
end

--- Build a complete ranking of users grouped by level in wikicode.
--- @param mapping RecordMapping
--- @return Wikitext
local function build_ranking(mapping)
    local score_list = get_sorted_user_score_list(mapping)
    local subset_ranking_parts = {}
    -- loop
    local sub_score_list = {}
    local rank = 0
    local node_index = 1
    for i, user in ipairs(score_list) do
        local user_level = calculate_level(user.score)
        while user_level < LEVEL_NODES[node_index] do
            local subset_ranking = create_subset_ranking(
                    sub_score_list,
                    LEVEL_NODES[node_index],
                    LEVEL_NODES[node_index - 1],
                    rank
            )
            table.insert(subset_ranking_parts, subset_ranking)
            -- reset for the next level
            sub_score_list = {}
            rank = i
            node_index = node_index + 1
        end
        table.insert(sub_score_list, user)
    end
    return table.concat(subset_ranking_parts, '\n')
end

local p = {}

local function makeInvokeFunc(funcName)
    return function(frame)
        local args = getArgs(frame)
        return p[funcName](args)
    end
end

p.score = makeInvokeFunc("_score")
function p._score(args)
    local mapping = CURRENT_RECORD_MAPPING
    local user = args[1]
    return get_score(mapping, user)
end

p.level = makeInvokeFunc("_level")
function p._level(args)
    local mapping = CURRENT_RECORD_MAPPING
    local user = args[1]
    return get_level(mapping, user)
end

p.ranking = makeInvokeFunc("_ranking")
function p._ranking(args)
    local mapping = CURRENT_RECORD_MAPPING
    return build_ranking(mapping)
end

return p