模块:Dictionary/Vocabulary.lua

来自「荏苒之境」
Sicusa留言 | 贡献2025年8月5日 (二) 01:36的版本

此模块的文档可以在模块:Dictionary/Vocabulary.lua/doc创建

local csv = require("Module:Csv.lua")
local dict_utils = require("Module:Dictionary/Utils.lua")
local dict_views = require("Module:Dictionary/Views.lua")

local html = mw.html
local cargo = mw.ext.cargo

local DICT_TABLE = dict_views.TABLE
local DICT_HEADER_PRIORITY = dict_utils.HEADER_PRIORITY

local vocabulary = {}

local function format_conflict(i, prev_row, row)
    prev_row[i] = (prev_row[i] or "").."<br/><i style=\"color: grey\">"..(row[i] or "").."</i>"
end

local function query_existing_rows(language, rows)
    local t = { "Language=\"", language, "\" AND Priority<>", tostring(DICT_HEADER_PRIORITY), " AND Spelling in (" }
    for i = 1, #rows do
        t[#t+1] = "'"
        t[#t+1] = rows[i][1]
        t[#t+1] = "',"
    end
    t[#t] = "')"
    local query = cargo.query(DICT_TABLE, "Spelling,Definition,Extra,Priority,_pageName", {
        where = table.concat(t)
    })
    local found = {}
    for i = 1, #query do
        local raw = query[i]
        raw.Priority = tonumber(raw.Priority)
        found[raw.Spelling] = raw
    end
    return found
end

local function check_conflicting(prev_row, row, extra_count, on_conflict)
    local compare_count = extra_count and extra_count + 2 or math.max(#row, #prev_row)
    local conflict = false
	if on_conflict == nil then
		for i = 1, compare_count do
	        local prev = prev_row[i]
	        local curr = row[i]
	        if prev ~= curr then
	        	return true
	        end
	    end
	else
	    for i = 1, compare_count do
	        local prev = prev_row[i]
	        local curr = row[i]
	        if prev ~= curr then
	            on_conflict(i, prev_row, row)
	            conflict = true
	        end
	    end
	end
    return conflict
end

local function store_row(language, priority, row, extra_count)
    local extra = {}
    for i = 1, extra_count or #row-2 do
        extra[i] = row[i+2]
    end
    cargo.store(DICT_TABLE, {
        Language = language,
        Spelling = row[1],
        Definition = row[2],
        Extra = csv.format_row(extra),
        Priority = priority
    })
end

vocabulary.show = function(frame)
    local args = frame.args
    local page_name = frame.args.page_name
    local language = args.language
    local format = args.format
    local priority = tonumber(args.priority) or 1
    local content = args.content

    local header
    local rows

    if format == "csv" then
        rows = csv.parse(content, true)
        header = rows.header
    else
        mw.addWarning("无效的词典格式:"..tostring(format))
        return
    end

    local extra_count = #header - 2
    local prev_header_raw = cargo.query(DICT_TABLE, "Spelling,Definition,Extra,_pageName", {
        where = "Language=\""..language.."\" AND Priority="..tostring(DICT_HEADER_PRIORITY)
    })[1]

    if prev_header_raw ~= nil and page_name ~= prev_header_raw._pageName then
        local prev_header = dict_utils.expand_row(prev_header_raw, extra_count)
        local is_conflicting = check_conflicting(prev_header, header, extra_count, format_conflict)
        if is_conflicting then
            return html.create("pre")
                :css("color", "red")
                :tag("p"):wikitext("检测到该词汇表的标题行(灰色文本)与之前储存的标题行文本冲突,请修复:"):done()
                :node(dict_views.vocabulary(prev_header, {}))
        end
    else
        store_row(language, DICT_HEADER_PRIORITY, header, extra_count)
    end

    local existing_rows = query_existing_rows(language, rows)
    local conflicting_rows = {}
    
    for i = 1, #rows do
        local row = rows[i]
        local prev_row_raw = existing_rows[row[1]]
        local prev_row = prev_row_raw and dict_utils.expand_row(prev_row_raw, extra_count)
        local ignore_store = false

        if prev_row_raw ~= nil then
            local prev_p = prev_row_raw.Priority
            if prev_p < priority then
                ignore_store = not check_conflicting(prev_row, row, extra_count)
            elseif prev_p > priority then
                ignore_store = true
            else
                ignore_store = check_conflicting(prev_row, row, extra_count, format_conflict)
                if ignore_store then
                    table.insert(conflicting_rows, prev_row)
                end
            end
        end

        if not ignore_store then
            store_row(language, priority, row, extra_count)
        end
    end

    local view = html.create("div")
    if #conflicting_rows ~= 0 then
        view:tag("pre")
            :css("color", "red")
            :tag("p")
                :wikitext("检测到旧词汇与新词汇(灰色文本)内容冲突的情况,且二者具有相同优先级,请统一词汇信息:")
                :done()
            :node(dict_views.vocabulary(header, conflicting_rows))
    end
    if frame.args.show_view == "true" then
        view:node(dict_views.vocabulary(header, rows))
    end
    return view
end

return vocabulary