Module:Deployment schedule
Documentation for this module may be created at Module:Deployment schedule/doc
local p = {}
local timezones = {
-- Add timezone offsets here!
Z = 0,
UTC = 0,
PST = -8,
PDT = -7,
}
function us_in_dst( time )
-- Determine if the United States is in Daylight Savings Time (as of 2022)
month = tonumber( os.date( "!%m", time ) )
day = tonumber( os.date( "!%d", time ) )
if ( month == 3 and day >= 10 ) then
return true
elseif ( month == 11 and day <= 3 ) then
return true
elseif ( month > 3 and month < 11 ) then
return true
else
return false
end
end
function trim( str )
-- Trims a string because Wikipedia apparently doesn't have this library...
str = string.gsub( str, "%s+$", "" )
str = string.gsub( str, "^%s+", "" )
return str
end
function p.row( frame )
-- Produces a raw row to be reconsumed by this script for sorting
-- Expects:
--- when, string, like YYYY-MM-DD HH:mm XXX where XXX is a valid timezone listed in timezones
--- length, number, of hours the deploy will take
--- window, string, what deployment window this is (e.g. SWAT, parsoid, MediaWiki)
--- who, string, who is performing the deploy
--- what, string, what will be deployed
--
-- Outputs: UTC Timestamp,Length,Who,What
--- commas in strings are replaced with ,
local when = frame.args['when']
local length = ( tonumber( frame.args['length'] ) or 2 ) * 60 * 60
local window = frame.args['window'] or "N/A"
local who = frame.args['who'] or "Anonymous Cowards (must provide 'who=')"
local what = frame.args['what'] or "Nothing? Then why is this an entry!? (must provide 'what=')"
who = string.gsub( who, ",", "," )
what = string.gsub( what, ",", "," )
if when == nil then
utc_time = 0
what = "No time given (must provide 'when' and 'tz', e.g.: 'when=YYYY-MM-DD HH:mm XXX'"
else
year, month, day, hour, minute, tz = string.match( when, '^(%d+)-(%d+)-(%d+) (%d+):(%d+) ([A-Z]+)$' )
ts = os.time({
year = year or 1980,
month = month or 1,
day = day or 1,
hour = hour or 1,
min = minute or 1
})
tz = timezones[tz]
if tz == nil then
utc_time = 0
what = "Unknown timezone in 'when' string! -- add it to the module :)"
else
utc_time = ts - ( tz * 60 * 60 )
end
end
return string.format( "%s,%s,%s,%s,%s", utc_time, length, window, who, what )
end
function p.formatTable( frame )
-- Formats a bunch of rows from p.row()
local rows = {}
local row
local count = 1
local retval = {}
local sflocal
local sfdatestr
local showdate = not ( frame.args['hidedate'] == 'true' )
local tzoffset = 0
local cutcday
local sftz = "No data"
-- Load and sort the arguments
row = frame.args[count]
while not ( row == nil ) do
utc, length, window, who, what = string.match( row, '^(.*),(.*),(.*),(.*),(.*)$' )
utc = tonumber( trim ( utc ) )
length = tonumber( trim( length ) )
who = trim( who )
what = trim( what )
table.insert( rows, { utc=utc, length=length, window=window, who=who, what=what } )
count = count + 1
row = frame.args[count]
end
table.sort( rows, function( a, b ) return a.utc < b.utc end )
-- For each entry create the final row
for count = 1, #rows do
local datestr
utc = rows[count].utc
length = rows[count].length
window = rows[count].window
who = rows[count].who
what = rows[count].what
if ( cutcday ~= os.date( '%x', utc ) ) then
-- Create a new date header in the table
table.insert( retval, "|-\n| colspan='4' class='deploycal-dayrow' | " .. os.date( "!%A, %B %d", utc ) )
cutcday = os.date( '%x', utc )
end
sftz = us_in_dst( utc ) and 'PDT' or 'PST'
tzoffset = timezones[sftz]
sflocal = utc + ( tzoffset * 60 * 60 )
-- Determine if the SF day is different from the UTC day
if os.date( "!%A", utc ) ~= os.date( "!%A", sflocal ) then
sfdatestr = os.date( "!<small>(%a)</small> ", sflocal )
else
sfdatestr = ""
end
table.insert( retval, string.format( [[ |- id="deploycal-item-%s" class="deploycal-item"
| <span class="deploycal-time-utc">%s–%s UTC</span> <br/> <span class="deploycal-time-sf">%s %s–%s %s</span>
| <span class="deploycal-window">%s</span>
| %s
| %s
]],
os.date( "!%Y%m%dT%H%M", utc ),
'<time class="deploycal-starttime" datetime="' .. os.date( "!%Y-%m-%dT%H:%M+00:00", utc ) .. '">' ..
os.date( "!%H:%M", utc ) .. '</time>',
'<time class="deploycal-endtime" datetime="' .. os.date( "!%Y-%m-%dT%H:%M+00:00", utc + length ) .. '">' ..
os.date( "!%H:%M", utc + length ) .. "</time>",
sfdatestr,
'<time class="deploycal-starttime" datetime="' .. os.date( "!%Y-%m-%dT%H:%M+" .. tzoffset .. ":00", sflocal ) .. '">' ..
os.date( "!%H:%M", sflocal ) .. "</time>",
'<time class="deploycal-endtime" datetime="' .. os.date( "!%Y-%m-%dT%H:%M+" .. tzoffset .. ":00", sflocal + length ) .. '">' ..
os.date( "!%H:%M", sflocal + length ) .. "</time>",
sftz,
window,
who,
what
))
end
return string.format( [[
{| class='wikitable deploycal'
!Time
!Component
!Deployer
!Changes
]], sftz ) .. table.concat( retval, "\n" ) .. "\n|}"
end
function p.dateOf( frame )
local requestedday = frame.args[1] or 'Sun'
local tdelta = 0
local weekstart = 0
local adddays = 0
day, hour, minute, sec = string.match( os.date('!%w %H %m %S'), '^(%d+) (%d+) (%d+) (%d+)$' )
tdelta = ( tonumber( day ) * 24 * 60 * 60 ) + ( tonumber( hour) * 60 * 60 ) + ( tonumber( minute ) * 60 ) + tonumber( sec )
weekstart = os.time() - tdelta
if ( requestedday == 'Mon' ) then
adddays = 1
elseif ( requestedday == 'Tue' ) then
adddays = 2
elseif ( requestedday == 'Wed' ) then
adddays = 3
elseif ( requestedday == 'Thu' ) then
adddays = 4
elseif ( requestedday == 'Fri' ) then
adddays = 5
end
return os.date( '%Y-%m-%d', weekstart + ( adddays * 24*60*60 ) )
end
function p.header( frame )
-- In my young and dumb days, this created a header for formatTable.
-- We still need it because the archive page calls it (though it no longer needs to)
end
function p.footer( frame )
-- In my young and dumb days, this created a footer for formatTable.
-- We still need it because the archive page calls it (though it no longer needs to)
end
return p