Module:Deployment schedule

Revision as of 10:36, 16 October 2022 by imported>Pemaksa (fix)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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 &#44;
    
    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, ",", "&#44;" )
    what = string.gsub( what, ",", "&#44;" )
    
    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,&nbsp;%B&nbsp;%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&ndash;%s&nbsp;UTC</span> <br/> <span class="deploycal-time-sf">%s %s&ndash;%s&nbsp;%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