Jump to content

Module:Television infoboxes disambiguation check: Difference between revisions

From WikipediNyah
Created page with "local libraryUtil = require('libraryUtil') --=============================================================-- -- DisambiguationPattern class. local function DisambiguationPattern(o) local DisambiguationPattern = o or {pattern = "", type = ""} local checkSelf = libraryUtil.makeCheckSelfFunction( 'Television infoboxes disambiguation check', 'DisambiguationPattern', DisambiguationPattern, 'Television infoboxes disambiguation check object' ) return DisambiguationPattern..."
 
No edit summary
 
Line 1: Line 1:
local libraryUtil = require('libraryUtil')
require("strict")


--=============================================================--
local libraryUtil = require("libraryUtil")
-- DisambiguationPattern class.
 
-----------------------------------------------------------------------
-- DisambiguationPattern "class"
-----------------------------------------------------------------------
local function DisambiguationPattern(o)
local function DisambiguationPattern(o)
local DisambiguationPattern = o or {pattern = "", type = ""}
local obj = o or { pattern = "", type = 0 }
local checkSelf = libraryUtil.makeCheckSelfFunction( 'Television infoboxes disambiguation check', 'DisambiguationPattern', DisambiguationPattern, 'Television infoboxes disambiguation check object' )
libraryUtil.makeCheckSelfFunction(
 
"Television infoboxes disambiguation check",
return DisambiguationPattern
"DisambiguationPattern",
obj,
"Television infoboxes disambiguation check object"
)
return obj
end
end


--=============================================================--
-----------------------------------------------------------------------
 
-- Constants
-- Constants.
-----------------------------------------------------------------------
local DAB_VALID = {
local DAB_VALID = {
[true] = "valid",
[true] = "valid",
Line 21: Line 28:


local validationTypeList = {
local validationTypeList = {
["VALIDATION_TYPE_YEAR_COUNTRY"] = 1,
VALIDATION_TYPE_YEAR_COUNTRY = 1,
["VALIDATION_TYPE_YEAR"] = 2,
VALIDATION_TYPE_YEAR = 2,
["VALIDATION_TYPE_COUNTRY"] = 3,
VALIDATION_TYPE_COUNTRY = 3,
["VALIDATION_TYPE_YEAR_SEASON_NUMBER"] = 4,
VALIDATION_TYPE_YEAR_SEASON_NUMBER = 4,
["VALIDATION_TYPE_COUNTRY_SEASON_NUMBER"] = 5,
VALIDATION_TYPE_COUNTRY_SEASON_NUMBER = 5,
["VALIDATION_TYPE_SEASON_NUMBER"] = 6,
VALIDATION_TYPE_SEASON_NUMBER = 6,
["VALIDATION_TYPE_COUNTRY_SEASON"] = 7,
VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER = 8
["VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER"] = 8
}
}


local debugMessageList = {
local debugMessageList = {
["DEBUG_EMPTY_TITLE"] = "Debug: Error: Empty title.",
DEBUG_EMPTY_TITLE = "Debug: Error: Empty title.",
["DEBUG_NO_DAB"] = "Debug: No disambiguation.",
DEBUG_NO_DAB = "Debug: No disambiguation.",
["DEBUG_TITLE_ON_EXCEPTION"] = "Debug: Title on exception list.",
DEBUG_TITLE_ON_EXCEPTION = "Debug: Title on exception list.",
["DEBUG_VALID_FORMAT"] = "Debug: Using a valid format.",
DEBUG_VALID_FORMAT = "Debug: Using a valid format.",
["DEBUG_NOT_VALID_FORMAT"] = "Debug: Not a valid format.",
DEBUG_NOT_VALID_FORMAT = "Debug: Not a valid format.",
["DEBUG_YEAR_COUNTRY"] = "Debug: Using a valid format with an extended Year and Country - {}.",
DEBUG_YEAR_COUNTRY = "Debug: Using a valid format with an extended Year and Country - {}.",
["DEBUG_YEAR"] = "Debug: Using a valid format with an extended Year - {}.",
DEBUG_YEAR = "Debug: Using a valid format with an extended Year - {}.",
["DEBUG_COUNTRY"] = "Debug: Using a valid format with an extended Country - {}.",
DEBUG_COUNTRY = "Debug: Using a valid format with an extended Country - {}.",
["DEBUG_INCORRECT_STYLE"] = "Debug: Using a valid format but using an incorrect extended style.",
DEBUG_INCORRECT_STYLE = "Debug: Using a valid format but using an incorrect extended style.",
["DEBUG_INCORRECT_INFOBOX"] = "Debug: Using incorrect infobox - {}.",
DEBUG_INCORRECT_INFOBOX = "Debug: Using incorrect infobox - {}.",
["DEBUG_YEAR_SEASON_NUMBER"] = "Debug: Using a valid format with an extended Year and Season number - {}.",
DEBUG_YEAR_SEASON_NUMBER = "Debug: Using a valid format with an extended Year and Season number - {}.",
["DEBUG_COUNTRY_SEASON_NUMBER"] = "Debug: Using a valid format with an extended Country and Season number - {}.",
DEBUG_COUNTRY_SEASON_NUMBER = "Debug: Using a valid format with an extended Country and Season number - {}.",
["DEBUG_SEASON_NUMBER"] = "Debug: Using a valid format with a Season number - {}.",
DEBUG_SEASON_NUMBER = "Debug: Using a valid format with a Season number - {}.",
["DEBUG_COUNTRY_SEASON"] = "Debug: Using a valid format with a Country and the word Season - {}.",
DEBUG_YEAR_COUNTRY_SEASON_NUMBER = "Debug: Using a valid format with an extended Year, Country and Season number - {}."
["DEBUG_YEAR_COUNTRY_SEASON_NUMBER"] = "Debug: Using a valid format with an extended Year, Country and Season number - {}."
}
}


-- Local function which checks if both booleans are true or not.
-----------------------------------------------------------------------
local function validateTwoParameters(isValid1, isValid2)
-- Validation helpers
if (isValid1 and isValid2) then
-----------------------------------------------------------------------
return true
local function validateTwoParameters(a, b)
else
return not not (a and b)
return false
end
end
end


-- Validate that the season number entered is a valid number -
-- that it does not start with a leading zero (0).
local function validateSeasonNumber(seasonNumber)
local function validateSeasonNumber(seasonNumber)
if (tonumber(string.sub(seasonNumber, 1, 1)) == 0) then
if not seasonNumber then
return false
return false
else
return true
end
end
return tonumber(seasonNumber:sub(1, 1)) ~= 0
end
end


-- Validate that the year entered is a valid year.
local function validateYear(year)
local function validateYear(year)
if (string.len(year) == 4) then
return year and #year == 4
return true
else
return false
end
end
end


-- Validate that the text entered is a supported country adjective.
local function validateCountryAdjective(adj)
local function validateCountryAdjective(adjective)
if not adj or adj == "" then
local data = mw.loadData('Module:Country adjective')
 
-- Search for a country corresponding to the given text.
if (data.getCountryFromAdj[adjective]) then
return true
else
return false
return false
end
end
local data = mw.loadData("Module:Country adjective")
return not not data.getCountryFromAdj[adj]
end
end


-- Checks pages using by validating the disambiguation patterns.
-----------------------------------------------------------------------
local function validatePatterns(disambiguation, disambiguationPatternList)
-- Pattern validation engine
local year = ""
-----------------------------------------------------------------------
local adjective = ""
local function validatePatterns(disambiguation, patternList)
local seasonNumber = ""
local year, adjective, seasonNumber
local isYearValid
local isYearValid, isAdjectiveValid, isSeasonNumberValid
local isAdjectiveValid
local isSeasonNumberValid


for i, v in ipairs(disambiguationPatternList) do
for i = 1, #patternList do
local currentDisambiguationPattern = disambiguationPatternList[i]
local p = patternList[i]
if (disambiguation:match(currentDisambiguationPattern.pattern)) then
if disambiguation:match(p.pattern) then
 
-- Year and Country styles: "1999 American TV series"
-- YEAR + COUNTRY
if (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_YEAR_COUNTRY"]) then
if p.type == validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY then
year, adjective = disambiguation:match(currentDisambiguationPattern.pattern)
year, adjective = disambiguation:match(p.pattern)
isYearValid = validateYear(year)
isYearValid = validateYear(year)
isAdjectiveValid = validateCountryAdjective(adjective)
isAdjectiveValid = validateCountryAdjective(adjective)
local ok = validateTwoParameters(isYearValid, isAdjectiveValid)
return ok, debugMessageList.DEBUG_YEAR_COUNTRY:gsub("{}", DAB_VALID[ok])


local isValid = validateTwoParameters(isYearValid, isAdjectiveValid)
-- YEAR
return isValid, debugMessageList["DEBUG_YEAR_COUNTRY"]:gsub("{}", DAB_VALID[isValid])
elseif p.type == validationTypeList.VALIDATION_TYPE_YEAR then
 
-- Year styles: "1999 TV series"
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_YEAR"]) then
year = disambiguation
year = disambiguation
isYearValid = validateYear(year)
isYearValid = validateYear(year)
return isYearValid, debugMessageList["DEBUG_YEAR"]:gsub("{}", DAB_VALID[isYearValid])
return isYearValid, debugMessageList.DEBUG_YEAR:gsub("{}", DAB_VALID[isYearValid])


-- Country styles: "American TV series"
-- COUNTRY
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_COUNTRY"]) then
elseif p.type == validationTypeList.VALIDATION_TYPE_COUNTRY then
adjective = disambiguation
adjective = disambiguation
isAdjectiveValid = validateCountryAdjective(adjective)
isAdjectiveValid = validateCountryAdjective(adjective)
return isAdjectiveValid, debugMessageList["DEBUG_COUNTRY"]:gsub("{}", DAB_VALID[isAdjectiveValid])
return isAdjectiveValid, debugMessageList.DEBUG_COUNTRY:gsub("{}", DAB_VALID[isAdjectiveValid])


-- Year and Season number styles: "1999 TV series, season 1"
-- YEAR + SEASON
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_YEAR_SEASON_NUMBER"]) then
elseif p.type == validationTypeList.VALIDATION_TYPE_YEAR_SEASON_NUMBER then
year, seasonNumber = disambiguation:match(currentDisambiguationPattern.pattern)
year, seasonNumber = disambiguation:match(p.pattern)
isYearValid = validateYear(year)
isYearValid = validateYear(year)
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
local ok = validateTwoParameters(isYearValid, isSeasonNumberValid)
return ok, debugMessageList.DEBUG_YEAR_SEASON_NUMBER:gsub("{}", DAB_VALID[ok])


local isValid = validateTwoParameters(isYearValid, isSeasonNumberValid)
-- COUNTRY + SEASON
return isValid, debugMessageList["DEBUG_YEAR_SEASON_NUMBER"]:gsub("{}", DAB_VALID[isValid])
elseif p.type == validationTypeList.VALIDATION_TYPE_COUNTRY_SEASON_NUMBER then
 
adjective, seasonNumber = disambiguation:match(p.pattern)
-- Country and Season number styles: "American season 1" and "American TV series, season 1"
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_COUNTRY_SEASON_NUMBER"]) then
adjective, seasonNumber = disambiguation:match(currentDisambiguationPattern.pattern)
isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
local ok = validateTwoParameters(isAdjectiveValid, isSeasonNumberValid)
local isValid = validateTwoParameters(isAdjectiveValid, isSeasonNumberValid)
return ok, debugMessageList.DEBUG_COUNTRY_SEASON_NUMBER:gsub("{}", DAB_VALID[ok])
return isValid, debugMessageList["DEBUG_COUNTRY_SEASON_NUMBER"]:gsub("{}", DAB_VALID[isValid])


-- Country and the word season: "American season"
-- SEASON ONLY
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_COUNTRY_SEASON"]) then
elseif p.type == validationTypeList.VALIDATION_TYPE_SEASON_NUMBER then
adjective = disambiguation:match(currentDisambiguationPattern.pattern)
seasonNumber = disambiguation:match(p.pattern)
isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
return isAdjectiveValid, debugMessageList["DEBUG_COUNTRY_SEASON"]:gsub("{}", DAB_VALID[isAdjectiveValid])
 
--Season number styles: "season 1"
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_SEASON_NUMBER"]) then
seasonNumber = disambiguation:match(currentDisambiguationPattern.pattern)
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
return isSeasonNumberValid, debugMessageList["DEBUG_SEASON_NUMBER"]:gsub("{}", DAB_VALID[isSeasonNumberValid])
return isSeasonNumberValid, debugMessageList.DEBUG_SEASON_NUMBER:gsub("{}", DAB_VALID[isSeasonNumberValid])


-- Year, Country and Season number styles: "Gladiators (2008 British TV series, series 2)"
-- YEAR + COUNTRY + SEASON
elseif (currentDisambiguationPattern.type == validationTypeList["VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER"]) then
elseif p.type == validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER then
year, adjective, seasonNumber = disambiguation:match(currentDisambiguationPattern.pattern)
year, adjective, seasonNumber = disambiguation:match(p.pattern)
isYearValid = validateYear(year)
isYearValid = validateYear(year)
isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
isSeasonNumberValid = validateSeasonNumber(seasonNumber)
local ok = validateTwoParameters(isYearValid, isAdjectiveValid)
local isValid = validateTwoParameters(isYearValid, isAdjectiveValid)
ok = validateTwoParameters(ok, isSeasonNumberValid)
isValid = validateTwoParameters(isValid, isSeasonNumberValid)
return ok, debugMessageList.DEBUG_YEAR_COUNTRY_SEASON_NUMBER:gsub("{}", DAB_VALID[ok])
return isValid, debugMessageList["DEBUG_YEAR_COUNTRY_SEASON_NUMBER"]:gsub("{}", DAB_VALID[isValid])
-- Not a valid supported style.
else
-- Do nothing.
end
end
else
-- Do nothing.
end
end
end
end
return false, debugMessageList["DEBUG_INCORRECT_STYLE"]
 
return false, debugMessageList.DEBUG_INCORRECT_STYLE
end
end


-- Validate that the disambiguation type is one of the supported types.
-----------------------------------------------------------------------
local function validateDisambiguationType(disambiguation, validDisambiguationTypeList)
-- Disambiguation type validation
local extendedDisambiguation
-----------------------------------------------------------------------
local function validateDisambiguationType(disambiguation, typeList)
local extended = disambiguation
local count = 0
local count = 0
 
for i, v in ipairs(validDisambiguationTypeList) do
for i = 1, #typeList do
extendedDisambiguation, count = disambiguation:gsub(v, '')
local t = typeList[i]
extendedDisambiguation = mw.text.trim(extendedDisambiguation)
extended, count = extended:gsub(t, "")
if (count ~= 0) then
extended = mw.text.trim(extended)
-- Disambiguation was a valid type; Exit loop.
if count ~= 0 then
break
break
end
end
end
end
return count ~= 0, extended
count = count ~= 0  
end
return count, extendedDisambiguation


end
local function validateDisambiguation(invoker, disambiguation, typeList, patternList)
if #typeList ~= 0 then
local ok, extended = validateDisambiguationType(disambiguation, typeList)


-- Validate that the complete disambiguation is using a supported style.
if not ok then
local function validateDisambiguation(invoker, disambiguation, validDisambiguationTypeList, validDisambiguationPatternList)
return false, debugMessageList.DEBUG_NOT_VALID_FORMAT
-- Check if the list is empty.
if (table.getn(validDisambiguationTypeList) ~= 0) then
local isDisambiguationValid, extendedDisambiguation = validateDisambiguationType(disambiguation, validDisambiguationTypeList)
-- Exit module if the disambiguation type is not a supported style.
if (not isDisambiguationValid) then
return false, debugMessageList["DEBUG_NOT_VALID_FORMAT"]
end
end
 
-- Check if there is no extended disambiguation.
if extended == "" then
if (extendedDisambiguation == '') then
return true, debugMessageList.DEBUG_VALID_FORMAT
return true, debugMessageList["DEBUG_VALID_FORMAT"]
end
end
 
-- A bit of hack so I won't need to refactor a ton of code.
if invoker ~= "infobox television season" then
if (invoker ~= "infobox television season") then
disambiguation = extended
disambiguation = extendedDisambiguation
end
end
end
end
 
return validatePatterns(disambiguation, validDisambiguationPatternList)
return validatePatterns(disambiguation, patternList)
end
end


-- Check if the page is using disambiguation style that belongs to a different infobox.
-----------------------------------------------------------------------
local function isPageUsingIncorrectInfobox(disambiguation, otherInfoboxList)
-- Incorrect infobox detection
for k, v in pairs(otherInfoboxList) do
-----------------------------------------------------------------------
if (string.match(disambiguation, k)) then
local function isPageUsingIncorrectInfobox(invoker, disambiguation, otherList)
return true, v, debugMessageList["DEBUG_INCORRECT_INFOBOX"]:gsub("{}", k)
 
-- Only skip incorrect-infobox detection for SEASON pages.
-- Season pages often have disambiguations like:
--  "season 1", "series 2", "season 1 TV series"
-- These should NOT trigger incorrect-infobox errors.
if invoker == "infobox television season" then
if disambiguation:match("^[Ss]eason%s+%d+")  
or disambiguation:match("^[Ss]eries%s+%d+") then
return false
end
end
end
end
-- Normal incorrect-infobox detection
for k, v in pairs(otherList) do
if disambiguation:match(k) then
return true, v, debugMessageList.DEBUG_INCORRECT_INFOBOX:gsub("{}", k)
end
end
return false
return false
end
end


-- Validate that the title has brackets that are part of the title and not part of disambiguation.
-----------------------------------------------------------------------
local function isOnExceptionList(title, exceptionList)
-- Exception list
for _, v in ipairs(exceptionList) do
-----------------------------------------------------------------------
if (v == title) then
local function isOnExceptionList(title, list)
return true
for _, v in ipairs(list) do
elseif (string.match(title, v)) then
if v == title or title:match(v) then
return true
return true
end
end
Line 241: Line 225:
end
end


-- Get the disambiguation text and make sure that if the title has more than 1 pair of brackets, it returns the last one.
-----------------------------------------------------------------------
-- Disambiguation extraction (supports new naming)
-----------------------------------------------------------------------
-- Get the disambiguation text and support both:
-- - Parenthetical only: "Big Brother (American TV series)"
-- - Parenthetical + season: "Big Brother (American TV series) season 5"
-- - Season only: "Lost season 1"
local function getDisambiguation(title)
local function getDisambiguation(title)
local match = require("Module:String")._match
local match = require("Module:String")._match
return match(title, "%s%((.-)%)", 1, -1, false, "")
-- return (string.match (title, '%s*%b()$') or ''):gsub('[%(%)]', '')
end


-- Validate that arg is not nill and not empty.
-- Last parenthetical chunk, if any.
local function isEmpty(arg)
local paren = match(title, "%s%((.-)%)", 1, -1, false, "")
if (not arg or arg == "") then
 
return true
-- Trailing "season X" or "series X", if any (X can be wrong; validation will decide).
local base, seasonPart = title:match("^(.-)%s+(season%s+.+)$")
if not base then
base, seasonPart = title:match("^(.-)%s+(series%s+.+)$")
end
 
if paren ~= "" and seasonPart then
-- Example: "Big Brother (American TV series) season 5"
-- Returns: "American TV series season 5"
return paren .. " " .. seasonPart
elseif paren ~= "" then
-- Example: "Big Brother (American TV series)"
-- Returns: "American TV series"
return paren
elseif seasonPart then
-- Example: "Lost season 1"
-- Returns: "season 1"
return seasonPart
else
else
return false
return ""
end
end
end
end


-- Returns two objects:
-----------------------------------------------------------------------
--- The first is either an empty string or a tracking category which will appear when using the live version.
-- Utility
--- The second is a debug string which will appear when using /testcases.
-----------------------------------------------------------------------
local function main(title, invoker, validDisambiguationTypeList, validDisambiguationPatternList, exceptionList, otherInfoboxList, invalidTitleStyleList)
local function isEmpty(x)
-- Exit module if the parameter has no value.
return not x or x == ""
if (isEmpty(title)) then
end
return "", debugMessageList["DEBUG_EMPTY_TITLE"]
 
-----------------------------------------------------------------------
-- Main entry point
-----------------------------------------------------------------------
local function main(title, invoker, typeList, patternList, exceptionList, otherInfoboxList, invalidTitleStyleList)
if isEmpty(title) then
return "", debugMessageList.DEBUG_EMPTY_TITLE
end
end


-- Exit module if the title has brackets that are part of the title (not disambiguation).
if isOnExceptionList(title, exceptionList) then
if (isOnExceptionList(title, exceptionList)) then
return "", debugMessageList.DEBUG_TITLE_ON_EXCEPTION
return "", debugMessageList["DEBUG_TITLE_ON_EXCEPTION"]
end
end
 
if (invoker == "infobox television season") then
if invoker == "infobox television season" then
if (#invalidTitleStyleList ~= 0) then
for i = 1, #invalidTitleStyleList do
for i = 1, #invalidTitleStyleList do
if title:find(invalidTitleStyleList[i]) then
if (string.find(title, invalidTitleStyleList[i])) then
return CATEGORY_INCORRECT, debugMessageList.DEBUG_NOT_VALID_FORMAT
return CATEGORY_INCORRECT, debugMessageList["DEBUG_NOT_VALID_FORMAT"]
end
end
end
end
end
end
end
 
-- Get the disambiguation.
local disambiguation = getDisambiguation(title)
local disambiguation = getDisambiguation(title)


-- Exit module if the title has no disambiguation.
if isEmpty(disambiguation) then
if (isEmpty(disambiguation)) then
return "", debugMessageList.DEBUG_NO_DAB
return "", debugMessageList["DEBUG_NO_DAB"]
end
end


-- Exit module if the disambiguation belongs to a different infobox.
local wrong, category, dbg = isPageUsingIncorrectInfobox(invoker, disambiguation, otherInfoboxList)
local isValid, category, debugString = isPageUsingIncorrectInfobox(disambiguation, otherInfoboxList)
if wrong then
if (isValid) then
return category, dbg
return category, debugString
end
end
 
-- Check if the disambiguation is valid.
local ok, dbg2 = validateDisambiguation(invoker, disambiguation, typeList, patternList)
isValid, debugString = validateDisambiguation(invoker, disambiguation, validDisambiguationTypeList, validDisambiguationPatternList)
 
if not ok then
-- Check if the disambiguation is not valid and add category.
return CATEGORY_INCORRECT, dbg2
if (not isValid) then
category = CATEGORY_INCORRECT
end
end


return category, debugString
return "", dbg2
end
end


-----------------------------------------------------------------------
-- Export
-----------------------------------------------------------------------
return {
return {
main = main,
main = main,
DisambiguationPattern = DisambiguationPattern
DisambiguationPattern = DisambiguationPattern,
}
 
VALIDATION_TYPE_YEAR_COUNTRY = validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY,
VALIDATION_TYPE_YEAR = validationTypeList.VALIDATION_TYPE_YEAR,
VALIDATION_TYPE_COUNTRY = validationTypeList.VALIDATION_TYPE_COUNTRY,
VALIDATION_TYPE_YEAR_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_YEAR_SEASON_NUMBER,
VALIDATION_TYPE_COUNTRY_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_COUNTRY_SEASON_NUMBER,
VALIDATION_TYPE_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_SEASON_NUMBER,
VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER
}

Latest revision as of 03:26, 2 July 2026

For actual documentation, view the corresponding Wikipedia pages.


require("strict")

local libraryUtil = require("libraryUtil")

-----------------------------------------------------------------------
-- DisambiguationPattern "class"
-----------------------------------------------------------------------
local function DisambiguationPattern(o)
	local obj = o or { pattern = "", type = 0 }
	libraryUtil.makeCheckSelfFunction(
		"Television infoboxes disambiguation check",
		"DisambiguationPattern",
		obj,
		"Television infoboxes disambiguation check object"
	)
	return obj
end

-----------------------------------------------------------------------
-- Constants
-----------------------------------------------------------------------
local DAB_VALID = {
	[true] = "valid",
	[false] = "invalid"
}

local CATEGORY_INCORRECT = "[[Category:Television articles with incorrect naming style]]"

local validationTypeList = {
	VALIDATION_TYPE_YEAR_COUNTRY = 1,
	VALIDATION_TYPE_YEAR = 2,
	VALIDATION_TYPE_COUNTRY = 3,
	VALIDATION_TYPE_YEAR_SEASON_NUMBER = 4,
	VALIDATION_TYPE_COUNTRY_SEASON_NUMBER = 5,
	VALIDATION_TYPE_SEASON_NUMBER = 6,
	VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER = 8
}

local debugMessageList = {
	DEBUG_EMPTY_TITLE = "Debug: Error: Empty title.",
	DEBUG_NO_DAB = "Debug: No disambiguation.",
	DEBUG_TITLE_ON_EXCEPTION = "Debug: Title on exception list.",
	DEBUG_VALID_FORMAT = "Debug: Using a valid format.",
	DEBUG_NOT_VALID_FORMAT = "Debug: Not a valid format.",
	DEBUG_YEAR_COUNTRY = "Debug: Using a valid format with an extended Year and Country - {}.",
	DEBUG_YEAR = "Debug: Using a valid format with an extended Year - {}.",
	DEBUG_COUNTRY = "Debug: Using a valid format with an extended Country - {}.",
	DEBUG_INCORRECT_STYLE = "Debug: Using a valid format but using an incorrect extended style.",
	DEBUG_INCORRECT_INFOBOX = "Debug: Using incorrect infobox - {}.",
	DEBUG_YEAR_SEASON_NUMBER = "Debug: Using a valid format with an extended Year and Season number - {}.",
	DEBUG_COUNTRY_SEASON_NUMBER = "Debug: Using a valid format with an extended Country and Season number - {}.",
	DEBUG_SEASON_NUMBER = "Debug: Using a valid format with a Season number - {}.",
	DEBUG_YEAR_COUNTRY_SEASON_NUMBER = "Debug: Using a valid format with an extended Year, Country and Season number - {}."
}

-----------------------------------------------------------------------
-- Validation helpers
-----------------------------------------------------------------------
local function validateTwoParameters(a, b)
	return not not (a and b)
end

local function validateSeasonNumber(seasonNumber)
	if not seasonNumber then
		return false
	end
	return tonumber(seasonNumber:sub(1, 1)) ~= 0
end

local function validateYear(year)
	return year and #year == 4
end

local function validateCountryAdjective(adj)
	if not adj or adj == "" then
		return false
	end
	local data = mw.loadData("Module:Country adjective")
	return not not data.getCountryFromAdj[adj]
end

-----------------------------------------------------------------------
-- Pattern validation engine
-----------------------------------------------------------------------
local function validatePatterns(disambiguation, patternList)
	local year, adjective, seasonNumber
	local isYearValid, isAdjectiveValid, isSeasonNumberValid

	for i = 1, #patternList do
		local p = patternList[i]
		if disambiguation:match(p.pattern) then

			-- YEAR + COUNTRY
			if p.type == validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY then
				year, adjective = disambiguation:match(p.pattern)
				isYearValid = validateYear(year)
				isAdjectiveValid = validateCountryAdjective(adjective)
				local ok = validateTwoParameters(isYearValid, isAdjectiveValid)
				return ok, debugMessageList.DEBUG_YEAR_COUNTRY:gsub("{}", DAB_VALID[ok])

			-- YEAR
			elseif p.type == validationTypeList.VALIDATION_TYPE_YEAR then
				year = disambiguation
				isYearValid = validateYear(year)
				return isYearValid, debugMessageList.DEBUG_YEAR:gsub("{}", DAB_VALID[isYearValid])

			-- COUNTRY
			elseif p.type == validationTypeList.VALIDATION_TYPE_COUNTRY then
				adjective = disambiguation
				isAdjectiveValid = validateCountryAdjective(adjective)
				return isAdjectiveValid, debugMessageList.DEBUG_COUNTRY:gsub("{}", DAB_VALID[isAdjectiveValid])

			-- YEAR + SEASON
			elseif p.type == validationTypeList.VALIDATION_TYPE_YEAR_SEASON_NUMBER then
				year, seasonNumber = disambiguation:match(p.pattern)
				isYearValid = validateYear(year)
				isSeasonNumberValid = validateSeasonNumber(seasonNumber)
				local ok = validateTwoParameters(isYearValid, isSeasonNumberValid)
				return ok, debugMessageList.DEBUG_YEAR_SEASON_NUMBER:gsub("{}", DAB_VALID[ok])

			-- COUNTRY + SEASON
			elseif p.type == validationTypeList.VALIDATION_TYPE_COUNTRY_SEASON_NUMBER then
				adjective, seasonNumber = disambiguation:match(p.pattern)
				isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
				isSeasonNumberValid = validateSeasonNumber(seasonNumber)
				local ok = validateTwoParameters(isAdjectiveValid, isSeasonNumberValid)
				return ok, debugMessageList.DEBUG_COUNTRY_SEASON_NUMBER:gsub("{}", DAB_VALID[ok])

			-- SEASON ONLY
			elseif p.type == validationTypeList.VALIDATION_TYPE_SEASON_NUMBER then
				seasonNumber = disambiguation:match(p.pattern)
				isSeasonNumberValid = validateSeasonNumber(seasonNumber)
				return isSeasonNumberValid, debugMessageList.DEBUG_SEASON_NUMBER:gsub("{}", DAB_VALID[isSeasonNumberValid])

			-- YEAR + COUNTRY + SEASON
			elseif p.type == validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER then
				year, adjective, seasonNumber = disambiguation:match(p.pattern)
				isYearValid = validateYear(year)
				isAdjectiveValid = validateCountryAdjective(mw.text.trim(adjective))
				isSeasonNumberValid = validateSeasonNumber(seasonNumber)
				local ok = validateTwoParameters(isYearValid, isAdjectiveValid)
				ok = validateTwoParameters(ok, isSeasonNumberValid)
				return ok, debugMessageList.DEBUG_YEAR_COUNTRY_SEASON_NUMBER:gsub("{}", DAB_VALID[ok])
			end
		end
	end

	return false, debugMessageList.DEBUG_INCORRECT_STYLE
end

-----------------------------------------------------------------------
-- Disambiguation type validation
-----------------------------------------------------------------------
local function validateDisambiguationType(disambiguation, typeList)
	local extended = disambiguation
	local count = 0

	for i = 1, #typeList do
		local t = typeList[i]
		extended, count = extended:gsub(t, "")
		extended = mw.text.trim(extended)
		if count ~= 0 then
			break
		end
	end
	return count ~= 0, extended
end

local function validateDisambiguation(invoker, disambiguation, typeList, patternList)
	if #typeList ~= 0 then
		local ok, extended = validateDisambiguationType(disambiguation, typeList)

		if not ok then
			return false, debugMessageList.DEBUG_NOT_VALID_FORMAT
		end

		if extended == "" then
			return true, debugMessageList.DEBUG_VALID_FORMAT
		end

		if invoker ~= "infobox television season" then
			disambiguation = extended
		end
	end

	return validatePatterns(disambiguation, patternList)
end

-----------------------------------------------------------------------
-- Incorrect infobox detection
-----------------------------------------------------------------------
local function isPageUsingIncorrectInfobox(invoker, disambiguation, otherList)

	-- Only skip incorrect-infobox detection for SEASON pages.
	-- Season pages often have disambiguations like:
	--   "season 1", "series 2", "season 1 TV series"
	-- These should NOT trigger incorrect-infobox errors.
	if invoker == "infobox television season" then
		if disambiguation:match("^[Ss]eason%s+%d+") 
			or disambiguation:match("^[Ss]eries%s+%d+") then
			return false
		end
	end

	-- Normal incorrect-infobox detection
	for k, v in pairs(otherList) do
		if disambiguation:match(k) then
			return true, v, debugMessageList.DEBUG_INCORRECT_INFOBOX:gsub("{}", k)
		end
	end

	return false
end

-----------------------------------------------------------------------
-- Exception list
-----------------------------------------------------------------------
local function isOnExceptionList(title, list)
	for _, v in ipairs(list) do
		if v == title or title:match(v) then
			return true
		end
	end
	return false
end

-----------------------------------------------------------------------
-- Disambiguation extraction (supports new naming)
-----------------------------------------------------------------------
-- Get the disambiguation text and support both:
-- - Parenthetical only: "Big Brother (American TV series)"
-- - Parenthetical + season: "Big Brother (American TV series) season 5"
-- - Season only: "Lost season 1"
local function getDisambiguation(title)
	local match = require("Module:String")._match

	-- Last parenthetical chunk, if any.
	local paren = match(title, "%s%((.-)%)", 1, -1, false, "")

	-- Trailing "season X" or "series X", if any (X can be wrong; validation will decide).
	local base, seasonPart = title:match("^(.-)%s+(season%s+.+)$")
	if not base then
		base, seasonPart = title:match("^(.-)%s+(series%s+.+)$")
	end

	if paren ~= "" and seasonPart then
		-- Example: "Big Brother (American TV series) season 5"
		-- Returns: "American TV series season 5"
		return paren .. " " .. seasonPart
	elseif paren ~= "" then
		-- Example: "Big Brother (American TV series)"
		-- Returns: "American TV series"
		return paren
	elseif seasonPart then
		-- Example: "Lost season 1"
		-- Returns: "season 1"
		return seasonPart
	else
		return ""
	end
end

-----------------------------------------------------------------------
-- Utility
-----------------------------------------------------------------------
local function isEmpty(x)
	return not x or x == ""
end

-----------------------------------------------------------------------
-- Main entry point
-----------------------------------------------------------------------
local function main(title, invoker, typeList, patternList, exceptionList, otherInfoboxList, invalidTitleStyleList)
	if isEmpty(title) then
		return "", debugMessageList.DEBUG_EMPTY_TITLE
	end

	if isOnExceptionList(title, exceptionList) then
		return "", debugMessageList.DEBUG_TITLE_ON_EXCEPTION
	end

	if invoker == "infobox television season" then
		for i = 1, #invalidTitleStyleList do
			if title:find(invalidTitleStyleList[i]) then
				return CATEGORY_INCORRECT, debugMessageList.DEBUG_NOT_VALID_FORMAT
			end
		end
	end

	local disambiguation = getDisambiguation(title)

	if isEmpty(disambiguation) then
		return "", debugMessageList.DEBUG_NO_DAB
	end

	local wrong, category, dbg = isPageUsingIncorrectInfobox(invoker, disambiguation, otherInfoboxList)
	if wrong then
		return category, dbg
	end

	local ok, dbg2 = validateDisambiguation(invoker, disambiguation, typeList, patternList)

	if not ok then
		return CATEGORY_INCORRECT, dbg2
	end

	return "", dbg2
end

-----------------------------------------------------------------------
-- Export
-----------------------------------------------------------------------
return {
	main = main,
	DisambiguationPattern = DisambiguationPattern,

	VALIDATION_TYPE_YEAR_COUNTRY = validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY,
	VALIDATION_TYPE_YEAR = validationTypeList.VALIDATION_TYPE_YEAR,
	VALIDATION_TYPE_COUNTRY = validationTypeList.VALIDATION_TYPE_COUNTRY,
	VALIDATION_TYPE_YEAR_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_YEAR_SEASON_NUMBER,
	VALIDATION_TYPE_COUNTRY_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_COUNTRY_SEASON_NUMBER,
	VALIDATION_TYPE_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_SEASON_NUMBER,
	VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER = validationTypeList.VALIDATION_TYPE_YEAR_COUNTRY_SEASON_NUMBER
}