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
Line 1: Line 1:
local p = {}
local p = {}


-- Full suggested structure kept for reference and "almost same" tolerance
local requiredSections = {
local requiredSections = {
"Description",
"Description",
"Use Cases & Purpose",
"Use Cases & Purpose",
    "Conditions and limits",
"Conditions and limits",
"How to Perform",
"How to Perform",
    "Origin & History",
"Origin & History",
"Variations & Related Tricks",
"Variations & Related Tricks",
    "Examples in Practice"
"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 (example given: "only 3" triggers). Set to 4.
local MIN_SECTIONS = 4
-- 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 24: Line 66:
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 33: Line 104:


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
-- The "almost the same" tolerance logic is now redundant if the goal is only
-- to enforce core sections and a minimum count. If it had a different purpose,
-- it would need to be re-evaluated. Based on the comments, its purpose seems
-- to have been superseded, so it can be safely removed or left as a final "pass" condition.
-- For example, keeping it here does no harm, as the critical checks have already passed.
local expected = #requiredSections
local matched = countMatches(present, requiredSections)
if matched >= expected - 1 then
return "" -- considered "almost the same", which is fine.
end
end


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


return p
return p

Revision as of 22:01, 17 September 2025

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

local p = {}

-- Full suggested structure kept for reference and "almost same" tolerance
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 (example given: "only 3" triggers). Set to 4.
local MIN_SECTIONS = 4

-- 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
	
	-- The "almost the same" tolerance logic is now redundant if the goal is only
	-- to enforce core sections and a minimum count. If it had a different purpose,
	-- it would need to be re-evaluated. Based on the comments, its purpose seems
	-- to have been superseded, so it can be safely removed or left as a final "pass" condition.
	-- For example, keeping it here does no harm, as the critical checks have already passed.
	local expected = #requiredSections
	local matched = countMatches(present, requiredSections)
	if matched >= expected - 1 then
		return "" -- considered "almost the same", which is fine.
	end

	-- All checks passed.
	return ""
end

return p