Toggle menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:TrickValidator: Difference between revisions

From Trackmania Wiki
No edit summary
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 3: Line 3:
local requiredSections = {
local requiredSections = {
"Description",
"Description",
"Origin",
"Use Cases & Purpose",
"Requirements and limits",
"Conditions and limits",
"Variations",
"How to Perform",
    "Examples"
"Origin & History",
"Variations & Related Tricks",
"Examples in Practice"
}
}
-- Core sections that MUST exist (case-insensitive; '&' titles accept left/right/full)
local coreRequired = {
"Description",
"Conditions and limits",
"How to Perform",
}
-- Too-few-sections threshold.
local MIN_SECTIONS = 3
-- Utils
local function norm(s)
if not s then return "" end
s = mw.text.trim(s)
s = mw.ustring.gsub(s, "&", "&")
s = mw.ustring.lower(s)
-- unify "and" with "&" for matching
s = mw.ustring.gsub(s, "%s+and%s+", " & ")
-- normalize spaces around ampersand
s = mw.ustring.gsub(s, "%s*&%s*", " & ")
-- collapse spaces/underscores
s = mw.ustring.gsub(s, "[%s_]+", " ")
return mw.text.trim(s)
end
local function ampVariants(title)
-- returns full + left/right variants if '&' present after normalization
local out = {}
local t = norm(title)
out[t] = true
local ampPos = mw.ustring.find(t, "&", 1, true)
if ampPos then
local left = mw.text.trim(mw.ustring.sub(t, 1, ampPos - 1))
local right = mw.text.trim(mw.ustring.sub(t, ampPos + 1))
if left ~= "" then out[left] = true end
if right ~= "" then out[right] = true end
end
return out
end


local function getPageWikitext()
local function getPageWikitext()
Line 22: Line 65:
end
end
return out
return out
end
local function buildPresentSet(headings)
-- set of normalized headings; for any '&' title, also record left/right parts
local present = {}
for _, h in ipairs(headings) do
local n = norm(h)
present[n] = true
for v, _ in pairs(ampVariants(n)) do
present[v] = true
end
end
return present
end
local function hasSection(present, name)
-- accepts full or left/right if '&' (also treats "and" as '&')
for v, _ in pairs(ampVariants(name)) do
if present[v] then return true end
end
return false
end
local function countMatches(present, list)
local c = 0
for _, name in ipairs(list) do
if hasSection(present, name) then c = c + 1 end
end
return c
end
end


Line 31: Line 103:


local found = l2Headings(pageContent)
local found = l2Headings(pageContent)
local present = buildPresentSet(found)


local ok = (#found == #requiredSections)
-- Rule 1: Check for missing CORE sections first, as this is the most critical error.
if ok then
local missing = {}
for i, req in ipairs(requiredSections) do
for _, req in ipairs(coreRequired) do
if found[i] ~= req then ok = false break end
if not hasSection(present, req) then
table.insert(missing, req)
end
end
end
if #missing > 0 then
local msg = "This article is missing required section(s): '''" .. table.concat(missing, "''', '''") .. "'''."
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
end
end


if not ok then
-- Rule 2: Now check if there are too few sections overall.
local msg = "This article's structure is incorrect. It must have the following sections in this exact order: '''"
-- This check is now secondary to the core section requirement.
.. table.concat(requiredSections, "''', '''") .. "'''. No other sections are allowed."
if #found < MIN_SECTIONS then
local msg = "This article has too few sections. Add more sections to match the typical structure."
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
end
end


-- All checks passed.
return ""
return ""
end
end


return p
return p

Latest revision as of 22:03, 17 September 2025

Documentation for this module may be created at Module:TrickValidator/doc

local p = {}

local requiredSections = {
	"Description",
	"Use Cases & Purpose",
	"Conditions and limits",
	"How to Perform",
	"Origin & History",
	"Variations & Related Tricks",
	"Examples in Practice"
}

-- Core sections that MUST exist (case-insensitive; '&' titles accept left/right/full)
local coreRequired = {
	"Description",
	"Conditions and limits",
	"How to Perform",
}

-- Too-few-sections threshold.
local MIN_SECTIONS = 3

-- Utils

local function norm(s)
	if not s then return "" end
	s = mw.text.trim(s)
	s = mw.ustring.gsub(s, "&amp;", "&")
	s = mw.ustring.lower(s)
	-- unify "and" with "&" for matching
	s = mw.ustring.gsub(s, "%s+and%s+", " & ")
	-- normalize spaces around ampersand
	s = mw.ustring.gsub(s, "%s*&%s*", " & ")
	-- collapse spaces/underscores
	s = mw.ustring.gsub(s, "[%s_]+", " ")
	return mw.text.trim(s)
end

local function ampVariants(title)
	-- returns full + left/right variants if '&' present after normalization
	local out = {}
	local t = norm(title)
	out[t] = true
	local ampPos = mw.ustring.find(t, "&", 1, true)
	if ampPos then
		local left = mw.text.trim(mw.ustring.sub(t, 1, ampPos - 1))
		local right = mw.text.trim(mw.ustring.sub(t, ampPos + 1))
		if left ~= "" then out[left] = true end
		if right ~= "" then out[right] = true end
	end
	return out
end

local function getPageWikitext()
	local t = mw.title.getCurrentTitle()
	return t and t:getContent() or nil
end

local function l2Headings(wikitext)
	local out = {}
	if not wikitext then return out end
	wikitext = "\n" .. wikitext .. "\n"
	for h in mw.ustring.gmatch(wikitext, "\n==%s*([^=\n][^\n]-)%s*==%s*\n") do
		table.insert(out, mw.text.trim(h))
	end
	return out
end

local function buildPresentSet(headings)
	-- set of normalized headings; for any '&' title, also record left/right parts
	local present = {}
	for _, h in ipairs(headings) do
		local n = norm(h)
		present[n] = true
		for v, _ in pairs(ampVariants(n)) do
			present[v] = true
		end
	end
	return present
end

local function hasSection(present, name)
	-- accepts full or left/right if '&' (also treats "and" as '&')
	for v, _ in pairs(ampVariants(name)) do
		if present[v] then return true end
	end
	return false
end

local function countMatches(present, list)
	local c = 0
	for _, name in ipairs(list) do
		if hasSection(present, name) then c = c + 1 end
	end
	return c
end

function p.validate(frame)
	local pageContent = getPageWikitext()
	if not pageContent then
		return "" -- cannot validate without content
	end

	local found = l2Headings(pageContent)
	local present = buildPresentSet(found)

	-- Rule 1: Check for missing CORE sections first, as this is the most critical error.
	local missing = {}
	for _, req in ipairs(coreRequired) do
		if not hasSection(present, req) then
			table.insert(missing, req)
		end
	end
	if #missing > 0 then
		local msg = "This article is missing required section(s): '''" .. table.concat(missing, "''', '''") .. "'''."
		return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
	end

	-- Rule 2: Now check if there are too few sections overall.
	-- This check is now secondary to the core section requirement.
	if #found < MIN_SECTIONS then
		local msg = "This article has too few sections. Add more sections to match the typical structure."
		return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
	end

	-- All checks passed.
	return ""
end

return p