Shotgun, automatic task status update

loocas | Python,Shotgun,technical | Thursday, April 28th, 2016

Get the Flash Player to see this content.

As a pipeline TD you might need to setup task status auto updates in Shotgun. It isn’t as simple as it may sound, because Shotgun, out of the box, doesn’t provide this functionality. However, it can be done with Shotgun Event Daemon and some scripting.

So, first off, a bit of background. As a pipeline guy, automation is essential. You need processes to work automatically, ideally in context and further more in a certain order. That happens all the time. Shotgun, and many other tools, provides a great backend for such automation, however, Shotgun itself doesn’t provide too much automation for its own tasks, versioning, events in general.

Luckily for us there is a large community of TDs working on such tools that can save us a ton of time and effort.

In my particular case, I needed to setup a system that’d watch for main Shot status changes and flip task statuses automatically based on what the main status was changed to. So, for example, if the producer or the director decides to pause work on an entire shot, it’d have to propagate down to the individual tasks, so that the 2D and 3D departments stopped working on the shot. The various tasks, further more, control additional tools and scripts at the studio, so, the task statuses have to be carefuly watched and changed properly in order to maintain smooth pipeline operation.

To do this, in Shotgun, you can download a Shotgun Event Daemon tool, written in Python. After you set it up (it is rather straight forward, just follow the instructions) you can start making your own tools.

First thing is to define the registerCallbacks() function. Again, just modify the attached demo plugins for the Event Daemon.

Then, more importantly, you can start programming what you want the Daemon to do and when. So, in my case, I needed to change all the tasks’ statuses when the main shot status was changed. Let’s take a look at this one example:

  • Shot’s status changes to On Hold (‘hld’)
  • All the tasks that are attached to this shot and belong to a pipeline step ‘3D’ and ‘2D’ will need to change its status to Stop Work (‘stopw’)

There are two tricky parts to this. The first one is to filter out all the tasks that belong to a particular shot. That is the easier part. The next one is to further filter the tasks that only belong to a particular pipeline step or, in this case, two different steps.

This can be done with a single Shotgun query, surprisingly :)

To query all the tasks for a particular shot, it’s simple enough:

taskFilters = [['project', 'is', {'type':'Project', 'id':PROJECT_ID}],
['entity', 'is', {'type':'Shot', 'id':SHOT_ID}]]

TASK_DATA = sg.find('Task', taskFilters, ['id', 'content', 'sg_status_list'])


TASK_DATA will simply return all the tasks associated with the shot (SHOT_ID and PROJECT_ID are required, obviously, you need to query those in advance)

All basic stuff. Then you can further specify more filters in the taskFilters object to narrow down your selection. So, to filter out only those tasks that belong to our shot and at the same time belong to a pipeline step ‘3D’, you can type this:

taskFilters = [['project', 'is', {'type':'Project', 'id':PROJECT_ID}],
['entity', 'is', {'type':'Shot', 'id':SHOT_ID}],
['step', 'is', {'type': 'Step', 'id': STEP_ID, 'name': '3D'}]]

TASK_DATA = sg.find('Task', taskFilters, ['id', 'content', 'sg_status_list'])


Again, rather simple. However, you need to know the pipline step id (STEP_ID), which you can get from querying all the tasks at once and filter that one out.

What isn’t as straight forward and simple is specifying multiple filters. In this case we want to filter out all the tasks from our particular shot that belong to either a ‘3D’ or a ‘2D’ pipeline step. There is a special syntax to do so. Here’s the syntax alone:

{ "filter_operator": "any", "filters": [ <list of conditions> ] }


So, to apply this logic to our query in order to get all the tasks from the ‘3D’ and ‘2D’ pipeline steps for our shot, we need to type:

taskFilters = [['project', 'is', {'type':'Project', 'id':PROJECT_ID}],
['entity', 'is', {'type':'Shot', 'id':SHOT_ID}],
{'filter_operator': 'any', 'filters':
[['step', 'is', {'type': 'Step', 'id': 3D_STEP_ID, 'name': '3D'}],
['step', 'is', {'type': 'Step', 'id': 2D_STEP_ID, 'name': '2D'}]]}]
	
TASK_DATA = sg.find('Task', taskFilters, ['id', 'content', 'sg_status_list'])


This is probably the hardest part to figure out for someone who just started with the Shotgun Python API or anyone who isn’t that used to the Shotgun’s API syntax.

Once you have this filter, you can simply define a function that will catch the events coming from Shotgun and based on the data you receive modify the tasks’ statuses. Here’s a code snippet from my script that does what I described above:

def flipAppropriateTasks(sg, logger, event, args):
	#First, let's change all tasks under a shot if the shot's status changes
	
	# If the shot status changes to ANIMATIC, change all tasks' status to ON HOLD
	if event['event_type'] == 'Shotgun_Shot_Change'
	and 'new_value' in event['meta']
	and event['meta']['new_value'] == 'hld':
		PROJECT_ID = event['project']['id']
		SHOT_ID = event['entity']['id']
	
		taskFilters = [['project', 'is', {'type':'Project', 'id':PROJECT_ID}],
		['entity', 'is', {'type':'Shot', 'id':SHOT_ID}]]
	
		TASK_DATA = sg.find('Task', taskFilters,
		['id', 'content', 'sg_status_list'])
		
		for task in TASK_DATA:
			updateData = {'sg_status_list': 'stopw'}
			
			try:
				sg.update('Task', task['id'], updateData)
			except:
				logger.warning('Something went wrong
				updating task {0} for shot {1}'.format(
				str(task['content']), str(entity['entity']['name'])))
		
			logger.info('Updated task {0} for shot {1}'.format(
			str(task['content']), str(event['entity']['name'])))


Sorry for the screwed up indentation, this space isn’t really suited for longer code, but I think you get the point. This function will need to be registered with the callback function and then it’ll perform the necessary filtering of the event data and the necessary changes in the tasks’ statuses.

One quick note about this plugin, it was set up to only catch changes in shot statuses in the registerCallbacks() function:

eventFilter = {'Shotgun_Shot_Change': ['sg_status_list']}

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress | Theme by Roy Tanck