模組:ACGaward
外观
![]() | 此模块使用Lua语言: |
本模块用于处理維基ACG專題獎分数相关内容。请在Module:ACGaward/list登记用户得分。
用法
本模组包括若干函数:
score
- 根据页面名獲得用戶专题奖得分。亦可用|user=
调取指定用户得分,或透过|mapping=
从其他模组调取分数(如Module:ACGaward/list/old
)。level
- 獲得特定用戶的专题奖等级。特性同上。ranking
- 获取用户专题奖等级列表,效果见WikiProject:ACG/維基ACG專題獎/分數排名榜。可透过|mapping=
从其他模组调取分数生成名单。diff
- 和Module:ACGaward/list/old搭配使用,比較兩筆記錄間的用戶分数變化情況。亦可使用|mapping1=
和|mapping2=
指定两个页面来对比。
備註
以下子模组和本模組功能無關,係爲避免「浪費」而借用本模組空間:
- /nominee check — 用於
{{ACG提名/check}}
。 - /nominee check new — 用於
{{ACG提名2/request}}
、{{ACG提名2/check}}
等处
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")
--- 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.user
return get_score(mapping, user)
end
p.level = makeInvokeFunc("_level")
function p._level(args)
-- For legacy usage
if args[1] > 0 then
return args[1]
end
local mapping = CURRENT_RECORD_MAPPING
local user = args.user
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