Module:Television episode short description

From Wikihussain
Revision as of 11:59, 10 November 2018 by en>MSGJ (fixed incorrect logic)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Documentation for this module may be created at Module:Television episode short description/doc

-- This module requires the use of Module:ConvertNumeric.
local convertNumeric = require('Module:ConvertNumeric')

-- Unique suffix list.
local uniqueSuffix = {
	[1] = 'st',
	[2] = 'nd',
	[3] = 'rd',
}

-- Common suffix.
local commonSuffix = "th"

-- Tracking category list.
local trackingCategoryList = {
	[1] = '[[Category:Television episode articles with short description with no series name]]',
	[2] = '[[Category:Television episode articles with short description with no season number]]',
	[3] = '[[Category:Television episode articles with short description with no episode number]]',
	[4] = '[[Category:Television episode articles with short description for single episodes]]',
	[5] = '[[Category:Television episode articles with short description for multi-part episodes]]',
	[6] = '[[Category:Television episode articles with short description and disambiguated page names]]'
}

local p = {}

-- Local function which is used to check if the article name is disambiguated.
-- This is usually in the format of "Episode name (<TV series name>)" or "Episode name (<TV series name> episode)".
local function isDisambiguated(articleTitle, tvSeriesName)
	local disambiguation = string.match(tostring(articleTitle), "%s%((.-)%)")								-- Get the text inside the disambiguation parentheses.

	if disambiguation and tvSeriesName then																	-- Check if the article has parentheses and that the TV series name is not nil.
		if (string.find(disambiguation, tvSeriesName)) then 												-- Article has parentheses; Search for the TV series name in the article name disambiguation.
			return true																						-- Article is disambiguated; Return true.
		else
			return false																					-- Article is not disambiguated; Return false.
		end
	else
		return false																						-- Article does not have parentheses; Return false.
	end
end

-- Local function which is used to return a relevant tracking category.
local function createTrackingCategory(tvSeriesName, categoryNumber)
	local articleTitle = mw.title.getCurrentTitle()															-- Get the current page's title.
	local namespace = articleTitle.nsText;																	-- Get the invoking namespace.
	if (namespace == '' or namespace == 'Draft') then														-- Check if the invoking page is from the allowed namespace.
		if (isDisambiguated(articleTitle, tvSeriesName) == true) then										-- Invoking page is from the allowed namespace; Call isDisambiguated() to check if page is disambiguated.
			return trackingCategoryList[categoryNumber] .. trackingCategoryList[6]							-- Article is disambiguated; Retrieve the correct tracking categories and return them.
		else
			return trackingCategoryList[categoryNumber]														-- Article is not disambiguated; Retrieve the correct tracking category and return it.
		end
	else
		return ''																							-- Invoking page is not from the allowed namespace; Return empty string.
	end
end

-- Local function which is used to create a short description in the style of: "A television episode".
-- Add article to the maintenance category: [[Category:Television episode articles with short description with no series name]].
local function getShortDescriptionNoValues()
	return "A television episode", createTrackingCategory(nil, 1)
end

-- Local function which is used to create a short description in the style of: "An episode of ''Lost''".
-- Add article to the maintenance category: [[Category:Television episode articles with short description with no season number]].
local function getShortDescriptionNoEpisodeNoSeasonsValues(tvSeriesName)
	return "An episode of ''" .. tvSeriesName .. "''", createTrackingCategory(tvSeriesName, 2)
end

-- Local function which is used to create a short description in the style of: "An episode of the first season of ''Lost''".
-- Add article to the maintenance category: [[Category:Television episode articles with short description with no episode number]].
local function getShortDescriptionNoEpisodeValue(seasonOrdinalNumber, tvSeriesName)
	return "An episode of the " .. seasonOrdinalNumber .. " season of ''" .. tvSeriesName .. "''", createTrackingCategory(tvSeriesName, 3)
end

-- Local function which is used to create a short description in the style of: "5th episode of the fourth season of ''Lost''".
-- Add article to the tracking category: [[Category:Television episode articles with short description for single episodes]].
local function getShortDescriptionSingleEpisode(episodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName)
	return episodeOrdinalNumber .. " episode of the " .. seasonOrdinalNumber .. " season of ''" .. tvSeriesName .. "''", createTrackingCategory(tvSeriesName, 4)
end

-- Local function which is used to create a short description for double episodes in the style of: "23rd and 24th episodes of the third season of ''Lost''".
-- Add article to the tracking category: [[Category:Television episode articles with short description for multi-part episodes]].
local function getShortDescriptionDoubleEpisode(episodeOrdinalNumber, secondEpisodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName)
	return episodeOrdinalNumber .. " and " .. secondEpisodeOrdinalNumber .. " episodes of the " .. seasonOrdinalNumber .. " season of ''" .. tvSeriesName .. "''", createTrackingCategory(tvSeriesName, 5)
end

-- Local function which is used to retrieve the ordinal indicator for an integer between 0 and 100.
local function getOrdinalIndicatorLessThan100(number)
	local suffix																							-- Variable to save the ordinal indicator suffix.

	while (suffix == nil) do																				-- Initiate a loop that goes on until a suffix has been found.
		if (number == 0) then																				-- Check if the number equals 0; This should never be a valid entry.
			suffix = ""																						-- Assign suffix as an empty string.
		elseif (number < 4) then																			-- Check if the number is less than 4; Numbers "1", "2" and "3" have unique suffixes.
			suffix = uniqueSuffix[number]																	-- It is; Get the unique suffix for that number and assign it.
		elseif (number < 20) then																			-- Check if the number is more than 4 AND less than 20; These numbers all have the same common suffix.
			suffix = commonSuffix																			-- It is; Assign suffix as the common suffix - "th".
		elseif (number % 10 == 0) then																		-- Check if the remainder after division of the number by 10 equals 0.
			suffix = commonSuffix																			-- It is; Assign suffix as the common suffix - "th".
		else																								-- Anything else - numbers that are above 20 and which their remainder doesn't equal 0 (such as 45).
			number = number % 10																			-- Save the new number to the remainder after division of the number by 100;
																											-- So if the current number is 45, the new number be 5.
		end
	end
	return suffix																							-- Return the suffix.
end

-- Local function which is used to retrieve the ordinal indicator for an integer between 0 and 1000.
local function getOrdinalIndicatorLessThan1000(number)
	if (number < 100) then																					-- Check if the number is less than 100.
																											-- The number is less than 100;
		return getOrdinalIndicatorLessThan100(number)														-- Call getOrdinalIndicatorLessThan100() to get the ordinal indicator and return it.
	elseif (number % 100 == 0) then																			-- Check if the remainder after division of the number by 100 equals 0.
		return commonSuffix																					-- It does; Return the common suffix - "th".
	else																									-- Anything else - numbers that are above 100 and which their remainder doesn't equal 0 (such as 345).
		return getOrdinalIndicatorLessThan100(number % 100)													-- Call getOrdinalIndicatorLessThan100() to get the ordinal indicator and return it;
																											-- Pass the remainder after division of the number by 100 (So for 345, it would pass 45) as the parameter.
	end
end

-- Local function which is used to create an ordinal number.
local function getEpisodeOrdinalNumber(number)
	local ordinalIndicator = getOrdinalIndicatorLessThan1000(number)										-- Call getOrdinalIndicatorLessThan1000() to get the number's ordinal indicator.
	return number .. ordinalIndicator																		-- Create an ordinal number and return it.
end

-- Local function which is used to validate if data was
-- entered into a parameter of type number.
local function validateNumberParam(number)
	if (tonumber(number) == nil) then																		-- Convert the string into a number and check if the value equals nil (conversion failed).
		return false																						-- Param is either empty or not a number; Return false.
	else																									-- Param is a number;
		return true																							-- Return true.
	end
end

-- Local function which is used to return a clean version of the number.
-- This is done to make sure that no malformed episode or season values
-- have been entered. The function will remove all text which is not part
-- of the first number in the string.
--
-- The function converts entries such as:
-- "1.2" -> "1";
-- "12.2" -> "12";
-- "1<ref name="number" />" -> "1"
local function getCleanNumber(number)
	if (number ~= nil) then																					-- Check if the number is not null (some kind of value was entered).
		local cleanNumber = string.match(number, '%d+')														-- The value is not null; Clean the number, if needed.
		return cleanNumber																					-- Return the number.
	else
		return nil																							-- The number is nil; Return nil.
	end
end

-- Local function which is used to create a short description
-- by validating if a "multi_episodes" value was entered.
local function createDescriptionValidateEpisodeValue(episodeNumberText, seasonOrdinalNumber, tvSeriesName, isDoubleEpisode)
	local episodeNumber = getCleanNumber(episodeNumberText)													-- Call getCleanNumber() to return a cleaned version of the number.
	episodeNumber = tonumber(episodeNumber)																	-- Convert the value into a number.

	if (validateNumberParam(episodeNumber)) then															-- Call validateNumberParam() to check if an episode number was entered.
		local episodeOrdinalNumber = getEpisodeOrdinalNumber(episodeNumber)									-- A number was entered; Call getEpisodeOrdinalNumber() to get the episode ordinal number.

		if (isDoubleEpisode == nil) then																	-- Check if a multi_episodes value was entered.
																											-- A multi_episodes value was not entered;
			return getShortDescriptionSingleEpisode(
				episodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName)									-- Call getShortDescriptionSingleEpisode() to get the episode's short description and return it.
		else																								-- A multi_episodes value was entered;
			local secondEpisodeOrdinalNumber = getEpisodeOrdinalNumber(episodeNumber + 1)					-- Call getEpisodeOrdinalNumber() and increment, to get the second episode ordinal number.
			return getShortDescriptionDoubleEpisode(
				episodeOrdinalNumber, secondEpisodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName)		-- Call getShortDescriptionDoubleEpisode() to get the double episode's short description and return it.
		end
	else																									-- A an episode number was not entered;
		return getShortDescriptionNoEpisodeValue(seasonOrdinalNumber, tvSeriesName)							-- Call getShortDescriptionNoEpisodeValue() to get the short description and return it.
	end
end

-- Local function which is used to retrieve the season number, since it can be entered in
-- either the "season" or "series_no" params.
local function getSeasonNumber(seasonNumber, seasonNumberUK)
	seasonNumber = getCleanNumber(seasonNumber)																-- Call getCleanNumber() to return a cleaned version of the number.
	seasonNumberUK = getCleanNumber(seasonNumberUK)															-- Call getCleanNumber() to return a cleaned version of the number.

	if (validateNumberParam(seasonNumber)) then																-- Call validateNumberParam() to check if the value
																											-- in the "|season_num" ("season") param is a number.
		return seasonNumber																					-- It is; Return value.
	elseif (validateNumberParam(seasonNumberUK)) then														-- Call validateNumberParam() to check if the value
																											-- in the "|season_num_uk" ("series_no") param is a number.
		return seasonNumberUK																				-- It is; Return value.
	else																									-- Anything else - value not entered.
		return ""																							-- Return empty string.
	end
end

-- Local function which is used to create a short description
-- by validating if a season number was entered.
local function createDescriptionValidateSeasonValue(episodeNumberText, seasonNumber, seasonNumberUK, tvSeriesName, isDoubleEpisode)
	seasonNumber = getSeasonNumber(seasonNumber, seasonNumberUK)											-- Call getSeasonNumber() to get the season number, as it can be in one of two fields.

	if (validateNumberParam(seasonNumber)) then																-- Call validateNumberParam() to check if a season number was entered.
																											-- A season number was entered;
		local seasonOrdinalNumber = convertNumeric.spell_number(											-- Call spell_number() from Module:ConvertNumeric to get the season ordinal number.
			seasonNumber,																					-- Pass it the season number.
			nil,																							-- Not used here.
			nil,																							-- Not used here.
			false,																							-- Not used here.
			false,																							-- Not used here.
			false,																							-- Not used here.
			true,																							-- Get the season ordinal number from the season number.
			false,																							-- Not used here.
			nil,																							-- Not used here.
			nil,																							-- Not used here.
			nil,																							-- Not used here.
			nil,																							-- Not used here.
			false)																							-- Not used here.

		return createDescriptionValidateEpisodeValue(episodeNumberText, 									-- Call createDescriptionValidateEpisodeValue() to continue validation process.
			seasonOrdinalNumber, tvSeriesName, isDoubleEpisode)
	else																									-- A season number was not entered;
		return getShortDescriptionNoEpisodeNoSeasonsValues(tvSeriesName)									-- Call getShortDescriptionNoEpisodeNoSeasonsValues() to get the short description and return it.
	end
end

-- Local function which is used to create a short description.
-- This creates a description by a process of validating which values where entered
-- into the Infobox and has the following options:
-- If no TV series name was entered, it calls getShortDescriptionNoValues().
-- If only the TV series name and season number were entered, it calls getShortDescriptionNoEpisodeValue().
-- If all information was entered and it is a single episode, it calls getShortDescriptionSingleEpisode().
-- If all information was entered and it is a double episode, it calls getShortDescriptionDoubleEpisode().
local function createDescription(episodeNumberText, seasonNumber, seasonNumberUK, tvSeriesName, isDoubleEpisode, notDisambiguated)
	if (tvSeriesName ~= nil) then																			-- Check if a TV series name was entered.

		if (notDisambiguated == nil) then																	-- A TV series name was entered; Check if a not_dab value was entered.
			tvSeriesName = string.gsub(tvSeriesName, "%s+%b()$", "", 1, false)								-- A not_dab value was not entered; Get the article title without the disambiguation.
		end

		return createDescriptionValidateSeasonValue(episodeNumberText,										-- Call createDescriptionValidateSeasonValue() to continue validation process.
			seasonNumber, seasonNumberUK, tvSeriesName, isDoubleEpisode)
	else																									-- A TV series name was not entered;
		return getShortDescriptionNoValues()																-- Call getShortDescriptionNoValues() to get the short description and return it.
	end
end

-- Local function which is used to clean the values from unwanted characters.
local function cleanValues(args)
	for _, v in ipairs({'episode_num', 'season_num', 'season_num_uk', 'series_name'}) do
		if args[v] then
			args[v] = args[v]:gsub('\127[^\127]*UNIQ%-%-(%a+)%-%x+%-QINU[^\127]*\127', '')					-- Remove all strip-markers.
			args[v] = args[v]:gsub('</? *br */?>', ' ')														-- Replace <br /> (and variants) with space character.
			args[v] = args[v]:gsub('%b<>[^<]+%b<>', '')														-- Remove html markup.
			args[v] = args[v]:gsub('%b<>', '')																-- Remove self-closed html tags.
			args[v] = args[v]:gsub('%[%[[^|]+|([^%]]+)%]%]', '%1')											-- Remove wiki-link retain label.
			args[v] = args[v]:gsub('%[%[([^%]]+)%]%]', '%1')												-- Remove wiki-link retain article.
			args[v] = args[v]:gsub('%[%S+ +([^%]]-)%]', '%1')												-- Remove URLs retain label.
			args[v] = args[v]:gsub('%[[^%]]-%]', '')														-- Remove all remaining URLs.

			if (args[v] == '') then																			-- Check if the value is an empty string.
				args[v] = nil																				-- The value is an empty string; Set it to nil.
			end
		end
	end
	return args																								-- Return args.
end

-- Local function which does the actual main process.
local function _getShortDescription(frame, args)
	args = cleanValues(args)																				-- Call cleanValues() to remove all unwanted characters.

	local episodeNumberText = args['episode_num']															-- Get the episode number in text.
	local seasonNumber = args['season_num']																	-- Get the season number from the {{{season}}} param.
	local seasonNumberUK = args['season_num_uk']															-- Get the season number if it's written in the "series_no" param.
	local tvSeriesName = args['series_name']																-- Get the TV series name.
	local isDoubleEpisode = args['multi_episodes']															-- Get the value of "multi_episodes" (used for episode consisting of two episodes).
	local notDisambiguated = args['not_dab']																-- Get the value of "not_dab" (used if the TV series name has parentheses as part of its name).

	local test = args['test']																				-- This param should only be used by tests runned through /testcases.
	local doc = args['doc']																					-- This param should only be used by the documentation page.

	local shortDescription, trackingCat = createDescription(
		episodeNumberText, seasonNumber, seasonNumberUK, tvSeriesName, isDoubleEpisode, notDisambiguated)	-- Call createDescription() and return two values: the episode's short description and tracking category.

	if (test == nil and doc == nil) then																	-- Check if the invoking page is from /testcases or doc pages.
		local tableData = {shortDescription, 'noreplace'}													-- Invoking page isn't a test; Create a table for the short description parameter.
		return frame:expandTemplate({title = 'short description', args = tableData}) .. trackingCat			-- Return expanded short description with tracking category.
	else
		return shortDescription																				-- Invoking page is a test; Return only short description.
	end
end

-- Public function which is used to create a television episode's short description
-- from the data available in [[Template:Infobox television episode]].
-- A suitable description will be generated depending on the values
-- of the various parameters. See documentation for examples.
--
-- This module function takes six parameters:
-- |episode_num= — optional; The episode's number
-- |season_num= — optional; The season's number.
-- |season_num_uk= — optional; The season's number if using the British "series" term.
-- |series_name= — optional; The TV series name.
-- |multi_episodes= — optional; Set if the episode is a double episode.
-- |not_dab= — optional; Set if the TV series name has parentheses as part of its name.
function p.getShortDescription(frame)
	local getArgs = require('Module:Arguments').getArgs;													-- Use Module:Arguments to access module arguments.
	local args = getArgs(frame);																			-- Get the arguments sent via the template.

	return _getShortDescription(frame, args)																-- Call _getShortDescription() to perform the actual process.
end

return p