Difference between revisions of "Module:Card/models/Limitation history"
(function to render the status history as a table) |
(Allow for debut date to be specified. Use that to fill in period of being Unlimited before first limitation, if applicable.) |
||
Line 1: | Line 1: | ||
-- @field card string Page name of the card whose history this is for | -- @field card string Page name of the card whose history this is for | ||
-- @field format string The format the history is for (e.g. "Advanced Format", "OCG", "Korean") | -- @field format string The format the history is for (e.g. "Advanced Format", "OCG", "Korean") | ||
+ | -- @field debutDate string ISO date of when the card was first released in the format | ||
-- @field history table List with details on each limitation list that the card was on and the status it had on each list | -- @field history table List with details on each limitation list that the card was on and the status it had on each list | ||
-- @field groupedHistory table Shorter version of `history` where consecutive lists in which the card had the same status are grouped together | -- @field groupedHistory table Shorter version of `history` where consecutive lists in which the card had the same status are grouped together | ||
Line 7: | Line 8: | ||
card = nil, | card = nil, | ||
format = nil, | format = nil, | ||
+ | debutDate = nil, | ||
history = {}, | history = {}, | ||
groupedHistory = {}, | groupedHistory = {}, | ||
Line 16: | Line 18: | ||
-- @param format string Page name of the format | -- @param format string Page name of the format | ||
-- @return LimitationHistory | -- @return LimitationHistory | ||
− | function LimitationHistory:new(card, format) | + | function LimitationHistory:new(card, format, debutDate) |
local lh = mw.clone(LimitationHistory) | local lh = mw.clone(LimitationHistory) | ||
lh.card = card | lh.card = card | ||
lh.format = format | lh.format = format | ||
+ | lh.debutDate = debutDate | ||
lh.history = lh:lookupHistory() | lh.history = lh:lookupHistory() | ||
lh.groupedHistory = lh:getGroupedHistory() | lh.groupedHistory = lh:getGroupedHistory() | ||
Line 126: | Line 129: | ||
thead:tag('th'):attr('scope', 'col'):wikitext('Start date') | thead:tag('th'):attr('scope', 'col'):wikitext('Start date') | ||
thead:tag('th'):attr('scope', 'col'):wikitext('End date') | thead:tag('th'):attr('scope', 'col'):wikitext('End date') | ||
+ | |||
+ | local firstListDate = self.groupedHistory[1].startDate | ||
+ | |||
+ | -- If the card was released before the first limitation status, | ||
+ | -- Add a row saying it was 'Unlimited' in that period | ||
+ | if (self.debutDate and self.debutDate < firstListDate) then | ||
+ | local tr = historyTable:tag('tr') | ||
+ | tr:tag('td'):addClass('status-unlimited'):wikitext('[[Unlimited]]') | ||
+ | -- Start when the card was released | ||
+ | tr:tag('td'):wikitext(self.debutDate) | ||
+ | -- End the day before its first limitation | ||
+ | tr:tag('td'):wikitext(addDaysToDate(firstListDate, -1)) | ||
+ | end | ||
-- Loop through each change in status | -- Loop through each change in status | ||
Line 169: | Line 185: | ||
function statusToClass(status) | function statusToClass(status) | ||
return 'status-' .. (status:gsub(' ', '-')):lower() | return 'status-' .. (status:gsub(' ', '-')):lower() | ||
+ | end | ||
+ | |||
+ | -- Add a number of days to a date | ||
+ | -- @param date string Date in the format YYYY-MM-DD | ||
+ | -- @param days number Number of days to add. Use a negative number to subtract | ||
+ | -- @return string New date in the format YYYY-MM-DD | ||
+ | function addDaysToDate(date, days) | ||
+ | local dateParts = mw.text.split(date, '%-') | ||
+ | |||
+ | return os.date('%Y-%m-%d', os.time{ | ||
+ | year = dateParts[1], | ||
+ | month = dateParts[2], | ||
+ | day = dateParts[3] + days | ||
+ | }) | ||
end | end | ||
return LimitationHistory | return LimitationHistory |
Revision as of 16:53, 10 December 2023
-- @field card string Page name of the card whose history this is for
-- @field format string The format the history is for (e.g. "Advanced Format", "OCG", "Korean")
-- @field debutDate string ISO date of when the card was first released in the format
-- @field history table List with details on each limitation list that the card was on and the status it had on each list
-- @field groupedHistory table Shorter version of `history` where consecutive lists in which the card had the same status are grouped together
-- @field currentStatus string The card's status on the list that's in effect
local LimitationHistory = {
card = nil,
format = nil,
debutDate = nil,
history = {},
groupedHistory = {},
currentStatus = nil,
}
-- Create a new instance of a LimitationHistory object
-- @param card string Page name of the card
-- @param format string Page name of the format
-- @return LimitationHistory
function LimitationHistory:new(card, format, debutDate)
local lh = mw.clone(LimitationHistory)
lh.card = card
lh.format = format
lh.debutDate = debutDate
lh.history = lh:lookupHistory()
lh.groupedHistory = lh:getGroupedHistory()
lh.currentStatus = lh:getCurrentStatus()
return lh
end
-- Look up all limitation status lists that include the card for the format
-- @return table
function LimitationHistory:lookupHistory()
local results = mw.smw.ask{
'[[List contains::' .. self.card .. ']]' ..
'[[-Has subobject.Page type::Status list]]' ..
'[[-Has subobject.Format::' .. self.format .. ']]',
'?Status# = status',
'?-Has subobject# = list',
'?-Has subobject.Start date# = startDate',
'?-Has subobject.Start date = startDateFormatted',
'?-Has subobject.End date# = endDate',
'?-Has subobject.End date = endDateFormatted',
'mainlabel = -',
'limit = 500'
} or {}
-- SMW sorting doesn't work with inverse properties. Doing it in Lua instead.
table.sort(results and results, orderHistory)
return results
end
-- Merge consecutive history items with the same status together
-- @return table
function LimitationHistory:getGroupedHistory()
local i = 0
local groupedHistory = {}
-- Can't use `for item in pairs(self.history) do`
-- because the order will get messed up.
for h = 1, #self.history do
local item = self.history[h]
-- Check if this item has a different status than the incumbent item
-- Always true for the first item.
local hasNewStatus = (i == 0 or item.status ~= groupedHistory[i].status)
if (hasNewStatus) then
-- Create a new object at the next space in the table
i = i + 1
groupedHistory[i] = mw.clone(item)
groupedHistory[i].firstList = item.list
groupedHistory[i].list = nil
else
-- Update the end date on the existing object
groupedHistory[i].endDate = item.endDate
groupedHistory[i].endDateFormatted = item.endDateFormatted
groupedHistory[i].lastList = item.list
end
end
return groupedHistory
end
function orderHistory(item1, item2)
return (item1.startDate or 'a') < (item2.startDate or 'a')
end
-- Get the current limitation status
-- @return string
-- @todo: Avoid potential issue with non-active lists that don't specify an end date
function LimitationHistory:getCurrentStatus()
local today = os.date('%Y-%m-%d')
-- Loop through the history items
-- If today falls within the start and end dates, that's the current status
for _, item in pairs(self.groupedHistory) do
local todayAfterStartDate = not item.startDate or item.startDate <= today
local todayBeforeEndDate = not item.endDate or item.endDate >= today
if (todayAfterStartDate and todayBeforeEndDate) then
-- Return the status or `nil` if it's "Unlimited"
return item.status == 'Unlimited'
and nil
or item.status
end
end
return nil
end
-- Render the history as a table
-- Table has a row for each change in status,
-- including the start and end date of the time with that status
--
-- @return string
function LimitationHistory:renderHistoryTable()
-- Return empty string if there is no history
if not self.groupedHistory or #self.groupedHistory == 0 then return '' end
-- Create a HTML table
local historyTable = mw.html.create('table'):addClass('wikitable')
-- Add heading row
local thead = historyTable:tag('tr')
thead:tag('th'):attr('scope', 'col'):wikitext('Status')
thead:tag('th'):attr('scope', 'col'):wikitext('Start date')
thead:tag('th'):attr('scope', 'col'):wikitext('End date')
local firstListDate = self.groupedHistory[1].startDate
-- If the card was released before the first limitation status,
-- Add a row saying it was 'Unlimited' in that period
if (self.debutDate and self.debutDate < firstListDate) then
local tr = historyTable:tag('tr')
tr:tag('td'):addClass('status-unlimited'):wikitext('[[Unlimited]]')
-- Start when the card was released
tr:tag('td'):wikitext(self.debutDate)
-- End the day before its first limitation
tr:tag('td'):wikitext(addDaysToDate(firstListDate, -1))
end
-- Loop through each change in status
for _, item in pairs(self.groupedHistory) do
-- Add a row for the status.
local tr = historyTable:tag('tr')
-- Add a cell for the status and add a class to style it per its status.
tr:tag('td'):addClass(statusToClass(item.status)):wikitext('[[' .. item.status .. ']]')
-- Add cells for the start and end dates.
-- Show the ISO dates in the cells (YYYY-MM-DD). Show the formatted dates on hover.
tr:tag('td'):attr('title', item.startDateFormatted):wikitext(item.startDate)
tr:tag('td'):attr('title', item.endDateFormatted):wikitext(item.endDate)
end
return tostring(historyTable)
end
-- Render a given status as a badge
-- @param status string
-- @param context string Text to display in parentheses after the status
function LimitationHistory.renderStatusBadge(status, context)
local badge = mw.html.create('div')
local statusText = '[[' .. status .. ']]'
if (context and context ~= '') then
statusText = statusText .. ' (' .. context .. ')'
end
badge
:addClass(statusToClass(status))
:css('border', '2px solid #666')
:css('border-radius', '3px')
:css('display', 'inline-block')
:css('padding', '.1em .5em')
:wikitext(statusText)
return tostring(badge)
end
function statusToClass(status)
return 'status-' .. (status:gsub(' ', '-')):lower()
end
-- Add a number of days to a date
-- @param date string Date in the format YYYY-MM-DD
-- @param days number Number of days to add. Use a negative number to subtract
-- @return string New date in the format YYYY-MM-DD
function addDaysToDate(date, days)
local dateParts = mw.text.split(date, '%-')
return os.date('%Y-%m-%d', os.time{
year = dateParts[1],
month = dateParts[2],
day = dateParts[3] + days
})
end
return LimitationHistory