-
Notifications
You must be signed in to change notification settings - Fork 204
Example: Handling Shotgun ActionMenuItem calls
kporangehat edited this page Sep 11, 2012
·
8 revisions
This is an example ActionMenu Python class to handle the GET request sent from an ActionMenuItem. It doesn't manage dispatching custom protocols but rather takes the arguments from any GET data and parses them into the easily accessible and correctly typed instance variables for your Python scripts.
Available as a Gist at https://gist.github.com/3253287
#!/usr/bin/env python
# encoding: utf-8
# ---------------------------------------------------------------------------------------------
# Description
# ---------------------------------------------------------------------------------------------
"""
The values sent by the Action Menu Item are in the form of a GET request that is similar to the
format: myCoolProtocol://doSomethingCool?user_id=24&user_login=shotgun&title=All%20Versions&...
In a more human-readable state that would translate to something like this:
{
'project_name': 'Demo Project',
'user_id': '24',
'title': 'All Versions',
'user_login': 'shotgun',
'sort_column': 'created_at',
'entity_type': 'Version',
'cols': 'created_at',
'ids': '5,2',
'selected_ids': '2,5',
'sort_direction': 'desc',
'project_id': '4',
'session_uuid': 'd8592bd6-fc41-11e1-b2c5-000c297a5f50',
'column_display_names':
[
'Version Name',
'Thumbnail',
'Link',
'Artist',
'Description',
'Status',
'Path to frames',
'QT',
'Date Created'
]
}
This simple class parses the url into easy to access types variables from the parameters,
action, and protocol sections of the url. This example url
myCoolProtocol://doSomethingCool?user_id=123&user_login=miled&title=All%20Versions&...
would be parsed like this:
(string) protocol: myCoolProtocol
(string) action: doSomethingCool
(dict) params: user_id=123&user_login=miled&title=All%20Versions&...
The parameters variable will be returned as a dictionary of string key/value pairs. Here's
how to instantiate:
sa = ShotgunAction(sys.argv[1]) # sys.argv[1]
sa.params['user_login'] # returns 'miled'
sa.params['user_id'] # returns 123
sa.protocol # returns 'myCoolProtocol'
"""
# ---------------------------------------------------------------------------------------------
# Imports
# ---------------------------------------------------------------------------------------------
import sys, os
import urllib
import logging as logger
from pprint import pprint
# ---------------------------------------------------------------------------------------------
# Variables
# ---------------------------------------------------------------------------------------------
# location to write logfile for this script
# logging is a bit of overkill for this class, but can still be useful.
logfile = os.path.dirname(sys.argv[0])+"/shotgun_action.log"
# ----------------------------------------------
# Generic ShotgunAction Exception Class
# ----------------------------------------------
class ShotgunActionException(Exception):
pass
# ----------------------------------------------
# ShotgunAction Class to manage ActionMenuItem call
# ----------------------------------------------
class ShotgunAction():
def __init__(self, url):
self.logger = self._init_log(logfile)
self.url = url
self.protocol, self.action, self.params = self._parse_url()
# entity type that the page was displaying
self.entity_type = self.params['entity_type']
# Project info (if the ActionMenuItem was launched from a page not belonging
# to a Project (Global Page, My Page, etc.), this will be blank
if 'project_id' in self.params:
self.project = { 'id':int(self.params['project_id']), 'name':self.params['project_name'] }
else:
self.project = None
# Internal column names currently displayed on the page
self.columns = self.params['cols']
# Human readable names of the columns currently displayed on the page
self.column_display_names = self.params['column_display_names']
# All ids of the entities returned by the query (not just those visible on the page)
self.ids = []
if len(self.params['ids']) > 0:
ids = self.params['ids'].split(',')
self.ids = [int(id) for id in ids]
# All ids of the entities returned by the query in filter format ready
# to use in a find() query
self.ids_filter = self._convert_ids_to_filter(self.ids)
# ids of entities that were currently selected
self.selected_ids = []
if len(self.params['selected_ids']) > 0:
sids = self.params['selected_ids'].split(',')
self.selected_ids = [int(id) for id in sids]
# All selected ids of the entities returned by the query in filter format ready
# to use in a find() query
self.selected_ids_filter = self._convert_ids_to_filter(self.selected_ids)
# sort values for the page
# (we don't allow no sort anymore, but not sure if there's legacy here)
if 'sort_column' in self.params:
self.sort = { 'column':self.params['sort_column'], 'direction':self.params['sort_direction'] }
else:
self.sort = None
# title of the page
self.title = self.params['title']
# user info who launched the ActionMenuItem
self.user = { 'id':self.params['user_id'], 'login':self.params['user_login']}
# session_uuid
self.session_uuid = self.params['session_uuid']
# ----------------------------------------------
# Set up logging
# ----------------------------------------------
def _init_log(self, filename="shotgun_action.log"):
try:
logger.basicConfig(level=logger.DEBUG,
format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%Y-%b-%d %H:%M:%S',
filename=filename,
filemode='w+')
except IOError, e:
raise ShotgunActionException ("Unable to open logfile for writing: %s" % e)
logger.info("ShotgunAction logging started.")
return logger
# ----------------------------------------------
# Parse ActionMenuItem call into protocol, action and params
# ----------------------------------------------
def _parse_url(self):
logger.info("Parsing full url received: %s" % self.url)
# get the protocol used
protocol, path = self.url.split(":", 1)
logger.info("protocol: %s" % protocol)
# extract the action
action, params = path.split("?", 1)
action = action.strip("/")
logger.info("action: %s" % action)
# extract the parameters
# 'column_display_names' and 'cols' occurs once for each column displayed so we store it as a list
params = params.split("&")
p = {'column_display_names':[], 'cols':[]}
for arg in params:
key, value = map(urllib.unquote, arg.split("=", 1))
if key == 'column_display_names' or key == 'cols' :
p[key].append(value)
else:
p[key] = value
params = p
logger.info("params: %s" % params)
return (protocol, action, params)
# ----------------------------------------------
# Convert IDs to filter format to us in find() queries
# ----------------------------------------------
def _convert_ids_to_filter(self, ids):
filter = []
for id in ids:
filter.append(['id','is',id])
logger.debug("parsed ids into: %s" % filter)
return filter
# ----------------------------------------------
# Main Block
# ----------------------------------------------
if __name__ == "__main__":
try:
sa = ShotgunAction(sys.argv[1])
logger.info("ShotgunAction: Firing... %s" % (sys.argv[1]) )
except IndexError, e:
raise ShotgunActionException("Missing GET arguments")
logger.info("ShotgunAction process finished.")