주요 기능
- GET vs POST:
- ActionMenuItem이 HTTP/HTTPS로 데이터를 전송하는 경우 POST 방식 사용.
- GET 방식은 사용자 지정 프로토콜(custom protocol)에서 사용되며, 브라우저에서 URL 길이 제한에 유의해야 합니다.
- 클래스 개요:
- ShotgunAction 클래스는 URL을 프로토콜, 액션, 파라미터로 분리하여 변수에 저장.
- GET 요청의 파라미터를 Python 딕셔너리로 파싱.
주요 코드 설명
# ---------------------------------------------------------------------------------------------
# Imports
# ---------------------------------------------------------------------------------------------
import sys, os
import six
import logging as logger
# ---------------------------------------------------------------------------------------------
# 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 as 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(six.moves.urllib.parse.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 as e:
raise ShotgunActionException("Missing GET arguments")
logger.info("ShotgunAction process finished.")
URL 파싱
- URL 구조:
- 프로토콜: myCoolProtocol
- 액션: doSomethingCool
- 파라미터: user_id=123, user_login=miled, title=All Versions
- myCoolProtocol://doSomethingCool?user_id=123&user_login=miled&title=All%20Versions
- 파싱된 결과:
- protocol: myCoolProtocol
- action: doSomethingCool
- params: { 'user_id': '123', 'user_login': 'miled', 'title': 'All Versions' }
파라미터 접근
- 사용자 로그인:
sa.params['user_login'] # 'miled'
- 사용자 ID:
sa.params['user_id'] # 123
ShotgunAction 클래스 주요 속성
초기화:
- URL 파싱 및 변수 설정:
sa = ShotgunAction(url)
속성 설명:
- protocol: URL의 프로토콜 (예: myCoolProtocol).
- action: 수행할 액션 이름.
- params: URL에서 파싱한 파라미터 딕셔너리.
- project: 프로젝트 ID 및 이름.
- columns: 현재 페이지의 내부 컬럼 이름.
- selected_ids: 선택된 엔터티의 ID 목록.
- sort: 정렬 기준 (열과 방향).
- user: 호출한 사용자 정보.
로깅(logging)
- 로그 파일 경로 설정:
logfile = os.path.dirname(sys.argv[0]) + "/shotgun_action.log"
- 로깅 예제:
logger.info("ShotgunAction: Firing...")
필터 변환
- ID 목록을 API의 find() 쿼리에 사용할 수 있도록 변환:
def _convert_ids_to_filter(self, ids):
filter = []
for id in ids:
filter.append(["id", "is", id])
return filter
사용 예제
메인 블록:
- URL을 인자로 받아 처리:
if __name__ == "__main__":
sa = ShotgunAction(sys.argv[1]) # sys.argv[1]에 URL 전달
액션 메뉴 아이템을 호출 했을 때 주는 url을 이용하는 방법을 알려주는 것 같음 실제로 사용해야 이해가 더 잘 될 것 같다.
https://developers.shotgridsoftware.com/python-api/cookbook/examples/ami_handler.html
Handling Action Menu Item Calls — python-api v3.7.0 documentation
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 typ
developers.shotgridsoftware.com
'Flow Production Tracking > Python api' 카테고리의 다른 글
Python API Best Practices (0) | 2024.12.29 |
---|---|
Python API Overview (0) | 2024.12.29 |
API Usage Tips (1) | 2024.12.25 |
API Reference(2) (0) | 2024.12.25 |
API Reference(1) (1) | 2024.12.25 |