Module:Television episode short description: Difference between revisions
Jump to navigation
Jump to search
>Gonnym added support for |special= and |single_season parameters; cleaned up code |
m 1 revision imported |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
-- | --- @module | ||
local | local television = {} | ||
-- Unique suffix list. | -- Unique suffix list. | ||
local uniqueSuffix = { | local uniqueSuffix = { | ||
[1] = | [1] = "st", | ||
[2] = | [2] = "nd", | ||
[3] = | [3] = "rd", | ||
} | } | ||
Line 15: | Line 15: | ||
local test = false | local test = false | ||
local descriptions = { | |||
local | 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. | -- Tracking category list. | ||
local | local trackingCategories = { | ||
disambiguated = "[[Category:Television episode articles with short description and disambiguated page names|%s]]" | |||
} | } | ||
local | --- 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 | end | ||
-- | -- Search for the TV series name in the article name disambiguation. | ||
if (string.find(disambiguation, tvSeriesName)) then | |||
return true | |||
if ( | |||
return | |||
end | end | ||
return false | |||
end | end | ||
-- | --- Returns the sort key for the current page. | ||
local function getSortKey() | local function getSortKey() | ||
local | local sortTitleModule = require("Module:Sort title") | ||
return | return sortTitleModule._getSortKey() | ||
end | 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 function | local articleTitle = mw.title.getCurrentTitle() | ||
local | 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 | 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 | |||
local sortKey = getSortKey() | |||
end | end | ||
return getTrackingCategoryFromList(descriptionName, false, sortKey) | |||
end | 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 function | local shortDescription = descriptions.no_series.text | ||
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: | ||
--- "Category:Television episode articles with short description with no season number". | |||
--- @param tvSeriesName string The TV series name. | |||
-- | local function getShortDescriptionOnlySeriesName(tvSeriesName) | ||
local function | local text = descriptions.only_series_name.text | ||
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: | ||
--- "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 | --- @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 | 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. | ||
local function | --- @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 | 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. | ||
local function getShortDescriptionSingleEpisode( | --- @param seasonTextStyle string The text to use for seasons - either "season" or "series". | ||
if ( | --- @param episodeOrdinalNumber string The episode's ordinal number. | ||
return | --- @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 | 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: | ||
--- "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") | |||
local episodeText = mw.text.listToText( | |||
if ( | |||
return | |||
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: | ||
--- "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. | ||
local function getShortDescriptionSpecialEpisode(special | --- @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" | ||
end | end | ||
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. | ||
--- @param descriptionType number A description type number. | |||
-- | --- @param tvSeriesName string The TV series name. | ||
local function | --- @param seasonOrdinalNumber string The season's ordinal number. | ||
if ( | --- @param seasonTextStyle string The text to use for seasons - either "season" or "series". | ||
return | --- @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 | return "" | ||
end | end | ||
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 | |||
return | |||
end | end | ||
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 | ||
local function | 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". | |||
if ( | --- 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 | 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 | ||
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 | -- 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. | ||
--- @param seasonNumber string The season number value in string format. | |||
local function validateSeasonNumber(seasonNumber) | |||
-- | if (tonumber(seasonNumber)) then | ||
local function | return true | ||
if ( | |||
return | |||
else | else | ||
return | 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. | ||
--- | |||
-- | --- 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 | return string.match(seasonNumber, "%d+") | ||
end | end | ||
return nil | |||
end | 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 | ||
local function | end | ||
return nil | |||
if (tvSeriesName) then | end | ||
if (not | |||
--- 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 | return tvSeriesName | ||
end | end | ||
return nil | |||
end | end | ||
-- | --- Returns the initial values after removing unwanted characters. | ||
--- @param args table The values that should be processed. | |||
-- | local function cleanValues(args) | ||
local function | for _, v in ipairs({"episode_num", "season_num", "season_num_uk", "series_name"}) do | ||
for _, v in ipairs({ | |||
if (args[v]) then | if (args[v]) then | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("\127[^\127]*UNIQ%-%-(%a+)%-%x+%-QINU[^\127]*\127", "") -- Remove all strip-markers. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("</? *br */?>", " ") -- Replace <br /> (and variants) with space character. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("%b<>[^<]+%b<>", "") -- Remove html markup. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("%b<>", "") -- Remove self-closed html tags. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("%[%[[^|]+|([^%]]+)%]%]", "%1") -- Remove wiki-link retain label. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("%[%[([^%]]+)%]%]", "%1") -- Remove wiki-link retain article. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("%[%S+ +([^%]]-)%]", "%1") -- Remove URLs retain label. | ||
args[v] = args[v]:gsub( | args[v] = args[v]:gsub("%[[^%]]-%]", "") -- Remove all remaining URLs. | ||
if (args[v] == | if (args[v] == "") then -- Check if the value is an empty string. | ||
args[v] = nil | args[v] = nil -- The value is an empty string; Set it to nil. | ||
end | end | ||
end | end | ||
end | end | ||
return args | return args | ||
end | end | ||
-- | --- Public function - main process. | ||
Public function | --- @param frame table The frame invoking the module. | ||
-- | --- @param args table The key-value parameters passed to the module. | ||
function | function television._getShortDescription(frame, args) | ||
args = | args = cleanValues(args) | ||
local shortDescription, trackingCat = | 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 | if (args.test) then | ||
return shortDescription, trackingCat | return shortDescription, trackingCat | ||
elseif (args | elseif (args.doc) then | ||
return shortDescription | return shortDescription | ||
else | else | ||
local tableData = {shortDescription, | local tableData = {shortDescription, "noreplace"} | ||
return frame:expandTemplate({title = | 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 | |||
--- 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 | if (test == "cat") then | ||
return categories | return categories | ||
Line 414: | Line 540: | ||
end | end | ||
return | return television |
Latest revision as of 00:59, 5 January 2023
This module depends on the following other modules: |
This Lua module is used on approximately 15,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
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
{{#invoke:Television episode short description|getShortDescription}}
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
- 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
- Category:Television episode articles with short description with no series name (0)
- Category:Television episode articles with short description with no season number (0)
- Category:Television episode articles with short description with no episode number (0)
- Category:Television episode articles with short description for single episodes (1)
- Category:Television episode articles with short description for multi-part episodes (0)
- Category:Television episode articles with short description and disambiguated page names (1)
- Category:Pages using Television episode short description with unknown parameters (0)
See also
- Template:Television episode short description
- Module:Extract short description
- Module:Television episode disambiguation description
- Template:Television episode disambiguation description
--- @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