Jump to content

Module:Gerrit api test

From Wikitech

Documentation for this module may be created at Module:Gerrit api test/doc (Test casesTest results)

local p = {}

local timezones = {
	-- Add timezone offsets here!
	Z = 0,
	UTC = 0,
	PST = -8,
	PDT = -7,
	SF = 'America/Los_Angeles',
	DE = 'Europe/Berlin',
}
local timezones2 = {
	SF = 'America/Los_Angeles',
	DE = 'Europe/Berlin',
}

function getSanFranciscoTimezone( time )
	-- mw.getContentLanguage():formatDate() will handle input with tzdata
	-- timezones, outputting the corresponding time in UTC. So we pass it input
	-- that is manually offset for PST and PDT and see which one matches UTC.
	local utc = os.date( '!%FT%TZ', time )
	local pst = mw.getContentLanguage():formatDate( 'Y-m-d\\TH:i:s\\Z', os.date( '!%FT%T America/Los_Angeles', time + timezones.PST * 3600 ) )
	local pdt = mw.getContentLanguage():formatDate( 'Y-m-d\\TH:i:s\\Z', os.date( '!%FT%T America/Los_Angeles', time + timezones.PDT * 3600 ) )

	if utc == pst then
		return 'PST'
	elseif utc == pdt then
		return 'PDT'
	else
		mw.logObject( { UTC = utc, PST = pst, PDT = pdt }, 'Cannot determine SF time' )
		return 'PST'
	end
end

function trim( str )
	-- Trims a string because wikitech 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]+)$' )
		tz = timezones[tz]
		
		if tz == nil then
			utc_time = 0
			what = "Unknown timezone in 'when' string! -- add it to the module :)"
		elseif type( tz ) == 'string' then
			local when2 = string.format( '%d-%d-%dT%d:%d:00 %s',
				year or 1980,
				month or 1,
				day or 1,
				hour or 1,
				minute or 1,
				tz
			)
			utc_time = mw.getContentLanguage():formatDate( 'U', when2 )
		else
			ts = os.time({
				year = year or 1980,
				month = month or 1,
				day = day or 1,
				hour = hour or 1,
				min = minute or 1
			})
			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.makeGerritSearchLink(utc)
	local dow = os.date('%u', utc)
	local hour = os.date('%H', utc)
	local swatWindow
	if hour == '23' then
		swatWindow = 'evening'
	elseif hour == '18' and dow ~= '3' then
		swatWindow = 'morning'
	elseif hour == '16' and dow == '3' then
		swatWindow = 'morning'
	elseif hour == '11' then
		swatWindow = 'eu'
	else
		swatWindow = hour
	end

	local swatTag = string.format('swat-%s-%s', os.date("%Y-%m-%d", utc), swatWindow)
	return string.format("'''Gerrit hashtag''': [https://gerrit.wikimedia.org/r/#/q/hashtag:%s %s]", swatTag, swatTag)
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' | \n==== " .. os.date( "!%A,&nbsp;%B&nbsp;%d", utc ) .. " ====" )
			cutcday = os.date( '%x', utc )
		end
		
		sftz = getSanFranciscoTimezone( utc )
		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

        -- if it's swat, but not puppet swat
		if string.find(window, "Puppet SWAT") == nil and string.find( window, "SWAT" ) ~= nil then
			what = string.format("%s\n%s", what, p.makeGerritSearchLink(utc))
		end
		
		table.insert( retval, string.format( [=[ |- id="deploycal-item-%s" class="deploycal-item"
				| <span class="deploycal-time-utc">%s&ndash;%s&nbsp;UTC [[#deploycal-item-%s|#]]</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>",
			os.date( "!%Y%m%dT%H%M", utc ),
			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

-- Re-usable version for the timestamp anchor logic in 'row' and 'formatTable'.
-- Used on [[User:Krinkle/Deployments sandbox]].
function p.anchor( frame )
	local start = frame.args['start']
	if start == nil or start == '' then
		return ""
	end
	year, month, day, hour, minute, tz_raw = string.match( start, '^(%d+)-(%d+)-(%d+) (%d+):(%d+) ([A-Z]+)$' )
	tz = timezones2[tz_raw] or tz_raw
	local formatted = string.format( '%d-%d-%dT%d:%d:00 %s',
		year or 1980,
		month or 0,
		day or 0,
		hour or 0,
		minute or 42,
		tz
	)
	utc = tonumber( mw.getContentLanguage():formatDate( 'U', formatted ) )
	return string.format( 'id="deploycal-item-%s"', os.date( "!%Y%m%dT%H%M", utc ) )
end

-- Re-usable version for the date fomatting logic in 'row' and 'formatTable'.
-- Used on [[User:Krinkle/Deployments sandbox]].
function p.when( frame )
	local start = frame.args['start']
	local seconds = ( tonumber( frame.args['hours'] ) or 2 ) * 60 * 60

	if start == nil or start == '' then
		return "No time given"
	end

	year, month, day, hour, minute, tz_raw = string.match( start, '^(%d+)-(%d+)-(%d+) (%d+):(%d+) ([A-Z]+)$' )
	tz = timezones2[tz_raw] or tz_raw
	local formatted = string.format( '%d-%d-%dT%d:%d:00 %s',
		year or 1980,
		month or 0,
		day or 0,
		hour or 0,
		minute or 42,
		tz
	)
	utc = tonumber( mw.getContentLanguage():formatDate( 'U', formatted ) )

	sftz = getSanFranciscoTimezone( utc )
	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

	return string.format( '<span class="deploycal-time-utc">%s&ndash;%s&nbsp;UTC [[#deploycal-item-%s|#]]</span>' ..
		' <br/> ' ..
		'<span class="deploycal-time-sf">%s %s&ndash;%s&nbsp;%s</span>',

		'<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 + seconds ) .. '">' ..
			os.date( "!%H:%M", utc + seconds ) .. "</time>",

		os.date( "!%Y%m%dT%H%M", utc ),

		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 + seconds ) .. '">' ..
			os.date( "!%H:%M", sflocal + seconds ) .. "</time>",
			
		sftz
	)
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