Module:Television episode short description: Difference between revisions

From IxWiki
Jump to navigation Jump to search
>Gonnym
added support for |special= and |single_season parameters; cleaned up code
 
display ordinals as numbers instead of words in every description, for consistency. See Template talk:Infobox television episode.
Line 1: Line 1:
-- This module requires the use of Module:ConvertNumeric.
--- @module
local convertNumeric = require('Module:ConvertNumeric')
local television = {}


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


Line 15: Line 15:
local test = false
local test = false


-- Description list.
local descriptions = {
local descriptionList = {
no_series = {
["NO_SERIES"] = "A television episode",
type = 1,
["ONLY_SERIES_NAME"] = "An episode of ''%s''",
text = "Television episode",
["EPISODE_AND_SERIES_NAME"] = "An episode of the %s season of ''%s''",
category = "[[Category:Television episode articles with short description with no series name|%s]]",
["ALL_VALUES"] = "%s episode%s of the %s season of ''%s''",
},
["SINGLE_SEASON"] = "%s episode%s of ''%s''",
only_series_name = {
["SPECIAL_EPISODE"] = "A %s episode of ''%s''"
type = 2,
text = "Episode of %s",
category = "[[Category:Television episode articles with short description with no season number|%s]]",
},
season_and_series_name = {
type = 3,
text = "Episode of the %s %s of %s",
category = "[[Category:Television episode articles with short description with no episode number|%s]]",
},
single_episode = {
type = 4,
text = "%s episode of the %s %s of %s",
category = "[[Category:Television episode articles with short description for single episodes|%s]]",
},
multi_episodes = {
type = 5,
text = "%s episodes of the %s %s of %s",
category = "[[Category:Television episode articles with short description for multi-part episodes|%s]]",
},
limited_series = {
type = 6,
text = {
single_episode = "%s episode of %s",
multi_episodes = "%s episodes of %s",
},
category = "", -- None
},
special_episode = {
type = 7,
text = "%s episode of %s",
category = "", -- None
},
}
}


-- Tracking category list.
-- Tracking category list.
local trackingCategoryList = {
local trackingCategories = {
["NO_SERIES"] = '[[Category:Television episode articles with short description with no series name|%s]]',
disambiguated = "[[Category:Television episode articles with short description and disambiguated page names|%s]]"
["NO_SEASON_NUMBER"] = '[[Category:Television episode articles with short description with no season number|%s]]',
["NO_EPISODE_NUMBER"] = '[[Category:Television episode articles with short description with no episode number|%s]]',
["SINGLE_EPISODE"] = '[[Category:Television episode articles with short description for single episodes|%s]]',
["MULTI_EPISODE"] = '[[Category:Television episode articles with short description for multi-part episodes|%s]]',
["DISAMBIGUATED_TITLE"] = '[[Category:Television episode articles with short description and disambiguated page names|%s]]'
}
}


local p = {}
--- Returns a tracking category from a list by its name and adds a sort key.
--- @param typeName string The name of the category type.
--- @param useTrackingList boolean Whether to return a category from the trackingCategories list.
--- @param sortKey string The key by which to sort the page in the category.
local function getTrackingCategoryFromList(typeName, useTrackingList, sortKey)
local category
if useTrackingList then
category = trackingCategories[typeName]
else
category = descriptions[typeName].category
end
return string.format(category, sortKey)
end
 
--- Returns true 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)".
--- @param articleTitle string The name of the page.
--- @param tvSeriesName string The TV series name.
local function isDisambiguated(articleTitle, tvSeriesName)
local disambiguation = string.match(tostring(articleTitle), "%s%((.-)%)")


--[[
if not (disambiguation and tvSeriesName) then
Local function which is used to retrieve the ordinal indicator for an integer between 0 and 100.
return false
--]]
local function getOrdinalIndicatorLessThan100(number)
local suffix -- Variable to save the ordinal indicator suffix.
while (not suffix) 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
end
return suffix -- Return the suffix.
end


--[[
-- Search for the TV series name in the article name disambiguation.
Local function which is used to retrieve the ordinal indicator for an integer between 0 and 1000.
if (string.find(disambiguation, tvSeriesName)) then
--]]
return true
local function getOrdinalIndicatorLessThan1000(number)
if (number < 100) then -- Check if the number is less than 100.
return getOrdinalIndicatorLessThan100(number) -- The number is less than 100; 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
end


--[[
return false
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
end


--[[
--- Returns the sort key for the current page.
Local function which retrieves the correct category with a sort key.
--]]
local function getCategory(categoryKey, sortKey)
local category = trackingCategoryList[categoryKey]
return string.format(category, sortKey)
end
--[[
Local function which "Module:Sort title" to retrieve a sortkey.
--]]
local function getSortKey()
local function getSortKey()
local sortkeyModule = require('Module:Sort title')
local sortTitleModule = require("Module:Sort title")
return sortkeyModule._getSortKey()
return sortTitleModule._getSortKey()
end
end


--[[
--- Returns a tracking category depending on the type of short description created.
Local function which is used to check if the article name is disambiguated.
--- @param tvSeriesName string The TV series name.
This is usually in the format of "Episode name (<TV series name>)" or "Episode name (<TV series name> episode)".
--- @param descriptionName string
--]]
local function getTrackingCategory(tvSeriesName, descriptionName)
local function isDisambiguated(articleTitle, tvSeriesName)
local articleTitle = mw.title.getCurrentTitle()
local disambiguation = string.match(tostring(articleTitle), "%s%((.-)%)") -- Get the text inside the disambiguation parentheses.
local namespace = articleTitle.nsText


if (disambiguation and tvSeriesName) then -- Check if the article has parentheses and that the TV series name is not nil.
-- Check if the invoking page is from the allowed namespace.
if (string.find(disambiguation, tvSeriesName)) then -- Article has parentheses; Search for the TV series name in the article name disambiguation.
if (not (namespace == "" or namespace == "Draft" or test)) then
return true -- Article is disambiguated; Return true.
return ""
else
return false -- Article is not disambiguated; Return false.
end
else
return false -- Article does not have parentheses; Return false.
end
end
end


--[[
local sortKey = getSortKey()
Local function which is used to return a relevant tracking category.
if (isDisambiguated(articleTitle, tvSeriesName) == true) then
--]]
local category1 = getTrackingCategoryFromList(descriptionName, false, sortKey)
local function createTrackingCategory(tvSeriesName, categoryKey)
local category2 = getTrackingCategoryFromList("disambiguated", true, sortKey)
local articleTitle = mw.title.getCurrentTitle() -- Get the current page's title.
return category1 .. category2
local namespace = articleTitle.nsText -- Get the invoking namespace.
local sortKey = getSortKey() -- Get sort key.
if (namespace == '' or namespace == 'Draft' or test) 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 getCategory(categoryKey, sortKey) .. getCategory("DISAMBIGUATED_TITLE", sortKey) -- Article is disambiguated; Call getCategory() to retrieve the correct tracking categories and return them.
else
return getCategory(categoryKey, sortKey) -- 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
return getTrackingCategoryFromList(descriptionName, false, sortKey)
end
end


--[[
 
Local function which is used to create a short description in the style of: "A television episode".
--- Returns a short description in the style of: "Television episode" and a maintenance category:
Adds article to the maintenance category: "Category:Television episode articles with short description with no series name".
--- "Category:Television episode articles with short description with no series name".
--]]
local function getShortDescriptionNoSeries()
local function getShortDescriptionNoValues()
local shortDescription = descriptions.no_series.text
return descriptionList["NO_SERIES"], createTrackingCategory(nil, "NO_SERIES")
local category = getTrackingCategory(nil, "no_series")
return shortDescription, category
end
end


--[[
--- Returns a short description in the style of: "Episode of Lost" and a maintenance category:
Local function which is used to create a short description in the style of: "An episode of ''Lost''".
--- "Category:Television episode articles with short description with no season number".
Adds article to the maintenance category: "Category:Television episode articles with short description with no season number".
--- @param tvSeriesName string The TV series name.
--]]
local function getShortDescriptionOnlySeriesName(tvSeriesName)
local function getShortDescriptionNoEpisodeNoSeasonsValues(tvSeriesName)
local text = descriptions.only_series_name.text
return  string.format(descriptionList["ONLY_SERIES_NAME"], tvSeriesName), createTrackingCategory(tvSeriesName, "NO_SEASON_NUMBER")
local shortDescription = string.format(text, tvSeriesName)
local category = getTrackingCategory(tvSeriesName, "only_series_name")
return shortDescription, category
end
end


--[[
--- Returns a short description in the style of: "Episode of the first season of Lost" and a maintenance category:
Local function which is used to create a short description in the style of: "An episode of the first season of ''Lost''".
--- "Category:Television episode articles with short description with no episode number".
Adds article to the maintenance category: "Category:Television episode articles with short description with no episode number".
--- @param tvSeriesName string The TV series name.
--]]
--- @param seasonOrdinalNumber string The season's ordinal number.
local function getShortDescriptionNoEpisodeValue(seasonOrdinalNumber, tvSeriesName)
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
return string.format(descriptionList["EPISODE_AND_SERIES_NAME"], seasonOrdinalNumber, tvSeriesName), createTrackingCategory(tvSeriesName, "NO_EPISODE_NUMBER")
local function getShortDescriptionSeasonAndSeriesName(tvSeriesName, seasonOrdinalNumber, seasonTextStyle)
local text = descriptions.season_and_series_name.text
local shortDescription = string.format(text, seasonOrdinalNumber, seasonTextStyle, tvSeriesName)
local category = getTrackingCategory(tvSeriesName, "season_and_series_name")
return shortDescription, category
end
end


--[[
--- Returns a short description for a limited series in the style of: "1st episode of WandaVision" and a tracking category
Local function which is used to create a short description for single season episodes in the style of: "1st episode of ''Lost''".
--- based on the categoryKey value.
Adds article to the tracking category: "Category:Television episode articles with short description for single episodes".
--- @param tvSeriesName string The TV series name.
--]]
--- @param episodeOrdinalNumber string The episode's ordinal number.
local function getShortDescriptionSingleSeason(episodeOrdinalNumber, plural, tvSeriesName, category)
--- @param descriptionName string A key from the descriptions table.
return string.format(descriptionList["SINGLE_SEASON"], episodeOrdinalNumber, plural, tvSeriesName), createTrackingCategory(tvSeriesName, category)
local function getShortDescriptionLimitedSeries(tvSeriesName, episodeOrdinalNumber, descriptionName)
local text = descriptions.limited_series.text[descriptionName]
local shortDescription = string.format(text, episodeOrdinalNumber, tvSeriesName)
local category = getTrackingCategory(tvSeriesName, descriptionName)
return shortDescription, category
end
end


--[[
--- Returns a short description in the style of: "5th episode of the fourth season of Lost" and a tracking category:
Local function which is used to create a short description in the style of: "5th episode of the fourth season of ''Lost''".
--- "Category:Television episode articles with short description for single episodes".
Adds article to the tracking category: "Category:Television episode articles with short description for single episodes".
--- @param tvSeriesName string The TV series name.
--]]
--- @param seasonOrdinalNumber string The season's ordinal number.
local function getShortDescriptionSingleEpisode(episodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName, singleSeason)
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
if (singleSeason) then
--- @param episodeOrdinalNumber string The episode's ordinal number.
return getShortDescriptionSingleSeason(episodeOrdinalNumber, "", tvSeriesName, "SINGLE_EPISODE")
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
else
local function getShortDescriptionSingleEpisode(tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumber, limitedSeries)
return string.format(descriptionList["ALL_VALUES"], episodeOrdinalNumber, "", seasonOrdinalNumber, tvSeriesName), createTrackingCategory(tvSeriesName, "SINGLE_EPISODE")
if (limitedSeries) then
return getShortDescriptionLimitedSeries(tvSeriesName, episodeOrdinalNumber,"single_episode")
end
end
local text = descriptions.single_episode.text
local shortDescription =  string.format(text, episodeOrdinalNumber, seasonOrdinalNumber, seasonTextStyle, tvSeriesName)
local category = getTrackingCategory(tvSeriesName, "single_episode")
return shortDescription, category
end
end


--[[
--- Returns a short description for a multi-part episode in the style of:
Local function which is used to create a short description for a multi-part episode in the style of: "23rd and 24th episodes of the third season of ''Lost''".
--- "23rd and 24th episodes of the third season of Lost" and a tracking category:
Adds article to the tracking category: "Category:Television episode articles with short description for multi-part episodes".
--- "Category:Television episode articles with short description for multi-part episodes".
--]]
--- @param tvSeriesName string The TV series name.
local function getShortDescriptionMultiEpisode(episodeOrdinalNumber, episodeNumber, seasonOrdinalNumber, tvSeriesName, multiEpisodes, singleSeason)
--- @param seasonOrdinalNumber string The season's ordinal number.
local episodeOrdinalList = {episodeOrdinalNumber}
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
-- Check if the |multi_episodes value was a number or a "yes" string.
--- @param episodeOrdinalNumbers table A list of episode ordinal numbers.
if (tonumber(multiEpisodes)) then
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
multiEpisodes = tonumber(multiEpisodes)
local function getShortDescriptionMultiEpisode(tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers, limitedSeries)
-- If the value was entered as 1, this isn't a multi-episode.
local episodeText = mw.text.listToText(episodeOrdinalNumbers)
if (multiEpisodes == 1) then
 
return getShortDescriptionSingleEpisode(episodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName)
if (limitedSeries) then
end
return getShortDescriptionLimitedSeries(tvSeriesName, episodeText, "multi_episodes")
-- Go over the amount entered minus 1 (as the first episode ordinal is already known).
for i = 1, multiEpisodes - 1 do
table.insert(episodeOrdinalList, getEpisodeOrdinalNumber(episodeNumber + i))
end
else
-- The value entered was "yes", use as default 2 episodes.
table.insert(episodeOrdinalList, getEpisodeOrdinalNumber(episodeNumber + 1))
end
local episodeText = mw.text.listToText(episodeOrdinalList)
if (singleSeason) then
return getShortDescriptionSingleSeason(episodeText, "s", tvSeriesName, "MULTI_EPISODE")
else
return string.format(descriptionList["ALL_VALUES"], episodeText, "s", seasonOrdinalNumber, tvSeriesName), createTrackingCategory(tvSeriesName, "MULTI_EPISODE")
end
end
local text = descriptions.multi_episodes.text
local shortDescription = string.format(text, episodeText, seasonOrdinalNumber, seasonTextStyle, tvSeriesName)
local category = getTrackingCategory(tvSeriesName, "multi_episodes")
return shortDescription, category
end
end


--[[
--- Returns a short description for a special episode in the style of:
Local function which is used to create a short description for a special episode in the style of: "A special episode of ''Lost''" or "A <value used for |special=> episode of ''Lost''"
--- "Special episode of Lost" or "<value> episode of Lost" and a tracking category:
Adds article to the tracking category: "Category:Television episode articles with short description for single episodes".
--- "Category:Television episode articles with short description for single episodes".
--]]
--- @param tvSeriesName string The TV series name.
local function getShortDescriptionSpecialEpisode(special, tvSeriesName)
--- @param special string The type of special episode. A "yes" value defaults to "Special".
local function getShortDescriptionSpecialEpisode(tvSeriesName, special)
if (special == "yes" or special == "y") then
if (special == "yes" or special == "y") then
special = "special"
special = "Special"
end
end
return string.format(descriptionList["SPECIAL_EPISODE"], special, tvSeriesName), createTrackingCategory(tvSeriesName, "SINGLE_EPISODE")
local text = descriptions.special_episode.text
local shortDescription = string.format(text, special, tvSeriesName)
local category = getTrackingCategory(tvSeriesName, "single_episode")
return shortDescription, category
end
end


--[[
--- Returns a short description based on the description type passed.
Local function which is used to validate if data was entered into a parameter of type number.
--- @param descriptionType number A description type number.
--]]
--- @param tvSeriesName string The TV series name.
local function validateNumberParam(number)
--- @param seasonOrdinalNumber string The season's ordinal number.
if (tonumber(number)) then -- Convert the string into a number and check if the value equals nil (conversion failed).
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
return true -- Param is a number; Return true.
--- @param episodeOrdinalNumbers table A list of episode ordinal numbers.
--- @param specialEpisode string The type of special episode.
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
local function getShortDescriptionByType(
descriptionType, tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers, specialEpisode, limitedSeries)
if descriptionType == descriptions.no_series.type then
return getShortDescriptionNoSeries()
elseif descriptionType == descriptions.only_series_name.type then
return getShortDescriptionOnlySeriesName(tvSeriesName)
elseif descriptionType == descriptions.season_and_series_name.type then
return getShortDescriptionSeasonAndSeriesName(tvSeriesName, seasonOrdinalNumber, seasonTextStyle)
elseif descriptionType == descriptions.single_episode.type then
return getShortDescriptionSingleEpisode(
                tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers[1], limitedSeries)
elseif descriptionType == descriptions.multi_episodes.type then
return getShortDescriptionMultiEpisode(
tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers, limitedSeries)
elseif descriptionType == descriptions.special_episode.type then
return getShortDescriptionSpecialEpisode(tvSeriesName, specialEpisode)
else
else
return false -- Param is either empty or not a number; Return false.
return ""
end
end
end
end


--[[
--- Returns the type of the description to use.
Local function which is used to return a clean version of the number.
--- @param tvSeriesName string The TV series name.
This is done to make sure that no malformed episode or season values
--- @param seasonOrdinalNumber string The season's ordinal number.
have been entered. The function will remove all text which is not part
--- @param episodeOrdinalNumbers table A list of episode ordinal numbers.
of the first number in the string.
--- @param specialEpisode string The type of special episode.
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
local function getDescriptionType(tvSeriesName, seasonOrdinalNumber, episodeOrdinalNumbers, specialEpisode, limitedSeries)
if (not tvSeriesName) then
return descriptions.no_series.type
end
 
if (specialEpisode) then
return descriptions.special_episode.type
end
 
if (not seasonOrdinalNumber and not limitedSeries) then
return descriptions.only_series_name.type
end
 
if (#episodeOrdinalNumbers < 1) then
return descriptions.season_and_series_name.type
end
 
if (#episodeOrdinalNumbers == 1) then
return descriptions.single_episode.type
end


The function converts entries such as:
if (#episodeOrdinalNumbers > 1) then
-- "1.2" -> "1"
return descriptions.multi_episodes.type
-- "12.2" -> "12"
-- "1<ref name="number" />" -> "1"
--]]
local function getCleanNumber(number)
if (number) then -- Check if the number is not nil (some kind of value was entered).
return string.match(number, '%d+') -- The value is not null; Clean the number, if needed.
else
return nil -- The number is nil; Return nil.
end
end
end
end


--[[
--- Returns true if the TV series is a limited series.
Local function which is used to create a short description
--- @param limitedSeries string Any value will be considered as true.
by validating if a "multi_episodes" value was entered.
local function isLimitedSeries(limitedSeries)
--]]
if (limitedSeries) then
local function createDescriptionValidateEpisodeValue(args, tvSeriesName, seasonOrdinalNumber)
return true
local episodeNumber = getCleanNumber(args['episode_num']) -- Call getCleanNumber() to return a cleaned version of the number.
end
episodeNumber = tonumber(episodeNumber) -- Convert the value into a number.
return false
end


if (validateNumberParam(episodeNumber)) then -- Call validateNumberParam() to check if an episode number was entered.
--- Returns the ordinal indicator for an integer between 0 and 100.
local episodeOrdinalNumber = getEpisodeOrdinalNumber(episodeNumber) -- A number was entered; Call getEpisodeOrdinalNumber() to get the episode ordinal number.
---
local multiEpisodes = args['multi_episodes']
--- Numbers "1", "2" and "3" have unique suffixes.
local singleSeason = false
--- Numbers between 4 and 20 have the same common suffix - "th".
if (seasonOrdinalNumber == -1) then
--- Numbers ending with 0 have the same common suffix - "th".
singleSeason = true
--- @param number number A number value.
end
local function getOrdinalIndicatorLessThan100(number)
if (multiEpisodes) then -- Check if a |multi_episodes= value was entered.
local suffix
return getShortDescriptionMultiEpisode(episodeOrdinalNumber, episodeNumber, seasonOrdinalNumber, tvSeriesName, multiEpisodes, singleSeason) -- A |multi_episodes= value was entered; Call getShortDescriptionMultiEpisode().
while (not suffix) do
-- Check if the number equals 0; This should never be a valid entry. Assign suffix as an empty string.
if (number == 0) then
suffix = ""
-- Check if the number is less than 4; Numbers "1", "2" and "3" have unique suffixes.
elseif (number < 4) then
suffix = uniqueSuffix[number]
-- Check if the number is more than 4 AND less than 20; These numbers all have the same common suffix.
elseif (number < 20) then
suffix = commonSuffix
-- Check if the remainder after division of the number by 10 equals 0.
elseif (number % 10 == 0) then
suffix = commonSuffix
else
else
return getShortDescriptionSingleEpisode(episodeOrdinalNumber, seasonOrdinalNumber, tvSeriesName, singleSeason) -- A |multi_episodes= value was not entered; Call getShortDescriptionSingleEpisode().
-- Numbers that are above 20 and which their remainder doesn't equal 0 (such as 45).
-- Remainder after division of the number by 10; So if the current number is 45, the new number is 5.
number = number % 10
end
end
end
return suffix
end
--- Returns the ordinal indicator for an integer between 0 and 1000.
--- @param number number A number value.
local function getOrdinalIndicatorLessThan1000(number)
if (number < 100) then
return getOrdinalIndicatorLessThan100(number)
elseif (number % 100 == 0) then
return commonSuffix
else
else
return getShortDescriptionNoEpisodeValue(seasonOrdinalNumber, tvSeriesName) -- A an episode number was not entered; Call getShortDescriptionNoEpisodeValue().
-- Numbers that are above 100 and which their remainder doesn't equal 0 (such as 345).
-- Pass the remainder after division of the number by 100 (So for 345, it would pass 45) as the parameter.
return getOrdinalIndicatorLessThan100(number % 100)
end
end
 
--- Returns a table of episode numbers.
---
--- Episode values may be of multipart episodes, in such situations, an episode may be seperated by one of the following:
--- ",", "/", "&", "-", "–", "and".
--- Decimal values and episode overall values sometimes erroneously used are removed.
--- @param number string A number value in string format.
local function cleanEpisodeNumber(number)
if (not number) then
return {}
end
 
number = string.gsub(number, "%(.*%)", " ")
number = string.gsub(number, "%.%d+", " ")
 
local numbers = {}
for digits in string.gmatch(number, "%d+") do
table.insert(numbers, tonumber(digits))
end
return numbers
end
 
--- Returns a table of episode ordinal numbers.
---
--- In most situations there will be only one episode, but this can support more.
--- @param episodeNumber number The episode's number.
local function getEpisodeOrdinalNumbers(episodeNumber)
local episodeNumbers = cleanEpisodeNumber(episodeNumber)
 
if (#episodeNumbers < 1) then
return episodeNumbers
end
end
local episodeOrdinals = {}
for _, cleanedEpisodeNumber in pairs(episodeNumbers) do
local ordinalIndicator = getOrdinalIndicatorLessThan1000(cleanedEpisodeNumber)
table.insert(episodeOrdinals, cleanedEpisodeNumber .. ordinalIndicator)
end
return episodeOrdinals
end
end


--[[
--- Returns true if the season number value is a number.
Local function which is used to retrieve the season number, since it can be entered in
--- @param seasonNumber string The season number value in string format.
either the "season" or "series_no" params.
local function validateSeasonNumber(seasonNumber)
--]]
if (tonumber(seasonNumber)) then
local function getSeasonNumber(seasonNumber, seasonNumberUK)
return true
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
else
return "" -- Anything else - value not entered. Return empty string.
return false
end
end
 
--- Returns the season's ordinal number, or nil if no season number was set.
--- @param seasonNumber string The season number.
local function getSeasonOrdinalNumber(seasonNumber)
if (seasonNumber) then
local convertOrdinal = require("Module:Ordinal")
return convertOrdinal._ordinal(seasonNumber)
end
end
return nil
end
end


--[[
--- Returns a season number after removing from it unwanted characters.
Local function which is used to create a short description by validating if a season number was entered.
---
--]]
--- This is done to make sure that no malformed season values have been entered.
local function createDescriptionValidateSeasonValue(args, tvSeriesName)
--- The function will remove all text which is not part of the first number in the string.
local seasonNumber = getSeasonNumber(args['season_num'], args['season_num_uk']) -- 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.
--- The function converts entries such as:
local seasonOrdinalNumber = convertNumeric.spell_number2({num = seasonNumber, ordinal = true}) -- A season number was entered; Call spell_number2() from Module:ConvertNumeric to get the season ordinal number.
--- "1.2" -> "1"
return createDescriptionValidateEpisodeValue(args, tvSeriesName, seasonOrdinalNumber) -- Call createDescriptionValidateEpisodeValue() to continue validation process.
--- "12.2" -> "12"
elseif (args['single_season']) then -- A season number was not entered; Check if a |single_season= value was entered.
--- @param seasonNumber string The season number value in string format.
return createDescriptionValidateEpisodeValue(args, tvSeriesName, -1) -- |single_season= was entered; Call createDescriptionValidateEpisodeValue().
local function cleanSeasonNumber(seasonNumber)
elseif (args['special']) then -- Check if a |special= value was entered.
if (seasonNumber) then
return getShortDescriptionSpecialEpisode(args['special'], tvSeriesName) -- Call getShortDescriptionSpecialEpisode().
return string.match(seasonNumber, "%d+")
else
return getShortDescriptionNoEpisodeNoSeasonsValues(tvSeriesName) -- A special value was not entered; Call getShortDescriptionNoEpisodeNoSeasonsValues().
end
end
return nil
end
end


--[[
--- Returns the season number after or cleaning it from unwanted values and validating value is a number.
Local function which is used to create a short description.
--- Also returns the text style to use - either "season" or "series".
This creates a description by a process of validating which values have values.
--- If no value was entered or if value was not a number, return nil.
These are the following options:
--- @param seasonNumber string The season number.
-- If no |series_name= was entered, it calls getShortDescriptionNoValues().
--- @param seasonNumberUK string The season number, if UK style was used.
-- If only |series_name= and |season_num= or |season_num_uk= were entered, it calls getShortDescriptionNoEpisodeValue().
local function getSeasonNumberAndTextStyle(seasonNumber, seasonNumberUK)
-- If all information was entered and |multi_episodes= was not entered, it calls getShortDescriptionSingleEpisode().
    for _, v in ipairs({{seasonNumber, "season"}, {seasonNumberUK, "series"}}) do
-- If all information and |multi_episodes= was entered, it calls getShortDescriptionDoubleEpisode().
        local cleanedSeasonNumber = cleanSeasonNumber(v[1])
-- If |series_name= and |special= was entered, it calls getShortDescriptionSpecialEpisode().
        if (validateSeasonNumber(cleanedSeasonNumber)) then
-- If |series_name=, |episode_num= and |no_season= were entered, it calls getShortDescriptionNoSeason().
            return cleanedSeasonNumber, v[2]
--]]
        end
local function getDescription(args)
    end
local tvSeriesName = args['series_name']
return nil
if (tvSeriesName) then -- Check if a TV series name was entered.
end
if (not args['not_dab']) 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.
--- Returns the TV series title without disambiguation, or nil if no TV series name was set.
--- @param tvSeriesName string The TV series name.
--- @param notDab string If set, the parenthesis in the title is not disambiguation.
local function getTVSeriesName(tvSeriesName, notDab)
if (tvSeriesName) then
if (not notDab) then
return string.gsub(tvSeriesName, "%s+%b()$", "", 1, false)
end
end
return createDescriptionValidateSeasonValue(args, tvSeriesName) -- Call createDescriptionValidateSeasonValue() to continue validation process.
return tvSeriesName
else
return getShortDescriptionNoValues() -- A TV series name was not entered; Call getShortDescriptionNoValues().
end
end
return nil
end
end


--[[
--- Returns the initial values after removing unwanted characters.
Local function which is used to clean the values from unwanted characters.
--- @param args table The values that should be processed.
--]]
local function cleanValues(args)
local function getCleanValues(args)
for _, v in ipairs({"episode_num", "season_num", "season_num_uk", "series_name"}) do
for _, v in ipairs({'episode_num', 'season_num', 'season_num_uk', 'series_name'}) do
if (args[v]) then
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("\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("</? *br */?>", " ") -- Replace <br /> (and variants) with space character.
args[v] = args[v]:gsub('%b<>[^<]+%b<>', '') -- Remove html markup.
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("%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 label.
args[v] = args[v]:gsub('%[%[([^%]]+)%]%]', '%1') -- Remove wiki-link retain article.
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("%[%S+ +([^%]]-)%]", "%1") -- Remove URLs retain label.
args[v] = args[v]:gsub('%[[^%]]-%]', '') -- Remove all remaining URLs.
args[v] = args[v]:gsub("%[[^%]]-%]", "") -- Remove all remaining URLs.


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


--[[
--- Public function - main process.
Public function which does the actual main process.
--- @param frame table The frame invoking the module.
--]]
--- @param args table The key-value parameters passed to the module.
function p._getShortDescription(frame, args)
function television._getShortDescription(frame, args)
args = getCleanValues(args) -- Call getCleanValues() to remove all unwanted characters.
args = cleanValues(args)
local shortDescription, trackingCat = getDescription(args) -- Call getDescription() and return two values: the episode's short description and tracking category.
local tvSeriesName = getTVSeriesName(args.series_name, args.not_dab)
    local seasonNumber, seasonTextStyle = getSeasonNumberAndTextStyle(args.season_num, args.season_num_uk)
local seasonOrdinalNumber = getSeasonOrdinalNumber(seasonNumber)
local episodeOrdinalNumbers = getEpisodeOrdinalNumbers(args.episode_num)
local limitedSeries = isLimitedSeries(args.limited)
local descriptionType = getDescriptionType(
tvSeriesName,
seasonOrdinalNumber,
episodeOrdinalNumbers,
args.special,
limitedSeries
)
 
local shortDescription, trackingCat = getShortDescriptionByType(
descriptionType,
tvSeriesName,
seasonOrdinalNumber,
            seasonTextStyle,
episodeOrdinalNumbers,
args.special,
limitedSeries
)


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


--[[
--- Public function which is used to create a television episode's short description
Public function which is used to create a television episode's short description
--- from the data available in [Template:Infobox television episode].
from the data available in [Template:Infobox television episode].
--- A suitable description will be generated depending on the values of the various parameters.
A suitable description will be generated depending on the values
--- See documentation for examples.
of the various parameters. See documentation for examples.
---
--- 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.
--- |not_dab= — optional; Set if the TV series name has parentheses as part of its name.
--- |special= — optional; Setting to "yes" will set the description as a "special episode".
--- Any other value will replace the word "special" with the one entered.
--- For example "special=recap" will create "recap episode".
--- |limited= — optional; Set if the series is a single season series, such as miniseries or limited series
--- and does not need a season number as part of the description.
--- @param frame table The frame invoking the module.
function television.getShortDescription(frame)
local getArgs = require("Module:Arguments").getArgs
local args = getArgs(frame)
return television._getShortDescription(frame, args)
end


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; Setting "yes" will default to a two-part episode.
If there are more than 2 parts, set the value to the number of parts.
-- |not_dab= — optional; Set if the TV series name has parentheses as part of its name.
-- |special= — optional; Setting to "yes" will set the description as a "special episode".
Any other value will replace the word "special" with the one entered. For example "special=recap" will create "recap episode".
-- |single_season= — optional; Set if the series is a single season series, such as miniseries or limited series and does not need "1st season" as part of the description.
--]]
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 p._getShortDescription(frame, args) -- Call _getShortDescription() to perform the actual process.
--- Public function which is used for testing output only.
end
--- @param frame table The frame invoking the module.
function television.test(frame)
local getArgs = require("Module:Arguments").getArgs
local args = getArgs(frame)
 
test = args.test
local shortDescription, categories = television._getShortDescription(frame, args)


--[[
Public function which is used for testing only.
--]]
function p.test(frame)
local getArgs = require('Module:Arguments').getArgs
local args = getArgs(frame)
test = args['test'] -- This param should only be used by tests runned through /testcases.
local shortDescription, categories = p._getShortDescription(frame, args)
if (test == "cat") then
if (test == "cat") then
return categories
return categories
Line 414: Line 540:
end
end


return p
return television

Revision as of 08:29, 29 August 2022

Module:Television episode short description extracts data from a television episode article's Template:Infobox television episode and creates a relevant short description based on the data available.

This module implements the {{Television episode short description}} template.

Usage

Parameter list

The parameter names use a corresponding parameter from Template:Infobox television episode to fill in the data. They are listed below.

Parameter Corresponding infobox parameter Description
series_name series The TV series name.
episode_num episode The episode's number.
season_num season The season's number.
season_num_uk series_no The season's number if using the British "series" term.
not_dab same name Set if the TV series name has parentheses as part of its name.
special same name special=recap will set the text to "recap episode".
limited same name Set if the series is a single season series, such as miniseries or limited series and does not need a season number as part of the description.

Examples

Issues

  1. If an article does not show the short description with the data from the infobox, make sure you entered the parameter names correctly in the infobox.

Tracking categories

See also


--- @module
local television = {}

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

-- Common suffix.
local commonSuffix = "th"

-- Test validation.
local test = false

local descriptions = {
	no_series = {
		type = 1,
		text = "Television episode",
		category = "[[Category:Television episode articles with short description with no series name|%s]]",
	},
	only_series_name = {
		type = 2,
		text = "Episode of %s",
		category = "[[Category:Television episode articles with short description with no season number|%s]]",
	},
	season_and_series_name = {
		type = 3,
		text = "Episode of the %s %s of %s",
		category = "[[Category:Television episode articles with short description with no episode number|%s]]",
	},
	single_episode = {
		type = 4,
		text = "%s episode of the %s %s of %s",
		category = "[[Category:Television episode articles with short description for single episodes|%s]]",
	},
	multi_episodes = {
		type = 5,
		text = "%s episodes of the %s %s of %s",
		category = "[[Category:Television episode articles with short description for multi-part episodes|%s]]",
	},
	limited_series = {
		type = 6,
		text = {
			single_episode = "%s episode of %s",
			multi_episodes = "%s episodes of %s",
		},
		category = "", -- None
	},
	special_episode = {
		type = 7,
		text = "%s episode of %s",
		category = "", -- None
	},
}

-- Tracking category list.
local trackingCategories = {
	disambiguated = "[[Category:Television episode articles with short description and disambiguated page names|%s]]"
}

--- Returns a tracking category from a list by its name and adds a sort key.
--- @param typeName string The name of the category type.
--- @param useTrackingList boolean Whether to return a category from the trackingCategories list.
--- @param sortKey string The key by which to sort the page in the category.
local function getTrackingCategoryFromList(typeName, useTrackingList, sortKey)
	local category
	if useTrackingList then
		category = trackingCategories[typeName]
	else
		category = descriptions[typeName].category
	end
	return string.format(category, sortKey)
end

--- Returns true 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)".
--- @param articleTitle string The name of the page.
--- @param tvSeriesName string The TV series name.
local function isDisambiguated(articleTitle, tvSeriesName)
	local disambiguation = string.match(tostring(articleTitle), "%s%((.-)%)")

	if not (disambiguation and tvSeriesName) then
		return false
	end

	-- Search for the TV series name in the article name disambiguation.
	if (string.find(disambiguation, tvSeriesName)) then
		return true
	end

	return false
end

--- Returns the sort key for the current page.
local function getSortKey()
	local sortTitleModule = require("Module:Sort title")
	return sortTitleModule._getSortKey()
end

--- Returns a tracking category depending on the type of short description created.
--- @param tvSeriesName string The TV series name.
--- @param descriptionName string
local function getTrackingCategory(tvSeriesName, descriptionName)
	local articleTitle = mw.title.getCurrentTitle()
	local namespace = articleTitle.nsText

	-- Check if the invoking page is from the allowed namespace.
	if (not (namespace == "" or namespace == "Draft" or test)) then
		return ""
	end

	local sortKey = getSortKey()
	if (isDisambiguated(articleTitle, tvSeriesName) == true) then
		local category1 = getTrackingCategoryFromList(descriptionName, false, sortKey)
		local category2 = getTrackingCategoryFromList("disambiguated", true, sortKey)
		return category1 .. category2
	end

	return getTrackingCategoryFromList(descriptionName, false, sortKey)
end


--- Returns a short description in the style of: "Television episode" and a maintenance category:
--- "Category:Television episode articles with short description with no series name".
local function getShortDescriptionNoSeries()
	local shortDescription = descriptions.no_series.text
	local category = getTrackingCategory(nil, "no_series")
	return shortDescription, category
end

--- Returns a short description in the style of: "Episode of Lost" and a maintenance category:
--- "Category:Television episode articles with short description with no season number".
--- @param tvSeriesName string The TV series name.
local function getShortDescriptionOnlySeriesName(tvSeriesName)
	local text = descriptions.only_series_name.text
	local shortDescription = string.format(text, tvSeriesName)
	local category = getTrackingCategory(tvSeriesName, "only_series_name")
	return shortDescription, category
end

--- Returns a short description in the style of: "Episode of the first season of Lost" and a maintenance category:
--- "Category:Television episode articles with short description with no episode number".
--- @param tvSeriesName string The TV series name.
--- @param seasonOrdinalNumber string The season's ordinal number.
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
local function getShortDescriptionSeasonAndSeriesName(tvSeriesName, seasonOrdinalNumber, seasonTextStyle)
	local text = descriptions.season_and_series_name.text
	local shortDescription = string.format(text, seasonOrdinalNumber, seasonTextStyle, tvSeriesName)
	local category = getTrackingCategory(tvSeriesName, "season_and_series_name")
	return shortDescription, category
end

--- Returns a short description for a limited series in the style of: "1st episode of WandaVision" and a tracking category
--- based on the categoryKey value.
--- @param tvSeriesName string The TV series name.
--- @param episodeOrdinalNumber string The episode's ordinal number.
--- @param descriptionName string A key from the descriptions table.
local function getShortDescriptionLimitedSeries(tvSeriesName, episodeOrdinalNumber, descriptionName)
	local text = descriptions.limited_series.text[descriptionName]
	local shortDescription = string.format(text, episodeOrdinalNumber, tvSeriesName)
	local category = getTrackingCategory(tvSeriesName, descriptionName)
	return shortDescription, category
end

--- Returns a short description in the style of: "5th episode of the fourth season of Lost" and a tracking category:
--- "Category:Television episode articles with short description for single episodes".
--- @param tvSeriesName string The TV series name.
--- @param seasonOrdinalNumber string The season's ordinal number.
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
--- @param episodeOrdinalNumber string The episode's ordinal number.
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
local function getShortDescriptionSingleEpisode(tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumber, limitedSeries)
	if (limitedSeries) then
		return getShortDescriptionLimitedSeries(tvSeriesName, episodeOrdinalNumber,"single_episode")
	end

	local text = descriptions.single_episode.text
	local shortDescription =  string.format(text, episodeOrdinalNumber, seasonOrdinalNumber, seasonTextStyle, tvSeriesName)
	local category = getTrackingCategory(tvSeriesName, "single_episode")
	return shortDescription, category
end

--- Returns a short description for a multi-part episode in the style of:
--- "23rd and 24th episodes of the third season of Lost" and a tracking category:
--- "Category:Television episode articles with short description for multi-part episodes".
--- @param tvSeriesName string The TV series name.
--- @param seasonOrdinalNumber string The season's ordinal number.
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
--- @param episodeOrdinalNumbers table A list of episode ordinal numbers.
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
local function getShortDescriptionMultiEpisode(tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers, limitedSeries)
	local episodeText = mw.text.listToText(episodeOrdinalNumbers)

	if (limitedSeries) then
		return getShortDescriptionLimitedSeries(tvSeriesName, episodeText, "multi_episodes")
	end

	local text = descriptions.multi_episodes.text
	local shortDescription = string.format(text, episodeText, seasonOrdinalNumber, seasonTextStyle, tvSeriesName)
	local category = getTrackingCategory(tvSeriesName, "multi_episodes")
	return shortDescription, category
end

--- Returns a short description for a special episode in the style of:
--- "Special episode of Lost" or "<value> episode of Lost" and a tracking category:
--- "Category:Television episode articles with short description for single episodes".
--- @param tvSeriesName string The TV series name.
--- @param special string The type of special episode. A "yes" value defaults to "Special".
local function getShortDescriptionSpecialEpisode(tvSeriesName, special)
	if (special == "yes" or special == "y") then
		special = "Special"
	end
	local text = descriptions.special_episode.text
	local shortDescription = string.format(text, special, tvSeriesName)
	local category = getTrackingCategory(tvSeriesName, "single_episode")
	return shortDescription, category
end

--- Returns a short description based on the description type passed.
--- @param descriptionType number A description type number.
--- @param tvSeriesName string The TV series name.
--- @param seasonOrdinalNumber string The season's ordinal number.
--- @param seasonTextStyle string The text to use for seasons - either "season" or "series".
--- @param episodeOrdinalNumbers table A list of episode ordinal numbers.
--- @param specialEpisode string The type of special episode.
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
local function getShortDescriptionByType(
		descriptionType, tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers, specialEpisode, limitedSeries)
	if descriptionType == descriptions.no_series.type then
		return getShortDescriptionNoSeries()
	elseif descriptionType == descriptions.only_series_name.type then
		return getShortDescriptionOnlySeriesName(tvSeriesName)
	elseif descriptionType == descriptions.season_and_series_name.type then
		return getShortDescriptionSeasonAndSeriesName(tvSeriesName, seasonOrdinalNumber, seasonTextStyle)
	elseif descriptionType == descriptions.single_episode.type then
		return getShortDescriptionSingleEpisode(
                tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers[1], limitedSeries)
	elseif descriptionType == descriptions.multi_episodes.type then
		return getShortDescriptionMultiEpisode(
				tvSeriesName, seasonOrdinalNumber, seasonTextStyle, episodeOrdinalNumbers, limitedSeries)
	elseif descriptionType == descriptions.special_episode.type then
		return getShortDescriptionSpecialEpisode(tvSeriesName, specialEpisode)
	else
		return ""
	end
end

--- Returns the type of the description to use.
--- @param tvSeriesName string The TV series name.
--- @param seasonOrdinalNumber string The season's ordinal number.
--- @param episodeOrdinalNumbers table A list of episode ordinal numbers.
--- @param specialEpisode string The type of special episode.
--- @param limitedSeries boolean Whether the episode belongs to a limited series.
local function getDescriptionType(tvSeriesName, seasonOrdinalNumber, episodeOrdinalNumbers, specialEpisode, limitedSeries)
	if (not tvSeriesName) then
		return descriptions.no_series.type
	end

	if (specialEpisode) then
		return descriptions.special_episode.type
	end

	if (not seasonOrdinalNumber and not limitedSeries) then
		return descriptions.only_series_name.type
	end

	if (#episodeOrdinalNumbers < 1) then
		return descriptions.season_and_series_name.type
	end

	if (#episodeOrdinalNumbers == 1) then
		return descriptions.single_episode.type
	end

	if (#episodeOrdinalNumbers > 1) then
		return descriptions.multi_episodes.type
	end
end

--- Returns true if the TV series is a limited series.
--- @param limitedSeries string Any value will be considered as true.
local function isLimitedSeries(limitedSeries)
	if (limitedSeries) then
		return true
	end
	return false
end

--- Returns the ordinal indicator for an integer between 0 and 100.
---
--- Numbers "1", "2" and "3" have unique suffixes.
--- Numbers between 4 and 20 have the same common suffix - "th".
--- Numbers ending with 0 have the same common suffix - "th".
--- @param number number A number value.
local function getOrdinalIndicatorLessThan100(number)
	local suffix
	while (not suffix) do
		-- Check if the number equals 0; This should never be a valid entry. Assign suffix as an empty string.
		if (number == 0) then
			suffix = ""
		-- Check if the number is less than 4; Numbers "1", "2" and "3" have unique suffixes.
		elseif (number < 4) then
			suffix = uniqueSuffix[number]
		-- Check if the number is more than 4 AND less than 20; These numbers all have the same common suffix.
		elseif (number < 20) then
			suffix = commonSuffix
		-- Check if the remainder after division of the number by 10 equals 0.
		elseif (number % 10 == 0) then
			suffix = commonSuffix
		else
			-- Numbers that are above 20 and which their remainder doesn't equal 0 (such as 45).
			-- Remainder after division of the number by 10; So if the current number is 45, the new number is 5.
			number = number % 10
		end
	end
	return suffix
end

--- Returns the ordinal indicator for an integer between 0 and 1000.
--- @param number number A number value.
local function getOrdinalIndicatorLessThan1000(number)
	if (number < 100) then
		return getOrdinalIndicatorLessThan100(number)
	elseif (number % 100 == 0) then
		return commonSuffix
	else
		-- Numbers that are above 100 and which their remainder doesn't equal 0 (such as 345).
		-- Pass the remainder after division of the number by 100 (So for 345, it would pass 45) as the parameter.
		return getOrdinalIndicatorLessThan100(number % 100)
	end
end

--- Returns a table of episode numbers.
---
--- Episode values may be of multipart episodes, in such situations, an episode may be seperated by one of the following:
--- ",", "/", "&", "-", "–", "and".
--- Decimal values and episode overall values sometimes erroneously used are removed.
--- @param number string A number value in string format.
local function cleanEpisodeNumber(number)
	if (not number) then
		return {}
	end

	number = string.gsub(number, "%(.*%)", " ")
	number = string.gsub(number, "%.%d+", " ")

	local numbers = {}
	for digits in string.gmatch(number, "%d+") do
		table.insert(numbers, tonumber(digits))
	end
	return numbers
end

--- Returns a table of episode ordinal numbers.
---
--- In most situations there will be only one episode, but this can support more.
--- @param episodeNumber number The episode's number.
local function getEpisodeOrdinalNumbers(episodeNumber)
	local episodeNumbers = cleanEpisodeNumber(episodeNumber)

	if (#episodeNumbers < 1) then
		return episodeNumbers
	end

	local episodeOrdinals = {}
	for _, cleanedEpisodeNumber in pairs(episodeNumbers) do
		local ordinalIndicator = getOrdinalIndicatorLessThan1000(cleanedEpisodeNumber)
		table.insert(episodeOrdinals, cleanedEpisodeNumber .. ordinalIndicator)
	end

	return episodeOrdinals
end

--- Returns true if the season number value is a number.
--- @param seasonNumber string The season number value in string format.
local function validateSeasonNumber(seasonNumber)
	if (tonumber(seasonNumber)) then
		return true
	else
		return false
	end
end

--- Returns the season's ordinal number, or nil if no season number was set.
--- @param seasonNumber string The season number.
local function getSeasonOrdinalNumber(seasonNumber)
	if (seasonNumber) then
		local convertOrdinal = require("Module:Ordinal")
		return convertOrdinal._ordinal(seasonNumber)
	end
	return nil
end

--- Returns a season number after removing from it unwanted characters.
---
--- This is done to make sure that no malformed 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"
--- @param seasonNumber string The season number value in string format.
local function cleanSeasonNumber(seasonNumber)
	if (seasonNumber) then
		return string.match(seasonNumber, "%d+")
	end
	return nil
end

--- Returns the season number after or cleaning it from unwanted values and validating value is a number.
--- Also returns the text style to use - either "season" or "series".
--- If no value was entered or if value was not a number, return nil.
--- @param seasonNumber string The season number.
--- @param seasonNumberUK string The season number, if UK style was used.
local function getSeasonNumberAndTextStyle(seasonNumber, seasonNumberUK)
    for _, v in ipairs({{seasonNumber, "season"}, {seasonNumberUK, "series"}}) do
        local cleanedSeasonNumber = cleanSeasonNumber(v[1])
        if (validateSeasonNumber(cleanedSeasonNumber)) then
            return cleanedSeasonNumber, v[2]
        end
    end
	return nil
end

--- Returns the TV series title without disambiguation, or nil if no TV series name was set.
--- @param tvSeriesName string The TV series name.
--- @param notDab string If set, the parenthesis in the title is not disambiguation.
local function getTVSeriesName(tvSeriesName, notDab)
	if (tvSeriesName) then
		if (not notDab) then
			return string.gsub(tvSeriesName, "%s+%b()$", "", 1, false)
		end
		return tvSeriesName
	end
	return nil
end

--- Returns the initial values after removing unwanted characters.
--- @param args table The values that should be processed.
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
end

--- Public function - main process.
--- @param frame table The frame invoking the module.
--- @param args table The key-value parameters passed to the module.
function television._getShortDescription(frame, args)
	args = cleanValues(args)
	local tvSeriesName = getTVSeriesName(args.series_name, args.not_dab)
    local seasonNumber, seasonTextStyle = getSeasonNumberAndTextStyle(args.season_num, args.season_num_uk)
	local seasonOrdinalNumber = getSeasonOrdinalNumber(seasonNumber)
	local episodeOrdinalNumbers = getEpisodeOrdinalNumbers(args.episode_num)
	local limitedSeries = isLimitedSeries(args.limited)
	local descriptionType = getDescriptionType(
			tvSeriesName,
			seasonOrdinalNumber,
			episodeOrdinalNumbers,
			args.special,
			limitedSeries
	)

	local shortDescription, trackingCat = getShortDescriptionByType(
			descriptionType,
			tvSeriesName,
			seasonOrdinalNumber,
            seasonTextStyle,
			episodeOrdinalNumbers,
			args.special,
			limitedSeries
	)

	-- Check if the invoking page is from /testcases or /doc pages.
	if (args.test) then
		return shortDescription, trackingCat
	elseif (args.doc) then
		return shortDescription
	else
		local tableData = {shortDescription, "noreplace"}
		return frame:expandTemplate({title = "short description", args = tableData}) .. trackingCat
	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.
---
--- 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.
--- |not_dab=			— optional; Set if the TV series name has parentheses as part of its name.
--- |special=			— optional; Setting to "yes" will set the description as a "special episode".
---							Any other value will replace the word "special" with the one entered.
---							For example "special=recap" will create "recap episode".
---	|limited=			— optional; Set if the series is a single season series, such as miniseries or limited series
---							and does not need a season number as part of the description.
--- @param frame table The frame invoking the module.
function television.getShortDescription(frame)
	local getArgs = require("Module:Arguments").getArgs
	local args = getArgs(frame)
	return television._getShortDescription(frame, args)
end


--- Public function which is used for testing output only.
--- @param frame table The frame invoking the module.
function television.test(frame)
	local getArgs = require("Module:Arguments").getArgs
	local args = getArgs(frame)

	test = args.test
	local shortDescription, categories = television._getShortDescription(frame, args)

	if (test == "cat") then
		return categories
	else
		return shortDescription
	end
end

return television