Module:Television episode short description
For actual documentation, view the corresponding Wikipedia pages.
--- @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