跳转到内容

模組:PJ:VG/HOT/v2

维基百科,自由的百科全书
--- [[Module:PJ:VG/HOT/v2]]

require("strict")

local getArgs = require("Module:Arguments").getArgs
local language = mw.language.getContentLanguage()

local p = {}

local grades = {
    ["典范"] = "fa",
    ["特色列表"] = "fl",
    ["甲"] = "a",
    ["甲级列表"] = "al",
    ["优良"] = "ga",
    ["乙上"] = "bplus",
    ["乙"] = "b",
    ["乙级列表"] = "bl",
    ["丙"] = "c",
    ["丙级列表"] = "cl",
    ["初"] = "start",
    ["列表"] = "list",
    ["小作品"] = "stub",
    ["小列表"] = "sl",
    ["未评"] = "unassessed",
}

local importance = {
    ["极高"] = "top",
    ["高"] = "high",
    ["中"] = "mid",
    ["低"] = "low",
    ["未知"] = "unknown",
}

-- Badges are enabled unless a project explicitly disables them.
local enabledBadges = {
    class = {
        ["电子游戏/宝可梦工作组"] = false,
    },
    importance = {
        ["电子游戏/宝可梦工作组"] = false,
        ["电子游戏/我的世界"] = false,  -- sic: no "工作组" suffix
        ["电子游戏/史克威尔艾尼克斯工作组"] = false,
        ["电子游戏/世嘉工作组"] = false,
        ["电子游戏/任天堂工作组"] = false,
        ["电子游戏/米哈游工作组"] = false,
    },
}

local siteRankLabels = {
    daily = "全站日榜第",
    weekly = "全站周榜第",
    monthly = "全站月榜第",
    quarterly = "全站季度榜第",
    yearly = "全站年榜第",
}

local function number(value)
    if value == nil then
        return "未知"
    end
    return language:formatNum(value)
end

local literal = mw.text.nowiki

local function date(value)
    local iso = string.format(
        "%04d-%02d-%02d", value[1], value[2], value[3]
    )
    local timestamp = os.time({
        year = value[1],
        month = value[2],
        day = value[3],
        hour = 0,
        isdst = false,
    })
    local label = string.format(
        "%d年%d月%d日", value[1], value[2], value[3]
    )
    return iso, timestamp, label
end

local function badge(parent, value, kind, lookup)
    local label = value or (kind == "class" and "未评" or "未知")
    local code = lookup[label]
    local description = (kind == "class" and "质量:" or "重要度:")
        .. label
    local class = table.concat({
        "vg-hot-badge",
        "vg-hot-" .. kind,
        "vg-hot-" .. kind .. "-" .. code,
    }, " ")

    parent:tag("span")
        :addClass(class)
        :attr("role", "img")
        :attr("title", description)
        :attr("aria-label", description)
end

local function articleLink(title)
    return "[[:" .. literal(title) .. "]]"
end

local function trend(parent, rank, previous, limit)
    if previous == nil then
        parent:tag("span")
            :addClass("vg-hot-trend vg-hot-trend-unknown")
            :attr("title", "上期名次未知")
            :wikitext("(—)")
        return
    elseif previous == -1 then
        parent:tag("span")
            :addClass("vg-hot-trend vg-hot-trend-surge vg-hot-trend-new")
            :attr("role", "img")
            :attr("title", "新条目")
            :attr("aria-label", "新条目")
            :wikitext("New")
        return
    end

    local delta = previous - rank
    if delta == 0 then
        return
    end

    local style = delta > 0 and "rise" or "fall"
    if delta >= limit then
        style = "surge"
    end

    local places = math.abs(delta)
    local shown = delta > 0 and places >= 1000 and "999+"
        or number(places)
    local label = "上期第" .. number(previous) .. "名"

    parent:tag("span")
        :addClass("vg-hot-trend vg-hot-trend-" .. style)
        :attr("role", "img")
        :attr("title", label)
        :attr("aria-label", label)
        :wikitext(shown)
end

local function views(parent, item, metadata, period)
    local total = parent:tag("span"):addClass("vg-hot-total")
    if item.site_rank ~= nil then
        local prefix = siteRankLabels[metadata.duration] or "全站榜第"
        local label = prefix .. number(item.site_rank) .. "名"
        total:addClass("vg-hot-site-top"):attr("title", label)
    end

    if item.views == nil then
        total:wikitext("未知")
    else
        local url = mw.uri.new("https://pageviews.wmcloud.org/")
        url:extend({
            project = "lib.yinfer.top",
            platform = "all-access",
            agent = "user",
            redirects = 0,
            start = period.start,
            ["end"] = period["end"],
            pages = item.title,
        })
        total:wikitext(
            "[" .. tostring(url) .. " " .. number(item.views) .. "]"
        )
    end

    -- A daily average would repeat the total.
    -- Longer periods retain their average.
    if period.days > 1 then
        local average = item.views
            and math.floor(item.views / period.days) or nil
        parent:tag("span")
            :addClass("vg-hot-average")
            :attr("title", "日均浏览量")
            :wikitext("(" .. number(average) .. ")")
    end
end

local function renderItem(list, item, metadata, period, limit)
    local row = list:tag("li"):attr("value", item.rank)
    if enabledBadges.class[metadata.project] ~= false then
        badge(row, item.assessments[1], "class", grades)
    end
    if enabledBadges.importance[metadata.project] ~= false then
        badge(row, item.assessments[2], "importance", importance)
    end

    row:wikitext(articleLink(item.title))
        :tag("span")
        :addClass("vg-hot-separator")
        :wikitext(" — ")
    views(row, item, metadata, period)
    trend(row, item.rank, item.previous_rank, limit)
end

local function renderSummary(parent, metadata, period)
    local summary = parent:tag("ol")
        :addClass("vg-hot-summary")
        :addClass("vg-hot-hlist")

    local dates = summary:tag("li")
    dates:tag("b"):wikitext("统计时间:")
    dates:wikitext(period.label)
    if period.days > 1 then
        dates:wikitext("<small>(计" .. number(period.days) .. "日)</small>")
    end

    local articles = summary:tag("li")
    articles:tag("b"):wikitext("总条目数:")
    articles:wikitext(number(metadata.total_pages) .. "篇")

    local views = summary:tag("li")
    views:tag("b"):wikitext("总浏览量:")
    views:wikitext(number(metadata.total_views) .. "次")
    if period.days > 1 then
        local average = metadata.total_views
            and math.floor(metadata.total_views / period.days) or nil
        views:tag("small")
            :wikitext("(日均" .. number(average) .. "次)")
    end
end

function p.main(frame)
    local args = getArgs(frame)
    return p._main(args)
end

-- Decode bot-generated JSON: arrays become one-based sequences.
-- Null keys vanish.
function p._main(args)
    local data = mw.text.jsonDecode(args.data)
    local metadata = data.metadata
    local start, startTime, startLabel = date(metadata.start)
    local finish, finishTime, finishLabel = date(metadata["end"])
    local period = {
        start = start,
        ["end"] = finish,
        days = os.difftime(finishTime, startTime) / 86400 + 1,
        label = start == finish and startLabel
            or startLabel .. "—" .. finishLabel,
    }

    local root = mw.html.create("div"):addClass("plainlinks")
    renderSummary(root, metadata, period)
    local list = root:tag("ol"):addClass("vg-hot-list")
    for _, item in ipairs(data.items) do
        renderItem(list, item, metadata, period, #data.items)
    end

    local styles = mw.getCurrentFrame():extensionTag(
        "templatestyles", "", { src = "Module:PJ:VG/HOT/v2/styles.css" }
    )
    return styles .. tostring(root)
end

return p