From e40bc0b267d4312f6d238c9b0937a7776f56f766 Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Mon, 27 Oct 2025 11:52:44 -0700 Subject: [PATCH 1/5] Auto update done by CoPilot --- docs/authentication.rst | 16 +- docs/cookbook/attachments.rst | 190 ++++++++++-------- docs/cookbook/examples/ami_handler.rst | 60 +++--- docs/cookbook/examples/basic_create_shot.rst | 56 +++--- .../basic_create_version_link_shot.rst | 42 ++-- docs/cookbook/examples/basic_delete_shot.rst | 8 +- docs/cookbook/examples/basic_find_shot.rst | 22 +- docs/cookbook/examples/basic_sg_instance.rst | 8 +- docs/cookbook/examples/basic_update_shot.rst | 42 ++-- docs/cookbook/usage_tips.rst | 181 ++++++++++------- docs/index.rst | 52 +++-- 11 files changed, 382 insertions(+), 295 deletions(-) diff --git a/docs/authentication.rst b/docs/authentication.rst index ea049fbd0..bcd74a222 100644 --- a/docs/authentication.rst +++ b/docs/authentication.rst @@ -9,9 +9,11 @@ User-based Authentication ************************* When authenticating as a user, you provide your normal login and password when instantiating your :class:`shotgun_api3.Shotgun` object. The actions performed by this instance will be limited to your permission level just as they are in the web application. :: - sg = shotgun_api3.Shotgun("https://my-site.shotgrid.autodesk.com", - login="rhendriks", - password="c0mPre$Hi0n") + sg = shotgun_api3.Shotgun( + "https://my-site.shotgrid.autodesk.com", + login="rhendriks", + password="c0mPre$Hi0n", + ) *************************** @@ -19,9 +21,11 @@ Script-based Authentication *************************** In order to authenticate as a script, your script must be :ref:`registered with Flow Production Tracking and have a valid API key `. When creating your :class:`shotgun_api3.Shotgun` object, provide the ``script_name`` and ``api_key``.:: - sg = shotgun_api3.Shotgun("https://my-site.shotgrid.autodesk.com", - script_name="compress", - api_key="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") + sg = shotgun_api3.Shotgun( + "https://my-site.shotgrid.autodesk.com", + script_name="compress", + api_key="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + ) .. note:: When using script-based authentication, we **strongly** recommend you register each script separately with Flow Production Tracking and have individual API keys for each. This allows you to track down each of your scripts and the actions they are performing much more accurately in the event logs. diff --git a/docs/cookbook/attachments.rst b/docs/cookbook/attachments.rst index de992431d..1388deac9 100644 --- a/docs/cookbook/attachments.rst +++ b/docs/cookbook/attachments.rst @@ -100,10 +100,12 @@ will vary. :: - {'content_type': 'image/jpeg', - 'link_type': 'upload', - 'name': 'western1FULL.jpg', - 'url': 'https://my-site.shotgrid.autodesk.com/file_serve/attachment/538'} + { + "content_type": "image/jpeg", + "link_type": "upload", + "name": "western1FULL.jpg", + "url": "https://my-site.shotgrid.autodesk.com/file_serve/attachment/538", + } - **Web links** Designated by ``link_type: 'web'``, this is represents a url link. Examples include an @@ -111,10 +113,12 @@ will vary. like ``rvlink://`` or ``cinesync://`` :: - {'content_type': None, - 'link_type': 'web', - 'name': 'Join GUN12158', - 'url': 'cinesync://session/GUN12158'} + { + "content_type": None, + "link_type": "web", + "name": "Join GUN12158", + "url": "cinesync://session/GUN12158", + } - **Local Files** Designated by ``link_type: 'local'``, this is represents a local file link. Additional keys @@ -124,17 +128,21 @@ will vary. :: - { 'content_type': 'video/quicktime', - 'link_type': 'local', - 'name': 'my_test_movie.mov', - 'local_path': '/Users/kp/Movies/testing/test_movie_002.mov' - 'local_path_linux': '/home/users/macusers/kp/Movies/testing/test_movie_002.mov' - 'local_path_mac': '/Users/kp/Movies/testing/test_movie_002.mov' - 'local_path_windows': 'M:\\macusers\kp\Movies\testing\test_movie_002.mov' - 'local_storage': {'id': 1, - 'name': 'Dailies Directories', - 'type': 'LocalStorage'}, - 'url': 'file:///Users/kp/Movies/testing/test_movie_002.mov'} + { + "content_type": "video/quicktime", + "link_type": "local", + "name": "my_test_movie.mov", + "local_path": "/Users/kp/Movies/testing/test_movie_002.mov", + "local_path_linux": "/home/users/macusers/kp/Movies/testing/test_movie_002.mov", + "local_path_mac": "/Users/kp/Movies/testing/test_movie_002.mov", + "local_path_windows": "M:\\macusers\kp\Movies\testing\test_movie_002.mov", + "local_storage": { + "id": 1, + "name": "Dailies Directories", + "type": "LocalStorage", + }, + "url": "file:///Users/kp/Movies/testing/test_movie_002.mov", + } ******************** @@ -146,14 +154,14 @@ Web Links :: myurl = { - 'url': 'http://apple.com/itunes', - 'name': 'Apple: iTunes' + "url": "http://apple.com/itunes", + "name": "Apple: iTunes", } data = { - 'this_file': myurl, - 'project': {'type':'Project','id':64} + "this_file": myurl, + "project": {"type": "Project", "id": 64}, } - result = sg.create('Attachment', data) + result = sg.create("Attachment", data) Uploads @@ -245,24 +253,30 @@ Reading Local File Fields :: - fields = ['sg_uploaded_movie'] - result = sg.find('Version', [['id', 'is', 123]], fields) + fields = ["sg_uploaded_movie"] + result = sg.find("Version", [["id", "is", 123]], fields) Returns:: - {'id':123, - 'sg_uploaded_movie': { 'content_type': None, - 'link_type': 'local', - 'name': 'my_test_movie.mov', - 'local_path': '/Users/kp/Movies/testing/test_movie_001_.mov' - 'local_path_linux': '/home/users/macusers/kp/Movies/testing/test_movie_001_.mov' - 'local_path_mac': '/Users/kp/Movies/testing/test_movie_001_.mov' - 'local_path_windows': 'M:\\macusers\kp\Movies\testing\test_movie_001_.mov' - 'local_storage': {'id': 1, - 'name': 'Dailies Directories', - 'type': 'LocalStorage'}, - 'url': 'file:///Users/kp/Movies/testing/test_movie_001_.mov'}, - 'type': 'Version'} + { + "id": 123, + "sg_uploaded_movie": { + "content_type": None, + "link_type": "local", + "name": "my_test_movie.mov", + "local_path": "/Users/kp/Movies/testing/test_movie_001_.mov", + "local_path_linux": "/home/users/macusers/kp/Movies/testing/test_movie_001_.mov", + "local_path_mac": "/Users/kp/Movies/testing/test_movie_001_.mov", + "local_path_windows": "M:\\macusers\kp\Movies\testing\test_movie_001_.mov", + "local_storage": { + "id": 1, + "name": "Dailies Directories", + "type": "LocalStorage", + }, + "url": "file:///Users/kp/Movies/testing/test_movie_001_.mov", + }, + "type": "Version", + } .. note:: When viewing results that include file/link fields with local file link values, all of the @@ -309,35 +323,36 @@ Example 1: Using ``local_path`` :: result = sg.update( - 'Version', + "Version", 123, { - 'sg_uploaded_movie': { - 'local_path': '/Users/kp/Movies/testing/test_movie_002.mov', - 'name': 'Better Movie', + "sg_uploaded_movie": { + "local_path": "/Users/kp/Movies/testing/test_movie_002.mov", + "name": "Better Movie", } - ) + }, + ) Returns:: { - 'id':123, - 'sg_uploaded_movie': { - 'content_type': 'video/quicktime', - 'link_type': 'local', - 'name': 'my_test_movie.mov', - 'local_path': '/Users/kp/Movies/testing/test_movie_002.mov' - 'local_path_linux': '/home/users/macusers/kp/Movies/testing/test_movie_002.mov' - 'local_path_mac': '/Users/kp/Movies/testing/test_movie_002.mov' - 'local_path_windows': 'M:\\macusers\kp\Movies\testing\test_movie_002.mov' - 'local_storage': { - 'id': 1, - 'name': 'Dailies Directories', - 'type': 'LocalStorage' + "id": 123, + "sg_uploaded_movie": { + "content_type": "video/quicktime", + "link_type": "local", + "name": "my_test_movie.mov", + "local_path": "/Users/kp/Movies/testing/test_movie_002.mov", + "local_path_linux": "/home/users/macusers/kp/Movies/testing/test_movie_002.mov", + "local_path_mac": "/Users/kp/Movies/testing/test_movie_002.mov", + "local_path_windows": "M:\\macusers\kp\Movies\testing\test_movie_002.mov", + "local_storage": { + "id": 1, + "name": "Dailies Directories", + "type": "LocalStorage", }, - 'url': 'file:///Users/kp/Movies/testing/test_movie_002.mov' + "url": "file:///Users/kp/Movies/testing/test_movie_002.mov", }, - 'type': 'Version', + "type": "Version", } The ``content_type`` was assigned a best-guess value based on the file extension. Flow Production Tracking selected @@ -350,38 +365,39 @@ Example 2: Using ``relative_path`` :: result = sg.update( - 'Version', + "Version", 123, { - 'sg_uploaded_movie': { - 'local_storage': { - 'type': 'LocalStorage', - 'name': 'Dailies Directories', + "sg_uploaded_movie": { + "local_storage": { + "type": "LocalStorage", + "name": "Dailies Directories", }, - 'relative_path': 'testing/test_movie_002.mov', + "relative_path": "testing/test_movie_002.mov", } - ) + }, + ) Returns:: { - 'id':123, - 'sg_uploaded_movie': { - 'content_type': 'video/quicktime', - 'link_type': 'local', - 'name': 'my_test_movie.mov', - 'local_path': '/Users/kp/Movies/testing/test_movie_002.mov', - 'local_path_linux': '/home/users/macusers/kp/Movies/testing/test_movie_002.mov', - 'local_path_mac': '/Users/kp/Movies/testing/test_movie_002.mov', - 'local_path_windows': 'M:\\macusers\kp\Movies\testing\test_movie_002.mov', - 'local_storage': { - 'id': 1, - 'name': 'Dailies Directories', - 'type': 'LocalStorage' + "id": 123, + "sg_uploaded_movie": { + "content_type": "video/quicktime", + "link_type": "local", + "name": "my_test_movie.mov", + "local_path": "/Users/kp/Movies/testing/test_movie_002.mov", + "local_path_linux": "/home/users/macusers/kp/Movies/testing/test_movie_002.mov", + "local_path_mac": "/Users/kp/Movies/testing/test_movie_002.mov", + "local_path_windows": "M:\\macusers\kp\Movies\testing\test_movie_002.mov", + "local_storage": { + "id": 1, + "name": "Dailies Directories", + "type": "LocalStorage", }, - 'url': 'file:///Users/kp/Movies/testing/test_movie_002.mov' + "url": "file:///Users/kp/Movies/testing/test_movie_002.mov", }, - 'type': 'Version', + "type": "Version", } @@ -390,11 +406,13 @@ Un-setting local file field values Removing a a local file field value is simple. Just set the value to ``None``:: - data = {'sg_uploaded_movie': None} - result = sg.update('Version', 123, data) + data = {"sg_uploaded_movie": None} + result = sg.update("Version", 123, data) Returns:: - {'id':123, - 'sg_uploaded_movie': None, - 'type': 'Version'}] + { + "id": 123, + "sg_uploaded_movie": None, + "type": "Version", + } diff --git a/docs/cookbook/examples/ami_handler.rst b/docs/cookbook/examples/ami_handler.rst index aee16f356..9642a88d4 100644 --- a/docs/cookbook/examples/ami_handler.rst +++ b/docs/cookbook/examples/ami_handler.rst @@ -45,30 +45,29 @@ via ``POST``. If you're using a custom protocol the data is sent via ``GET``. 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' - ] + "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, @@ -83,18 +82,19 @@ via ``POST``. If you're using a custom protocol the data is sent via ``GET``. 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 = ShotgunAction(sys.argv[1]) # sys.argv[1] - sa.params['user_login'] # returns 'miled' - sa.params['user_id'] # returns 123 - sa.protocol # returns 'myCoolProtocol' + sa.params["user_login"] # returns "miled" + sa.params["user_id"] # returns 123 + sa.protocol # returns "myCoolProtocol" """ # --------------------------------------------------------------------------------------------- # Imports # --------------------------------------------------------------------------------------------- - import sys, os + import sys + import os import logging as logger # --------------------------------------------------------------------------------------------- @@ -213,7 +213,7 @@ via ``POST``. If you're using a custom protocol the data is sent via ``GET``. 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 + # "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: diff --git a/docs/cookbook/examples/basic_create_shot.rst b/docs/cookbook/examples/basic_create_shot.rst index f7d412f89..c646f4dd4 100644 --- a/docs/cookbook/examples/basic_create_shot.rst +++ b/docs/cookbook/examples/basic_create_shot.rst @@ -15,12 +15,12 @@ To create a Shot, you need to provide the following values: Example:: data = { - 'project': {"type":"Project","id": 4}, - 'code': '100_010', - 'description': 'Open on a beautiful field with fuzzy bunnies', - 'sg_status_list': 'ip' + "project": {"type": "Project", "id": 4}, + "code": "100_010", + "description": "Open on a beautiful field with fuzzy bunnies", + "sg_status_list": "ip", } - result = sg.create('Shot', data) + result = sg.create("Shot", data) This will create a new Shot named "100_010" in the Project "Gunslinger" (which has an ``id`` of 4). @@ -36,12 +36,12 @@ This will create a new Shot named "100_010" in the Project "Gunslinger" (which h The variable ``result`` now contains a dictionary hash with the Shot information you created.:: { - 'code': '100_010', - 'description': 'Open on a beautiful field with fuzzy bunnies', - 'id': 40435, - 'project': {'id': 4, 'name': 'Demo Project', 'type': 'Project'}, - 'sg_status_list': 'ip', - 'type': 'Shot' + "code": "100_010", + "description": "Open on a beautiful field with fuzzy bunnies", + "id": 40435, + "project": {"id": 4, "name": "Demo Project", "type": "Project"}, + "sg_status_list": "ip", + "type": "Shot", } In addition, Flow Production Tracking has returned the ``id`` that it has assigned to the Shot, as well as a @@ -61,20 +61,20 @@ The Complete Example for creating a Shot # Imports # -------------------------------------- import shotgun_api3 - from pprint import pprint # useful for debugging + from pprint import pprint # useful for debugging # -------------------------------------- # Globals # -------------------------------------- # make sure to change this to match your Flow Production Tracking server and auth credentials. SERVER_PATH = "https://my-site.shotgrid.autodesk.com" - SCRIPT_NAME = 'my_script' - SCRIPT_KEY = '27b65d7063f46b82e670fe807bd2b6f3fd1676c1' + SCRIPT_NAME = "my_script" + SCRIPT_KEY = "27b65d7063f46b82e670fe807bd2b6f3fd1676c1" # -------------------------------------- # Main # -------------------------------------- - if __name__ == '__main__': + if __name__ == "__main__": sg = shotgun_api3.Shotgun(SERVER_PATH, SCRIPT_NAME, SCRIPT_KEY) @@ -82,21 +82,23 @@ The Complete Example for creating a Shot # Create a Shot with data # -------------------------------------- data = { - 'project': {"type":"Project","id": 4}, - 'code': '100_010', - 'description': 'Open on a beautiful field with fuzzy bunnies', - 'sg_status_list': 'ip' + "project": {"type": "Project", "id": 4}, + "code": "100_010", + "description": "Open on a beautiful field with fuzzy bunnies", + "sg_status_list": "ip", } - result = sg.create('Shot', data) + result = sg.create("Shot", data) pprint(result) - print("The id of the {} is {}.".format(result['type'], result['id'])) + print("The id of the {} is {}.".format(result["type"], result["id"])) And here is the output:: - {'code': '100_010', - 'description': 'Open on a beautiful field with fuzzy bunnies', - 'id': 40435, - 'project': {'id': 4, 'name': 'Demo Project', 'type': 'Project'}, - 'sg_status_list': 'ip', - 'type': 'Shot'} + { + "code": "100_010", + "description": "Open on a beautiful field with fuzzy bunnies", + "id": 40435, + "project": {"id": 4, "name": "Demo Project", "type": "Project"}, + "sg_status_list": "ip", + "type": "Shot", + } The id of the Shot is 40435. diff --git a/docs/cookbook/examples/basic_create_version_link_shot.rst b/docs/cookbook/examples/basic_create_version_link_shot.rst index 3f025eb46..82634bfa6 100644 --- a/docs/cookbook/examples/basic_create_version_link_shot.rst +++ b/docs/cookbook/examples/basic_create_version_link_shot.rst @@ -9,9 +9,11 @@ First we need to find the Shot since we'll need to know know its ``id`` in order to it. :: - filters = [ ['project', 'is', {'type': 'Project', 'id': 4}], - ['code', 'is', '100_010'] ] - shot = sg.find_one('Shot', filters) + filters = [ + ["project", "is", {"type": "Project", "id": 4}], + ["code", "is", "100_010"], + ] + shot = sg.find_one("Shot", filters) Find the Task @@ -21,10 +23,12 @@ Version we're creating. For this search we'll use the Shot ``id`` (which we have variable from the previous search) and the Task Name, which maps to the ``content`` field. :: - filters = [ ['project', 'is', {'type': 'Project', 'id': 4}], - ['entity', 'is',{'type':'Shot', 'id': shot['id']}], - ['content', 'is', 'Animation'] ] - task = sg.find_one('Task', filters) + filters = [ + ["project", "is", {"type": "Project", "id": 4}], + ["entity", "is", {"type": "Shot", "id": shot["id"]}], + ["content", "is", "Animation"], + ] + task = sg.find_one("Task", filters) .. note:: Linking a Task to the Version is good practice. By doing so it is easy for users to see at what stage a particular Version was created, and opens up other possibilities for tracking @@ -34,15 +38,17 @@ Create the Version ------------------ Now we can create the Version with the link to the Shot and the Task:: - data = { 'project': {'type': 'Project','id': 4}, - 'code': '100_010_anim_v1', - 'description': 'first pass at opening shot with bunnies', - 'sg_path_to_frames': '/v1/gun/s100/010/frames/anim/100_010_animv1_jack.#.jpg', - 'sg_status_list': 'rev', - 'entity': {'type': 'Shot', 'id': shot['id']}, - 'sg_task': {'type': 'Task', 'id': task['id']}, - 'user': {'type': 'HumanUser', 'id': 165} } - result = sg.create('Version', data) + data = { + "project": {"type": "Project", "id": 4}, + "code": "100_010_anim_v1", + "description": "first pass at opening shot with bunnies", + "sg_path_to_frames": "/v1/gun/s100/010/frames/anim/100_010_animv1_jack.#.jpg", + "sg_status_list": "rev", + "entity": {"type": "Shot", "id": shot["id"]}, + "sg_task": {"type": "Task", "id": task["id"]}, + "user": {"type": "HumanUser", "id": 165}, + } + result = sg.create("Version", data) This will create a new Version named '100_010_anim_v1' linked to the 'Animation' Task for Shot '100_010' in the Project 'Gunslinger'. @@ -62,8 +68,8 @@ This will create a new Version named '100_010_anim_v1' linked to the 'Animation' this example, I know the 'id' that corresponds to this user, but if you don't know the id you can look it up by searching on any of the fields, similar to what we did for the Shot above, like:: - filters = [['login', 'is', 'jschmoe']] - user = sg.find('HumanUser', filters) + filters = [["login", "is", "jschmoe"]] + user = sg.find("HumanUser", filters) The ``result`` variable now contains the ``id`` of the new Version that was created:: diff --git a/docs/cookbook/examples/basic_delete_shot.rst b/docs/cookbook/examples/basic_delete_shot.rst index 886b962d3..26c2f6015 100644 --- a/docs/cookbook/examples/basic_delete_shot.rst +++ b/docs/cookbook/examples/basic_delete_shot.rst @@ -23,20 +23,20 @@ The Complete Example for deleting a Shot # Imports # -------------------------------------- import shotgun_api3 - from pprint import pprint # useful for debugging + from pprint import pprint # useful for debugging # -------------------------------------- # Globals # -------------------------------------- # make sure to change this to match your Flow Production Tracking server and auth credentials. SERVER_PATH = "https://my-site.shotgrid.autodesk.com" - SCRIPT_NAME = 'my_script' - SCRIPT_KEY = '27b65d7063f46b82e670fe807bd2b6f3fd1676c1' + SCRIPT_NAME = "my_script" + SCRIPT_KEY = "27b65d7063f46b82e670fe807bd2b6f3fd1676c1" # -------------------------------------- # Main # -------------------------------------- - if __name__ == '__main__': + if __name__ == "__main__": sg = shotgun_api3.Shotgun(SERVER_PATH, SCRIPT_NAME, SCRIPT_KEY) diff --git a/docs/cookbook/examples/basic_find_shot.rst b/docs/cookbook/examples/basic_find_shot.rst index 4f5d73934..8309e4354 100644 --- a/docs/cookbook/examples/basic_find_shot.rst +++ b/docs/cookbook/examples/basic_find_shot.rst @@ -7,8 +7,8 @@ Building the Query ------------------ We are going to assume we know the 'id' of the Shot we're looking for in this example.:: - filters = [['id', 'is', 40435]] - result = sg.find_one('Shot', filters) + filters = [["id", "is", 40435]] + result = sg.find_one("Shot", filters) Pretty simple right? Well here's a little more insight into what's going on. @@ -23,7 +23,7 @@ Seeing the Result ----------------- So what does this return? The variable result now contains:: - {'type': 'Shot','id': 40435} + {"type": "Shot", "id": 40435} By default, :meth:`~shotgun_api3.Shotgun.find_one` returns a single dictionary object with the ``type`` and ``id`` fields. So in this example, we found a Shot matching that id, and Flow Production Tracking @@ -35,7 +35,7 @@ to help with debugging. It will print out objects in a nicely formatted way that easier to read. So we'll add that to the import section of our script.:: import shotgun_api3 - from pprint import pprint # useful for debugging + from pprint import pprint # useful for debugging The Complete Example for finding a Shot --------------------------------------- @@ -47,30 +47,30 @@ The Complete Example for finding a Shot # Imports # -------------------------------------- import shotgun_api3 - from pprint import pprint # useful for debugging + from pprint import pprint # useful for debugging # -------------------------------------- # Globals # -------------------------------------- # make sure to change this to match your Flow Production Tracking server and auth credentials. SERVER_PATH = "https://my-site.shotgrid.autodesk.com" - SCRIPT_NAME = 'my_script' - SCRIPT_KEY = '27b65d7063f46b82e670fe807bd2b6f3fd1676c1' + SCRIPT_NAME = "my_script" + SCRIPT_KEY = "27b65d7063f46b82e670fe807bd2b6f3fd1676c1" # -------------------------------------- # Main # -------------------------------------- - if __name__ == '__main__': + if __name__ == "__main__": sg = shotgun_api3.Shotgun(SERVER_PATH, SCRIPT_NAME, SCRIPT_KEY) # -------------------------------------- # Find a Shot by id # -------------------------------------- - filters = [['id', 'is', 40435]] - result = sg.find_one('Shot', filters) + filters = [["id", "is", 40435]] + result = sg.find_one("Shot", filters) pprint(result) And here is the output:: - {'type': 'Shot','id': 40435} + {"type": "Shot", "id": 40435} diff --git a/docs/cookbook/examples/basic_sg_instance.rst b/docs/cookbook/examples/basic_sg_instance.rst index d17b57de5..f830f8aca 100644 --- a/docs/cookbook/examples/basic_sg_instance.rst +++ b/docs/cookbook/examples/basic_sg_instance.rst @@ -8,19 +8,19 @@ authentication. ``sg`` represents your Flow Production Tracking API instance. Be :ref:`Setting Up Flow Production Tracking for API Access `. :: - import pprint # Useful for debugging + import pprint # Useful for debugging import shotgun_api3 SERVER_PATH = "https://my-site.shotgrid.autodesk.com" - SCRIPT_NAME = 'my_script' - SCRIPT_KEY = '27b65d7063f46b82e670fe807bd2b6f3fd1676c1' + SCRIPT_NAME = "my_script" + SCRIPT_KEY = "27b65d7063f46b82e670fe807bd2b6f3fd1676c1" sg = shotgun_api3.Shotgun(SERVER_PATH, SCRIPT_NAME, SCRIPT_KEY) # Just for demo purposes, this will print out property and method names available on the # sg connection object - pprint.pprint([symbol for symbol in sorted(dir(sg)) if not symbol.startswith('_')]) + pprint.pprint([symbol for symbol in sorted(dir(sg)) if not symbol.startswith("_")]) For further information on what you can do with this Flow Production Tracking object you can read the :ref:`API reference `. diff --git a/docs/cookbook/examples/basic_update_shot.rst b/docs/cookbook/examples/basic_update_shot.rst index 4e2055d49..d2f511c46 100644 --- a/docs/cookbook/examples/basic_update_shot.rst +++ b/docs/cookbook/examples/basic_update_shot.rst @@ -7,10 +7,10 @@ To update a Shot, you need to provide the ``id`` of the Shot and a list of field update.:: data = { - 'description': 'Open on a beautiful field with fuzzy bunnies', - 'sg_status_list': 'ip' - } - result = sg.update('Shot', 40435, data) + "description": "Open on a beautiful field with fuzzy bunnies", + "sg_status_list": "ip", + } + result = sg.update("Shot", 40435, data) This will update the ``description`` and the ``sg_status_list`` fields for the Shot with ``id`` of **40435**. @@ -27,10 +27,10 @@ Result The variable ``result`` now contains the Shot object that with the updated values.:: { - 'description': 'Opening establishing shot with titles and fuzzy bunnies', - 'sg_status_list': 'ip', - 'type': 'Shot', - 'id': 40435 + "description": "Opening establishing shot with titles and fuzzy bunnies", + "sg_status_list": "ip", + "type": "Shot", + "id": 40435, } In addition, Flow Production Tracking has returned the ``id`` for the Shot, as well as a ``type`` value. ``type`` @@ -50,20 +50,20 @@ The Complete Example for updating a Shot # Imports # -------------------------------------- import shotgun_api3 - from pprint import pprint # useful for debugging + from pprint import pprint # useful for debugging # -------------------------------------- # Globals # -------------------------------------- # make sure to change this to match your Flow Production Tracking server and auth credentials. SERVER_PATH = "https://my-site.shotgrid.autodesk.com" - SCRIPT_NAME = 'my_script' - SCRIPT_KEY = '27b65d7063f46b82e670fe807bd2b6f3fd1676c1' + SCRIPT_NAME = "my_script" + SCRIPT_KEY = "27b65d7063f46b82e670fe807bd2b6f3fd1676c1" # -------------------------------------- # Main # -------------------------------------- - if __name__ == '__main__': + if __name__ == "__main__": sg = shotgun_api3.Shotgun(SERVER_PATH, SCRIPT_NAME, SCRIPT_KEY) @@ -71,15 +71,17 @@ The Complete Example for updating a Shot # Update Shot with data # -------------------------------------- data = { - 'description': 'Open on a beautiful field with fuzzy bunnies', - 'sg_status_list': 'ip' - } - result = sg.update('Shot', 40435, data) + "description": "Open on a beautiful field with fuzzy bunnies", + "sg_status_list": "ip", + } + result = sg.update("Shot", 40435, data) pprint(result) And here is the output:: - {'description': 'Opening establishing shot with titles and fuzzy bunnies', - 'id': 40435, - 'sg_status_list': 'ip', - 'type': 'Shot'} + { + "description": "Opening establishing shot with titles and fuzzy bunnies", + "id": 40435, + "sg_status_list": "ip", + "type": "Shot", + } diff --git a/docs/cookbook/usage_tips.rst b/docs/cookbook/usage_tips.rst index 5d1a7bc1f..c36092d9f 100644 --- a/docs/cookbook/usage_tips.rst +++ b/docs/cookbook/usage_tips.rst @@ -41,7 +41,7 @@ When you do a :meth:`~shotgun_api3.Shotgun.find` or a :meth:`~shotgun_api3.Shotg that returns a field of type **entity** or **multi-entity** (for example the 'Assets' column on Shot), the entities are returned in a standard dictionary:: - {'type': 'Asset', 'name': 'redBall', 'id': 1} + {"type": "Asset", "name": "redBall", "id": 1} For each entity returned, you will get a ``type``, ``name``, and ``id`` key. This does not mean there are fields named ``type`` and ``name`` on the Asset. These are only used to provide a @@ -69,15 +69,15 @@ Something like the following:: # studio_globals.py entity_type_map = { - 'Widget': 'CustomEntity01', - 'Foobar': 'CustomEntity02', - 'Baz': 'CustomNonProjectEntity01, + "Widget": "CustomEntity01", + "Foobar": "CustomEntity02", + "Baz": "CustomNonProjectEntity01", } # or even simpler, you could use a global like this - ENTITY_WIDGET = 'CustomEntity01' - ENTITY_FOOBAR = 'CustomEntity02' - ENTITY_BAZ = 'CustomNonProjectEntity01' + ENTITY_WIDGET = "CustomEntity01" + ENTITY_FOOBAR = "CustomEntity02" + ENTITY_BAZ = "CustomNonProjectEntity01" Then when you're writing scripts, you don't need to worry about remembering which Custom Entity "Foobars" are, you just use your global:: @@ -85,10 +85,16 @@ Then when you're writing scripts, you don't need to worry about remembering whic import shotgun_api3 import studio_globals - sg = shotgun_api3.Shotgun('https://my-site.shotgrid.autodesk.com', 'script_name', '0123456789abcdef0123456789abcdef0123456') - result = sg.find(studio_globals.ENTITY_WIDGET, - filters=[['sg_status_list', 'is', 'ip']], - fields=['code', 'sg_shot']) + sg = shotgun_api3.Shotgun( + "https://my-site.shotgrid.autodesk.com", + "script_name", + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + ) + result = sg.find( + studio_globals.ENTITY_WIDGET, + filters=[["sg_status_list", "is", "ip"]], + fields=["code", "sg_shot"], + ) .. _connection_entities: @@ -110,59 +116,87 @@ entities in Shtogun and are accessible just like any other entity type in Flow P To find information about your Versions in the Playlist "Director Review" (let's say it has an ``id`` of 4). We'd run a query like so:: - filters = [['playlist', 'is', {'type':'Playlist', 'id':4}]] - fields = ['playlist.Playlist.code', 'sg_sort_order', 'version.Version.code', 'version.Version.user', 'version.Version.entity'] - order=[{'column':'sg_sort_order','direction':'asc'}] - result = sg.find('PlaylistVersionConnection', filters, fields, order) + filters = [["playlist", "is", {"type": "Playlist", "id": 4}]] + fields = [ + "playlist.Playlist.code", + "sg_sort_order", + "version.Version.code", + "version.Version.user", + "version.Version.entity", + ] + order = [{"column": "sg_sort_order", "direction": "asc"}] + result = sg.find("PlaylistVersionConnection", filters, fields, order) Which returns the following:: - [{'id': 28, - 'playlist.Playlist.code': 'Director Review', - 'sg_sort_order': 1.0, - 'type': 'PlaylistVersionConnection', - 'version.Version.code': 'bunny_020_0010_comp_v003', - 'version.Version.entity': {'id': 880, - 'name': 'bunny_020_0010', - 'type': 'Shot'}, - 'version.Version.user': {'id': 19, 'name': 'Artist 1', 'type': 'HumanUser'}}, - {'id': 29, - 'playlist.Playlist.code': 'Director Review', - 'sg_sort_order': 2.0, - 'type': 'PlaylistVersionConnection', - 'version.Version.code': 'bunny_020_0020_comp_v003', - 'version.Version.entity': {'id': 881, - 'name': 'bunny_020_0020', - 'type': 'Shot'}, - 'version.Version.user': {'id': 12, 'name': 'Artist 8', 'type': 'HumanUser'}}, - {'id': 30, - 'playlist.Playlist.code': 'Director Review', - 'sg_sort_order': 3.0, - 'type': 'PlaylistVersionConnection', - 'version.Version.code': 'bunny_020_0030_comp_v003', - 'version.Version.entity': {'id': 882, - 'name': 'bunny_020_0030', - 'type': 'Shot'}, - 'version.Version.user': {'id': 33, 'name': 'Admin 5', 'type': 'HumanUser'}}, - {'id': 31, - 'playlist.Playlist.code': 'Director Review', - 'sg_sort_order': 4.0, - 'type': 'PlaylistVersionConnection', - 'version.Version.code': 'bunny_020_0040_comp_v003', - 'version.Version.entity': {'id': 883, - 'name': 'bunny_020_0040', - 'type': 'Shot'}, - 'version.Version.user': {'id': 18, 'name': 'Artist 2', 'type': 'HumanUser'}}, - {'id': 32, - 'playlist.Playlist.code': 'Director Review', - 'sg_sort_order': 5.0, - 'type': 'PlaylistVersionConnection', - 'version.Version.code': 'bunny_020_0050_comp_v003', - 'version.Version.entity': {'id': 884, - 'name': 'bunny_020_0050', - 'type': 'Shot'}, - 'version.Version.user': {'id': 15, 'name': 'Artist 5', 'type': 'HumanUser'}}] + [ + { + "id": 28, + "playlist.Playlist.code": "Director Review", + "sg_sort_order": 1.0, + "type": "PlaylistVersionConnection", + "version.Version.code": "bunny_020_0010_comp_v003", + "version.Version.entity": { + "id": 880, + "name": "bunny_020_0010", + "type": "Shot", + }, + "version.Version.user": {"id": 19, "name": "Artist 1", "type": "HumanUser"}, + }, + { + "id": 29, + "playlist.Playlist.code": "Director Review", + "sg_sort_order": 2.0, + "type": "PlaylistVersionConnection", + "version.Version.code": "bunny_020_0020_comp_v003", + "version.Version.entity": { + "id": 881, + "name": "bunny_020_0020", + "type": "Shot", + }, + "version.Version.user": {"id": 12, "name": "Artist 8", "type": "HumanUser"}, + }, + { + "id": 30, + "playlist.Playlist.code": "Director Review", + "sg_sort_order": 3.0, + "type": "PlaylistVersionConnection", + "version.Version.code": "bunny_020_0030_comp_v003", + "version.Version.entity": { + "id": 882, + "name": "bunny_020_0030", + "type": "Shot", + }, + "version.Version.user": {"id": 33, "name": "Admin 5", "type": "HumanUser"}, + }, + { + "id": 31, + "playlist.Playlist.code": "Director Review", + "sg_sort_order": 4.0, + "type": "PlaylistVersionConnection", + "version.Version.code": "bunny_020_0040_comp_v003", + "version.Version.entity": { + "id": 883, + "name": "bunny_020_0040", + "type": "Shot", + }, + "version.Version.user": {"id": 18, "name": "Artist 2", "type": "HumanUser"}, + }, + { + "id": 32, + "playlist.Playlist.code": "Director Review", + "sg_sort_order": 5.0, + "type": "PlaylistVersionConnection", + "version.Version.code": "bunny_020_0050_comp_v003", + "version.Version.entity": { + "id": 884, + "name": "bunny_020_0050", + "type": "Shot", + }, + "version.Version.user": {"id": 15, "name": "Artist 5", "type": "HumanUser"}, + }, + ] - ``version`` is the Version record for this connection instance. @@ -248,14 +282,14 @@ To write logging output from the Flow Production Tracking API to a file, define import logging import shotgun_api3 as shotgun - logging.basicConfig(level=logging.DEBUG, filename='/path/to/your/log') + logging.basicConfig(level=logging.DEBUG, filename="/path/to/your/log") To suppress the logging output from the API in a script which uses logging, set the level of the Flow Production Tracking logger to a higher level:: import logging import shotgun_api3 as shotgun - sg_log = logging.getLogger('shotgun_api3') + sg_log = logging.getLogger("shotgun_api3") sg_log.setLevel(logging.ERROR) ************* @@ -276,17 +310,21 @@ associated with. Without using "field hopping" in an API call, you would first g then use that data for your follow up query, like so:: # Get the project - project_name = 'Big Buck Bunny' - sg_project = sg.find("Project", [['name', 'is', project_name]]) + project_name = "Big Buck Bunny" + sg_project = sg.find("Project", [["name", "is", project_name]]) # Use project result to get associated shots - sg_shots = sg.find("Shot", [['project', 'is', sg_project]], ['code']) + sg_shots = sg.find("Shot", [["project", "is", sg_project]], ["code"]) With "field hopping" you can combine these queries into:: # Get all shots on 'Big Buck Bunny' project - project_name = 'Big Buck Bunny' - sg_shots = sg.find("Shot", [['project.Project.name', 'is', project_name]], ['code']) + project_name = "Big Buck Bunny" + sg_shots = sg.find( + "Shot", + [["project.Project.name", "is", project_name]], + ["code"], + ) As you can see above, the syntax is to use "``.``" dot notation, joining field names to entity types in a chain. In this example we start with the field ``project`` on the ``Shot`` entity, then @@ -296,9 +334,12 @@ Now that we've demonstrated querying using dot notation, let's take a look at re by adding the status of each Sequence entity associated with each Shot in our previous query:: # Get shot codes and sequence status all in one query - project_name = 'Big Buck Bunny' - sg_shots = sg.find("Shot", [['project.Project.name', 'is', project_name]], - ['code', 'sg_sequence.Sequence.sg_status_list']) + project_name = "Big Buck Bunny" + sg_shots = sg.find( + "Shot", + [["project.Project.name", "is", project_name]], + ["code", "sg_sequence.Sequence.sg_status_list"], + ) The previous examples use the :meth:`~shotgun_api3.Shotgun.find` method. However, it's also applicable to the :meth:`~shotgun_api3.Shotgun.create` method. diff --git a/docs/index.rst b/docs/index.rst index 4450b60d7..93a314404 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,28 +32,42 @@ Quicktimes, etc. **Example**:: - sg = shotgun_api3.Shotgun("https://my-site.shotgrid.autodesk.com", - login="rhendriks", - password="c0mPre$Hi0n") - sg.find("Shot", filters=[["sg_status_list", "is", "ip"]], fields=["code", "sg_status_list"]) + sg = shotgun_api3.Shotgun( + "https://my-site.shotgrid.autodesk.com", + login="rhendriks", + password="c0mPre$Hi0n", + ) + sg.find( + "Shot", + filters=[["sg_status_list", "is", "ip"]], + fields=["code", "sg_status_list"], + ) **Output**:: - [{'code': 'bunny_020_0170', - 'id': 896, - 'sg_sequence': {'id': 5, 'name': 'bunny_020', 'type': 'Sequence'}, - 'sg_status_list': 'ip', - 'type': 'Shot'}, - {'code': 'bunny_020_0200', - 'id': 899, - 'sg_sequence': {'id': 5, 'name': 'bunny_020', 'type': 'Sequence'}, - 'sg_status_list': 'ip', - 'type': 'Shot'}, - {'code': 'bunny_030_0080', - 'id': 907, - 'sg_sequence': {'id': 6, 'name': 'bunny_030', 'type': 'Sequence'}, - 'sg_status_list': 'ip', - 'type': 'Shot'}] + [ + { + "code": "bunny_020_0170", + "id": 896, + "sg_sequence": {"id": 5, "name": "bunny_020", "type": "Sequence"}, + "sg_status_list": "ip", + "type": "Shot", + }, + { + "code": "bunny_020_0200", + "id": 899, + "sg_sequence": {"id": 5, "name": "bunny_020", "type": "Sequence"}, + "sg_status_list": "ip", + "type": "Shot", + }, + { + "code": "bunny_030_0080", + "id": 907, + "sg_sequence": {"id": 6, "name": "bunny_030", "type": "Sequence"}, + "sg_status_list": "ip", + "type": "Shot", + }, + ] ********** From 4c7576ad3c3af4cd139f492e900f45bbc75082bd Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Mon, 27 Oct 2025 12:38:25 -0700 Subject: [PATCH 2/5] test better --- docs/cookbook/attachments.rst | 26 ++-- docs/cookbook/tasks/updating_tasks.rst | 106 ++++++++--------- docs/cookbook/usage_tips.rst | 14 +-- docs/index.rst | 3 +- docs/installation.rst | 10 +- docs/reference.rst | 157 +++++++++++++------------ 6 files changed, 161 insertions(+), 155 deletions(-) diff --git a/docs/cookbook/attachments.rst b/docs/cookbook/attachments.rst index 1388deac9..8f452d667 100644 --- a/docs/cookbook/attachments.rst +++ b/docs/cookbook/attachments.rst @@ -253,8 +253,11 @@ Reading Local File Fields :: - fields = ["sg_uploaded_movie"] - result = sg.find("Version", [["id", "is", 123]], fields) + sg.find( + "Version", + [["id", "is", 123]], # filters + fields = ["sg_uploaded_movie"], + ) Returns:: @@ -322,10 +325,10 @@ Example 1: Using ``local_path`` :: - result = sg.update( - "Version", - 123, - { + sg.update( + "Version", # entity_type + 123, # entity_id + { # data "sg_uploaded_movie": { "local_path": "/Users/kp/Movies/testing/test_movie_002.mov", "name": "Better Movie", @@ -364,10 +367,10 @@ Example 2: Using ``relative_path`` :: - result = sg.update( - "Version", - 123, - { + sg.update( + "Version", # entity_type + 123, # entity_id + { # data "sg_uploaded_movie": { "local_storage": { "type": "LocalStorage", @@ -406,8 +409,7 @@ Un-setting local file field values Removing a a local file field value is simple. Just set the value to ``None``:: - data = {"sg_uploaded_movie": None} - result = sg.update("Version", 123, data) + sg.update("Version", 123, {"sg_uploaded_movie": None}) Returns:: diff --git a/docs/cookbook/tasks/updating_tasks.rst b/docs/cookbook/tasks/updating_tasks.rst index db2433e79..6f227a42c 100644 --- a/docs/cookbook/tasks/updating_tasks.rst +++ b/docs/cookbook/tasks/updating_tasks.rst @@ -74,8 +74,8 @@ Regardless of current values on the Task, this behavior rules:: :: - sg.update ('Task', 123, {'start_date':'2011-05-25', 'duration':3600}) - # Task = {'start_date': '2011-05-25', 'due_date': '2011-06-01', 'duration': 3600, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-25", "duration":3600}) + # Task = {"start_date": "2011-05-25", "due_date": "2011-06-01", "duration": 3600, "id":123} - ``start_date`` is updated. - ``duration`` is updated. @@ -85,8 +85,8 @@ Regardless of current values on the Task, this behavior rules:: :: - sg.update ('Task', 123, {'due_date': '2011-05-20', 'duration':3600}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-20', 'duration': 600, 'id':123} + sg.update ("Task", 123, {"due_date": "2011-05-20", "duration":3600}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-20", "duration": 600, "id":123} - ``duration`` is updated. - ``due_date`` is updated. @@ -104,14 +104,14 @@ will behave. :: - Task = {'start_date': '2011-05-20', 'due_date': None, 'duration': None, 'id':123} + Task = {"start_date": "2011-05-20", "due_date": None, "duration": None, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-25', 'due_date': None, 'duration': None, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-25", "due_date": None, "duration": None, "id":123} - Only ``start_date`` is updated. @@ -119,8 +119,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} - ``due_date`` is updated. - ``duration`` is updated to (``due_date`` - ``start_date``). @@ -129,8 +129,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':2400}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"duration":2400}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} - ``duration`` is updated. - ``due_date`` is set to (``start_date`` + ``duration``) @@ -145,14 +145,14 @@ will behave. :: - # Task = {'start_date': None, 'due_date': '2011-05-25', 'duration': None, 'id':123} + # Task = {"start_date": None, "due_date": "2011-05-25", "duration": None, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-20'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-20"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} - ``start_date`` is updated. - ``duration`` is updated to (``due_date`` - ``start_date``). @@ -161,8 +161,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-20'}) - # Task = {'start_date': None, 'due_date': '2011-05-20', 'duration': None, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-20"}) + # Task = {"start_date": None, "due_date": "2011-05-20", "duration": None, "id":123} - only ``due_date`` is updated. @@ -170,8 +170,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':2400}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"duration":2400}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} - ``duration`` is updated. - ``start_date`` is set to (``due_date`` - ``duration``) @@ -186,14 +186,14 @@ will behave. :: - # Task = {'start_date': None, 'due_date': None, 'duration': 2400, 'id':123} + # Task = {"start_date": None, "due_date": None, "duration": 2400, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-20'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-20"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} - ``start_date`` is updated. - ``due_date`` is updated to (``start_date`` + ``duration``). @@ -202,8 +202,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} - ``due_date`` is updated. - ``start_date`` is updated to (``due_date`` - ``duration``) @@ -212,8 +212,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':3600}) - # Task = {'start_date': None, 'due_date': None, 'duration': 3600, 'id':123} + sg.update ("Task", 123, {"duration":3600}) + # Task = {"start_date": None, "due_date": None, "duration": 3600, "id":123} - only ``duration`` is updated. @@ -227,14 +227,14 @@ will behave. :: - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': None, 'id':123} + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": None, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-25', 'due_date': '2011-05-25', 'duration': 600, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-25", "due_date": "2011-05-25", "duration": 600, "id":123} - ``start_date`` is updated. - ``duration`` is updated to (``due_date`` - ``start_date``). @@ -243,8 +243,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-30'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-30', 'duration': 4200, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-30"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-30", "duration": 4200, "id":123} - ``due_date`` is updated. - ``duration`` is updated to (``due_date`` - ``start_date``) @@ -253,8 +253,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':3600}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-27', 'duration': 3600, 'id':123} + sg.update ("Task", 123, {"duration":3600}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-27", "duration": 3600, "id":123} - ``duration`` is updated. - ``due_date`` is updated to (``start_date`` + ``duration``) @@ -269,14 +269,14 @@ will behave. :: - # Task = {'start_date': '2011-05-20', 'due_date': None, 'duration': 2400, 'id':123} + # Task = {"start_date": "2011-05-20", "due_date": None, "duration": 2400, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-25', 'due_date': '2011-05-30', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-25", "due_date": "2011-05-30", "duration": 2400, "id":123} - ``start_date`` is updated. - ``due_date`` is updated to (``start_date`` +``duration``). @@ -285,8 +285,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-30'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-30', 'duration': 4200, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-30"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-30", "duration": 4200, "id":123} - ``due_date`` is updated. - ``duration`` is updated to (``due_date`` - ``start_date``). @@ -295,8 +295,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':3600}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-27', 'duration': 3600, 'id':123} + sg.update ("Task", 123, {"duration":3600}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-27", "duration": 3600, "id":123} - ``duration`` is updated. - ``due_date`` is updated to (``start_date`` + ``duration``) @@ -311,14 +311,14 @@ will behave. :: - # Task = {'start_date': None, 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + # Task = {"start_date": None, "due_date": "2011-05-25", "duration": 2400, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-25', 'due_date': '2011-05-30', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-25", "due_date": "2011-05-30", "duration": 2400, "id":123} - ``start_date`` is updated. - ``due_date`` is updated to (``start_date`` + ``duration``). @@ -327,8 +327,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-30'}) - # Task = {'start_date': '2011-05-25', 'due_date': '2011-05-30', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-30"}) + # Task = {"start_date": "2011-05-25", "due_date": "2011-05-30", "duration": 2400, "id":123} - ``due_date`` is updated. - ``start_date`` is updated to (``due_date`` - ``duration``). @@ -337,8 +337,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':3600}) - # Task = {'start_date': '2011-05-18', 'due_date': '2011-05-25', 'duration': 3600, 'id':123} + sg.update ("Task", 123, {"duration":3600}) + # Task = {"start_date": "2011-05-18", "due_date": "2011-05-25", "duration": 3600, "id":123} - ``duration`` is updated. - ``start_date`` is updated to (``due_date`` - ``duration``) @@ -353,14 +353,14 @@ will behave. :: - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-25', 'duration': 2400, 'id':123} + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-25", "duration": 2400, "id":123} **Update start_date** :: - sg.update ('Task', 123, {'start_date':'2011-05-25'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-30', 'duration': 2400, 'id':123} + sg.update ("Task", 123, {"start_date":"2011-05-25"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-30", "duration": 2400, "id":123} - ``start_date`` is updated. - ``due_date`` is updated to (``start_date`` + ``duration``). @@ -369,8 +369,8 @@ will behave. :: - sg.update ('Task', 123, {'due_date':'2011-05-30'}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-30', 'duration': 4200, 'id':123} + sg.update ("Task", 123, {"due_date":"2011-05-30"}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-30", "duration": 4200, "id":123} - ``due_date`` is updated. - ``duration`` is updated to (``due_date`` - ``start_date``) @@ -379,8 +379,8 @@ will behave. :: - sg.update ('Task', 123, {'duration':3600}) - # Task = {'start_date': '2011-05-20', 'due_date': '2011-05-27', 'duration': 3600, 'id':123} + sg.update ("Task", 123, {"duration":3600}) + # Task = {"start_date": "2011-05-20", "due_date": "2011-05-27", "duration": 3600, "id":123} - ``duration`` is updated. - ``due_date`` is updated to (``start_date`` + ``duration``) diff --git a/docs/cookbook/usage_tips.rst b/docs/cookbook/usage_tips.rst index c36092d9f..d2b291c29 100644 --- a/docs/cookbook/usage_tips.rst +++ b/docs/cookbook/usage_tips.rst @@ -92,7 +92,7 @@ Then when you're writing scripts, you don't need to worry about remembering whic ) result = sg.find( studio_globals.ENTITY_WIDGET, - filters=[["sg_status_list", "is", "ip"]], + [["sg_status_list", "is", "ip"]], # filters fields=["code", "sg_shot"], ) @@ -125,7 +125,7 @@ To find information about your Versions in the Playlist "Director Review" (let's "version.Version.entity", ] order = [{"column": "sg_sort_order", "direction": "asc"}] - result = sg.find("PlaylistVersionConnection", filters, fields, order) + result = sg.find("PlaylistVersionConnection", filters, fields=fields, order=order) Which returns the following:: @@ -318,12 +318,12 @@ then use that data for your follow up query, like so:: With "field hopping" you can combine these queries into:: - # Get all shots on 'Big Buck Bunny' project + # Get all shots on "Big Buck Bunny" project project_name = "Big Buck Bunny" sg_shots = sg.find( "Shot", - [["project.Project.name", "is", project_name]], - ["code"], + [["project.Project.name", "is", project_name]], # filters + fields=["code"], ) As you can see above, the syntax is to use "``.``" dot notation, joining field names to entity @@ -337,8 +337,8 @@ by adding the status of each Sequence entity associated with each Shot in our pr project_name = "Big Buck Bunny" sg_shots = sg.find( "Shot", - [["project.Project.name", "is", project_name]], - ["code", "sg_sequence.Sequence.sg_status_list"], + [["project.Project.name", "is", project_name]], # filters + fields=["code", "sg_sequence.Sequence.sg_status_list"], ) The previous examples use the :meth:`~shotgun_api3.Shotgun.find` method. However, it's also applicable diff --git a/docs/index.rst b/docs/index.rst index 93a314404..c892779da 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,9 +37,10 @@ Quicktimes, etc. login="rhendriks", password="c0mPre$Hi0n", ) + sg.find( "Shot", - filters=[["sg_status_list", "is", "ip"]], + [["sg_status_list", "is", "ip"]], # filters fields=["code", "sg_status_list"], ) diff --git a/docs/installation.rst b/docs/installation.rst index b082b1669..ed310f1a1 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -44,18 +44,10 @@ If you wish to install the current master, use the following command:: Installing A specific Version From Github ========================================= To install a specific version of the package from Github, run the following command. This example -installs the v3.0.26 tag, replace the version tag with the one you want:: +installs the ``v3.0.26`` tag, replace the version tag with the one you want:: pip install git+https://github.com/shotgunsoftware/python-api.git@v3.0.26 - -``requirements.txt`` -~~~~~~~~~~~~~~~~~~~~ -If you're using pip with `requirements.txt`, add the following line:: - - git+https://github.com/shotgunsoftware/python-api.git - - **************************** Installing with ``setup.py`` **************************** diff --git a/docs/reference.rst b/docs/reference.rst index 28bfabf1b..0b3786fac 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -241,11 +241,13 @@ Basic Example Using the default filter_operator ``"all"``, the following filters will return all Shots whose status is "ip" AND is linked to Asset #9:: - filters = [ - ["sg_status_list", "is", "ip"], - ["assets", "is", {"type": "Asset", "id": 9}] - ] - result = sg.find("Shot", filters) + sg.find( + "Shot", + [ # filters + ["sg_status_list", "is", "ip"], + ["assets", "is", {"type": "Asset", "id": 9}], + ] + ) Complex Filters @@ -262,17 +264,20 @@ Complex Example Using the default filter_operator ``"all"``, the following filters will return all Shots whose status is "ip" AND is linked to either Asset #9 OR Asset #23:: - filters = [ - ["sg_status_list", "is", "ip"], - { - "filter_operator": "any", - "filters": [ - ["assets", "is", {"type": "Asset", "id": 9}], - ["assets", "is", {"type": "Asset", "id": 23}] - ] - } - ] - result = sg.find("Shot", filters) + sg.find( + "Shot", + [ # filters + ["sg_status_list", "is", "ip"], + { + "filter_operator": "any", + "filters": [ + ["assets", "is", {"type": "Asset", "id": 9}], + ["assets", "is", {"type": "Asset", "id": 23}], + ], + }, + ], + ) + Operators and Arguments @@ -496,13 +501,13 @@ created:: additional_filter_presets = [ { "preset_name": "LATEST", - "latest_by": "ENTITIES_CREATED_AT" + "latest_by": "ENTITIES_CREATED_AT", } ] filters = [['code', 'is', 'ABC']] - result = sg.find('Version', filters = filters, additional_filter_presets = additional_filter_presets) + sg.find('Version', filters, additional_filter_presets = additional_filter_presets) The following query will find all CutItems associated to Cut #1 and return all Versions associated @@ -515,7 +520,7 @@ to the Shot linked to each of these CutItems:: } ] - result = sg.find('Version', additional_filter_presets = additional_filter_presets) + result = sg.find('Version', additional_filter_presets = additional_filter_presets) # TODO not working Available Filter Presets by Entity Type --------------------------------------- @@ -610,11 +615,11 @@ List of dicts:: [ { - 'type': 'HumanUser' | 'Group', - 'id': int, - ... + "type": "HumanUser" | "Group", + "id": int, + # ... }, - ... + # ... ] checkbox @@ -669,9 +674,9 @@ entity :: { - 'type': "string", - 'id': int, - ... + "type": "string", + "id": int, + # ... } float @@ -714,11 +719,11 @@ List of dicts [ { - 'type': "string", - 'id': int, - ... + "type": "string", + "id": int, + # ... }, - ... + # ... ] number @@ -781,10 +786,10 @@ url (file/link field) :: { - 'content_type': "string", - 'link_type': "local" | "url" | "upload", - 'name': "string", - 'url': "string" + "content_type": "string", + "link_type": "local" | "url" | "upload", + "name": "string", + "url": "string" } Local File Links @@ -797,27 +802,27 @@ Additional keys exist for local file links :: { - 'content_type': "string", - 'link_type': "local", - 'local_path': "string" | None, - 'local_path_linux': "string" | None, - 'local_path_mac': "string" | None, - 'local_path_windows': "string" | None, - 'local_storage': { - 'type': 'LocalStorage', - 'id': int | None, - 'name': "string" | None, + "content_type": "string", + "link_type": "local", + "local_path": "string" | None, + "local_path_linux": "string" | None, + "local_path_mac": "string" | None, + "local_path_windows": "string" | None, + "local_storage": { + "type": "LocalStorage", + "id": int | None, + "name": "string" | None, }, - 'name': "string", - 'relative_path': "string" | None - 'url': "string", + "name": "string", + "relative_path": "string" | None + "url": "string", } API versions < v3.0.3: { - 'url': "string", - 'name': "string", - 'content_type': "string" + "url": "string", + "name": "string", + "content_type": "string" } .. _interpreting_image_field_strings: @@ -978,12 +983,12 @@ For example, a ``find`` call like this: .. code-block:: python - sg.find('Asset', [['project', 'is', { - 'created_at': datetime.datetime(2015, 12, 16, 11, 2, 10, tzinfo), - 'id': 9999, - 'name': 'Demo: Game', - 'type': 'Project', - # More entity attributes + sg.find("Asset", [["project", "is", { + "created_at": datetime.datetime(2015, 12, 16, 11, 2, 10, tzinfo), + "id": 9999, + "name": "Demo: Game", + "type": "Project", + # ... }]]) @@ -991,7 +996,7 @@ Will internally be transformed as if you invoked something like this: .. code-block:: python - sg.find('Asset', [['project', 'is', {'id': 999, 'type': 'Project'}]]) + sg.find("Asset", [["project", "is", {"id": 999, "type": "Project"}]]) ************ @@ -1013,24 +1018,30 @@ Example for a user whose language preference is set to Japanese: >>> sg = Shotgun(site_name, script_name, script_key) >>> sg.config.localized # checking that localization is disabled False - >>> sg.schema_field_read('Shot') + >>> sg.schema_field_read("Shot") { - 'sg_vendor_groups': { - 'mandatory': {'editable': False, 'value': False}, - # the value field (display name) is not localized - 'name': {'editable': True, 'value': 'Vendor Groups'}, - ... - }, - ... + "sg_vendor_groups": { + "mandatory": { + "editable": False, + "value": False, + }, + # the value field (display name) is not localized + "name": {"editable": True, "value": "Vendor Groups"}, + # ... + }, + # ... } >>> sg.config.localized = True # enabling the localization - >>> sg.schema_field_read('Shot') + >>> sg.schema_field_read("Shot") { - 'sg_vendor_groups': { - 'mandatory': {'editable': False, 'value': False}, - # the value field (display name) is localized - 'name': {'editable': True, 'value': '\xe3\x83\x99\xe3\x83\xb3\xe3\x83\x80\xe3\x83\xbc \xe3\x82\xb0\xe3\x83\xab\xe3\x83\xbc\xe3\x83\x97'}, - ... - }, - ... + "sg_vendor_groups": { + "mandatory": {"editable": False, "value": False}, + # the value field (display name) is localized + "name": { + "editable": True, + "value": "\xe3\x83\x99\xe3\x83\xb3\xe3\x83\x80\xe3\x83\xbc \xe3\x82\xb0\xe3\x83\xab\xe3\x83\xbc\xe3\x83\x97", + }, + # ... + }, + # ... } From f2fb0c7a9d7d24c1a190798febf80b91b4fdc6ab Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Mon, 27 Oct 2025 13:48:40 -0700 Subject: [PATCH 3/5] More co pilot --- .../examples/ami_version_packager.rst | 139 ++++++++++------ docs/cookbook/examples/svn_integration.rst | 79 +++++---- docs/cookbook/tasks/split_tasks.rst | 157 +++++++++--------- docs/cookbook/tasks/task_dependencies.rst | 84 ++++++---- 4 files changed, 263 insertions(+), 196 deletions(-) diff --git a/docs/cookbook/examples/ami_version_packager.rst b/docs/cookbook/examples/ami_version_packager.rst index 415075a1d..429ff3350 100644 --- a/docs/cookbook/examples/ami_version_packager.rst +++ b/docs/cookbook/examples/ami_version_packager.rst @@ -46,7 +46,8 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # --------------------------------------------------------------------------------------------- # Imports # --------------------------------------------------------------------------------------------- - import sys, os + import sys + import os import logging as logger import subprocess import re @@ -61,16 +62,16 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # --------------------------------------------------------------------------------------------- # Flow Production Tracking server auth info shotgun_conf = { - 'url':'https://my-site.shotgrid.autodesk.com', - 'name':'YOUR_SCRIPT_NAME_HERE', - 'key':'YOUR_SCRIPT_KEY_HERE' - } + "url": "https://my-site.shotgrid.autodesk.com", + "name": "YOUR_SCRIPT_NAME_HERE", + "key": "YOUR_SCRIPT_KEY_HERE", + } # location to write logfile for this script - logfile = os.path.dirname(sys.argv[0])+"/version_packager.log" + logfile = os.path.dirname(sys.argv[0]) + "/version_packager.log" # temporary directory to download movie files to and create thumbnail files in - file_dir = os.path.dirname(sys.argv[0])+"/tmp" + file_dir = os.path.dirname(sys.argv[0]) + "/tmp" # compress command # tar czf /home/user/backup_www.tar.gz -C / var/www/html @@ -91,13 +92,15 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # ---------------------------------------------- def init_log(filename="version_packager.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+') + 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 ShotgunException ("Unable to open logfile for writing: %s" % e) + raise ShotgunException("Unable to open logfile for writing: %s" % e) logger.info("Version Packager logging started.") return logger @@ -107,7 +110,7 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # ---------------------------------------------- def extract_attachment_id(attachment): # extract the Attachment id from the url location - attachment_id = attachment['url'].rsplit('/',1)[1] + attachment_id = attachment["url"].rsplit("/", 1)[1] try: attachment_id = int(attachment_id) except: @@ -121,7 +124,7 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # ---------------------------------------------- # Download Movie to Disk # ---------------------------------------------- - def download_attachment_to_disk(attachment,destination_filename): + def download_attachment_to_disk(attachment, destination_filename): attachment_id = extract_attachment_id(attachment) if type(attachment_id) != int: return None @@ -129,29 +132,31 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h logger.info("Downloading Attachment #%s" % (attachment_id)) stream = sg.download_attachment(attachment_id) try: - file = open(destination_filename, 'w') + file = open(destination_filename, "w") file.write(stream) file.close() logger.info("Downloaded attachment %s" % (destination_filename)) return True except e: - raise ShotgunException("unable to write attachment to disk: %s"% e) + raise ShotgunException("unable to write attachment to disk: %s" % e) # ---------------------------------------------- # Compress files # ---------------------------------------------- - def compress_files(files,destination_filename): + def compress_files(files, destination_filename): destination_filename += ".tar.gz" files = [path.lstrip("/") for path in files] - squish_me = compress_cmd % (destination_filename, " ".join(files) ) + squish_me = compress_cmd % (destination_filename, " ".join(files)) logger.info("Compressing %s files..." % len(files)) logger.info("Running command: %s" % squish_me) try: - output = subprocess.Popen(squish_me, shell=True, stdout=subprocess.PIPE).stdout.read() - logger.info('tar/gzip command returned: %s' % output) + output = subprocess.Popen( + squish_me, shell=True, stdout=subprocess.PIPE + ).stdout.read() + logger.info("tar/gzip command returned: %s" % output) except e: - raise ShotgunException("unable compress files: %s"% e) + raise ShotgunException("unable compress files: %s" % e) logger.info("compressed files to: %s" % destination_filename) return destination_filename @@ -160,57 +165,80 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # Remove downloaded files # ---------------------------------------------- def remove_downloaded_files(files): - remove_me = 'rm %s' % ( " ".join(files) ) + remove_me = "rm %s" % (" ".join(files)) logger.info("Removing %s files..." % len(files)) logger.info("Running command: %s" % remove_me) try: - output = subprocess.Popen(remove_me, shell=True, stdout=subprocess.PIPE).stdout.read() - logger.info('rm command returned: %s' % output) + output = subprocess.Popen( + remove_me, shell=True, stdout=subprocess.PIPE + ).stdout.read() + logger.info("rm command returned: %s" % output) logger.info("removed downloaded files") return True except e: - logger.error("unable remove files: %s"% e) + logger.error("unable remove files: %s" % e) return False # ---------------------------------------------- # Copy files # ---------------------------------------------- - def copy_files(files,destination_directory): + def copy_files(files, destination_directory): if type(files) == list: files = " ".join(files) copy_me_args = "%s %s" % (files, destination_directory) logger.info("Running command: mv %s" % copy_me_args) try: - result = subprocess.Popen("mv " + copy_me_args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = subprocess.Popen( + "mv " + copy_me_args, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) # 0 = success, 1 = recoverable issues if result.returncode > 0: response = result.stderr.read() - logger.error("Copy failed: %s"% response) - raise ShotgunException("Copy failed: %s"% response) + logger.error("Copy failed: %s" % response) + raise ShotgunException("Copy failed: %s" % response) except OSError, e: - raise ShotgunException("unable copy files: %s"% e) + raise ShotgunException("unable copy files: %s" % e) logger.info("copied files to: %s" % destination_directory) return destination_directory - def packageFilesForClient(file_field,destination_dir): + def packageFilesForClient(file_field, destination_dir): # get entities matching the selected ids - logger.info("Querying Shotgun for %s %ss" % (len(sa.selected_ids_filter), sa.params['entity_type'])) - entities = sg.find(sa.params['entity_type'],sa.selected_ids_filter,['id','code',file_field],filter_operator='any') + logger.info( + "Querying Shotgun for %s %ss" + % (len(sa.selected_ids_filter), sa.params["entity_type"]) + ) + entities = sg.find( + sa.params["entity_type"], + sa.selected_ids_filter, + ["id", "code", file_field], + filter_operator="any", + ) # download the attachments for each entity, zip them, and copy to destination directory files = [] for e in entities: if not e[file_field]: - logger.info("%s #%s: No file exists. Skippinsa." % (sa.params['entity_type'], e['id'])) + logger.info( + "%s #%s: No file exists. Skippinsa." + % (sa.params["entity_type"], e["id"]) + ) else: - logger.info("%s #%s: %s" % (sa.params['entity_type'], e['id'], e[file_field])) - path_to_file = file_dir+"/"+re.sub(r"\s+", '_', e[file_field]['name']) - result = download_attachment_to_disk(e[file_field], path_to_file ) + logger.info( + "%s #%s: %s" + % (sa.params["entity_type"], e["id"], e[file_field]) + ) + path_to_file = ( + file_dir + "/" + re.sub(r"\s+", "_", e[file_field]["name"]) + ) + result = download_attachment_to_disk(e[file_field], path_to_file) # only include attachments. urls won't return true if result: @@ -218,17 +246,22 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h # compress files # create a nice valid destination filename - project_name = '' - if 'project_name' in sa.params: - project_name = re.sub(r"\s+", '_', sa.params['project_name'])+'_' - dest_filename = project_name+datetime.today().strftime('%Y-%m-%d-%H%M%S')+"_"+sa.params['user_login'] - archive = compress_files(files,file_dir+"/"+dest_filename) + project_name = "" + if "project_name" in sa.params: + project_name = re.sub(r"\s+", "_", sa.params["project_name"]) + "_" + dest_filename = ( + project_name + + datetime.today().strftime("%Y-%m-%d-%H%M%S") + + "_" + + sa.params["user_login"] + ) + archive = compress_files(files, file_dir + "/" + dest_filename) # now that we have the archive, remove the downloads r = remove_downloaded_files(files) # copy to directory - result = copy_files([archive],destination_dir) + result = copy_files([archive], destination_dir) return True @@ -241,16 +274,22 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h try: sa = ShotgunAction(sys.argv[1]) - logger.info("Firing... %s" % (sys.argv[1]) ) + logger.info("Firing... %s" % (sys.argv[1])) except IndexError, e: raise ShotgunException("Missing POST arguments") - sg = Shotgun(shotgun_conf['url'], shotgun_conf['name'], shotgun_conf['key'],convert_datetimes_to_utc=convert_tz) - - if sa.action == 'package4client': - result = packageFilesForClient('sg_qt','/Users/kp/Documents/shotgun/dev/api/files/') + sg = Shotgun( + shotgun_conf["url"], + shotgun_conf["name"], + shotgun_conf["key"], + convert_datetimes_to_utc=convert_tz, + ) + + if sa.action == "package4client": + result = packageFilesForClient( + "sg_qt", "/Users/kp/Documents/shotgun/dev/api/files/" + ) else: raise ShotgunException("Unknown action... :%s" % sa.action) - print("\nVersion Packager done!") diff --git a/docs/cookbook/examples/svn_integration.rst b/docs/cookbook/examples/svn_integration.rst index c676fd8ea..fc779cdad 100644 --- a/docs/cookbook/examples/svn_integration.rst +++ b/docs/cookbook/examples/svn_integration.rst @@ -83,45 +83,54 @@ link to the Revision. # --------------------------------------------------------------------------------------------- # Globals - update all of these values to those of your studio # --------------------------------------------------------------------------------------------- - SERVER_PATH = 'https ://my-site.shotgrid.autodesk.com' # or http: - SCRIPT_USER = 'script_name' - SCRIPT_KEY = '3333333333333333333333333333333333333333' - REVISIONS_PATH = 'https ://serveraddress/trac/changeset/' # or other web-based UI - PROJECT = {'type':'Project', 'id':27} + SERVER_PATH = "https://my-site.shotgrid.autodesk.com" # or http: + SCRIPT_USER = "script_name" + SCRIPT_KEY = "3333333333333333333333333333333333333333" + REVISIONS_PATH = "https://serveraddress/trac/changeset/" # or other web-based UI + PROJECT = {"type": "Project", "id": 27} # --------------------------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------------------------- - if __name__ == '__main__': - - sg = Shotgun(SERVER_PATH, SCRIPT_USER, SCRIPT_KEY) - - # Set Python variables from the environment objects - revision_code = os.environ['REV'] - repository = os.environ['REPOS'] - description = os.environ['COMMENT'] - author = os.environ['AUTHOR'] - - # Set the Trac path for this specific revision - revision_url = REVISIONS_PATH + revision_code - - # Validate that author is a valid Flow Production Tracking HumanUser - result = sg.find_one("HumanUser", [['login', 'is', author]]) - if result: - # Create Revision - url = {'content_type':'http_url', 'url':revision_url, 'name':'Trac'} - parameters = {'project':PROJECT, - 'code':str(revision_code), - 'description':description, - 'attachment':url, - 'created_by':{"type":"HumanUser", "id":result['id']} - } - revision = sg.create("Revision", parameters) - print("created Revision #"+str(revision_code)) - - # Send error message if valid HumanUser is not found - else: - print("Unable to find a valid Flow Production Tracking User with login: {}, Revision not created in Flow Production Tracking.".format(author)) + if __name__ == "__main__": + + sg = Shotgun(SERVER_PATH, SCRIPT_USER, SCRIPT_KEY) + + # Set Python variables from the environment objects + revision_code = os.environ["REV"] + repository = os.environ["REPOS"] + description = os.environ["COMMENT"] + author = os.environ["AUTHOR"] + + # Set the Trac path for this specific revision + revision_url = REVISIONS_PATH + revision_code + + # Validate that author is a valid Flow Production Tracking HumanUser + result = sg.find_one("HumanUser", [["login", "is", author]]) + if result: + # Create Revision + url = { + "content_type": "http_url", + "url": revision_url, + "name": "Trac", + } + parameters = { + "project": PROJECT, + "code": str(revision_code), + "description": description, + "attachment": url, + "created_by": {"type": "HumanUser", "id": result["id"]}, + } + revision = sg.create("Revision", parameters) + print("created Revision #" + str(revision_code)) + + # Send error message if valid HumanUser is not found + else: + print( + "Unable to find a valid Flow Production Tracking User with login: {}, Revision not created in Flow Production Tracking.".format( + author + ) + ) diff --git a/docs/cookbook/tasks/split_tasks.rst b/docs/cookbook/tasks/split_tasks.rst index 96f639037..95395ea83 100644 --- a/docs/cookbook/tasks/split_tasks.rst +++ b/docs/cookbook/tasks/split_tasks.rst @@ -17,7 +17,10 @@ in the ``YYYY-mm-dd`` format. :: - [{'start': '2012-12-11', 'end': '2012-12-12'}, {'start': '2012-12-18', 'end': '2012-12-19'}] + [ + {"start": "2012-12-11", "end": "2012-12-12"}, + {"start": "2012-12-18", "end": "2012-12-19"}, + ] - Splits should be ordered from eldest to newest. - There should be gaps between each split. @@ -28,7 +31,17 @@ in the ``YYYY-mm-dd`` format. If there are multiple splits but there between two or more splits there is no gap, an error will be raised. For example:: - >>> sg.update('Task', 2088, {'splits':[{'start':'2012-12-10', 'end':'2012-12-11'}, {'start':'2012-12-12', 'end':'2012-12-14'}, {'start':'2012-12-19', 'end':'2012-12-20'}]}) + >>> sg.update( + ... "Task", + ... 2088, + ... { + ... "splits": [ + ... {"start": "2012-12-10", "end": "2012-12-11"}, + ... {"start": "2012-12-12", "end": "2012-12-14"}, + ... {"start": "2012-12-19", "end": "2012-12-20"}, + ... ] + ... }, + ... ) Traceback (most recent call last): File "", line 1, in File "/shotgun/src/python-api/shotgun_api3/shotgun.py", line 600, in update @@ -71,24 +84,28 @@ start_date, due_date and duration being ignored :: - sg.update('Task', 2088, { - 'start_date': '2012-12-06', - 'due_date': '2012-12-23', - 'duration': 3600, - 'splits': [ - {'start': '2012-12-11', 'end': '2012-12-12'}, - {'start': '2012-12-18', 'end': '2012-12-19'} - ] - }) + sg.update( + "Task", + 2088, + { + "start_date": "2012-12-06", + "due_date": "2012-12-23", + "duration": 3600, + "splits": [ + {"start": "2012-12-11", "end": "2012-12-12"}, + {"start": "2012-12-18", "end": "2012-12-19"}, + ], + }, + ) # Task = { - # 'start_date': '2012-12-11', - # 'due_date': '2012-12-19', - # 'duration': 2400, - # 'splits': [ - # {'start': '2012-12-11', 'end': '2012-12-12'}, - # {'start': '2012-12-18', 'end': '2012-12-19'} - # ] + # "start_date": "2012-12-11", + # "due_date": "2012-12-19", + # "duration": 2400, + # "splits": [ + # {"start": "2012-12-11", "end": "2012-12-12"}, + # {"start": "2012-12-18", "end": "2012-12-19"}, + # ], # } Result: @@ -100,17 +117,15 @@ Moving the start_date of a split task :: - sg.update('Task', 2088, { - 'start_date': '2012-12-10' - }) + sg.update("Task", 2088, {"start_date": "2012-12-10"}) # Task = { - # 'start_date': '2012-12-10', - # 'due_date': '2012-12-18', - # 'splits': [ - # {'start': '2012-12-10', 'end': '2012-12-11'}, - # {'start': '2012-12-14', 'end': '2012-12-18'} - # ] + # "start_date": "2012-12-10", + # "due_date": "2012-12-18", + # "splits": [ + # {"start": "2012-12-10", "end": "2012-12-11"}, + # {"start": "2012-12-14", "end": "2012-12-18"}, + # ], # } Result: @@ -122,17 +137,15 @@ Moving the due_date of a split task :: - sg.update('Task', 2088, { - 'due_date': '2012-12-19' - }) + sg.update("Task", 2088, {"due_date": "2012-12-19"}) # Task = { - # 'start_date': '2012-12-10', - # 'due_date': '2012-12-19', - # 'splits': [ - # {'start': '2012-12-10', 'end': '2012-12-11'}, - # {'start': '2012-12-14', 'end': '2012-12-19'} - # ] + # "start_date": "2012-12-10", + # "due_date": "2012-12-19", + # "splits": [ + # {"start": "2012-12-10", "end": "2012-12-11"}, + # {"start": "2012-12-14", "end": "2012-12-19"}, + # ], # } Result: @@ -144,18 +157,16 @@ Setting a longer duration :: - sg.update('Task', 2088, { - 'duration': 4200 - }) + sg.update("Task", 2088, {"duration": 4200}) # Task = { - # 'start_date': '2012-12-10', - # 'due_date': '2012-12-21', - # 'duration': 4200, - # 'splits': [ - # {'start': '2012-12-10', 'end': '2012-12-11'}, - # {'start': '2012-12-14', 'end': '2012-12-21'} - # ] + # "start_date": "2012-12-10", + # "due_date": "2012-12-21", + # "duration": 4200, + # "splits": [ + # {"start": "2012-12-10", "end": "2012-12-11"}, + # {"start": "2012-12-14", "end": "2012-12-21"}, + # ], # } Result: @@ -167,18 +178,16 @@ Setting a shorter duration :: - sg.update('Task', 2088, { - 'duration': 2400 - }) + sg.update("Task", 2088, {"duration": 2400}) # Task = { - # 'start_date': '2012-12-10', - # 'due_date': '2012-12-18', - # 'duration': 2400, - # 'splits': [ - # {'start': '2012-12-10', 'end': '2012-12-11'}, - # {'start': '2012-12-14', 'end': '2012-12-18'} - # ] + # "start_date": "2012-12-10", + # "due_date": "2012-12-18", + # "duration": 2400, + # "splits": [ + # {"start": "2012-12-10", "end": "2012-12-11"}, + # {"start": "2012-12-14", "end": "2012-12-18"}, + # ], # } Result: @@ -195,18 +204,16 @@ who's duration we will shorten past the last split. :: - sg.update('Task', 2088, { - 'duration': 1800 - }) + sg.update("Task", 2088, {"duration": 1800}) # Task = { - # 'start_date': '2012-12-10', - # 'due_date': '2012-12-18', - # 'duration': 2400, - # 'splits': [ - # {'start': '2012-12-10', 'end': '2012-12-11'}, - # {'start': '2012-12-14', 'end': '2012-12-18'} - # ] + # "start_date": "2012-12-10", + # "due_date": "2012-12-18", + # "duration": 2400, + # "splits": [ + # {"start": "2012-12-10", "end": "2012-12-11"}, + # {"start": "2012-12-14", "end": "2012-12-18"}, + # ], # } Result: @@ -225,18 +232,16 @@ For this example let's assume as a starting point the result of the 5th example: :: - sg.update('Task', 2088, { - 'due_date': '2012-12-13' - }) + sg.update("Task", 2088, {"due_date": "2012-12-13"}) # Task = { - # 'start_date': '2012-12-10', - # 'due_date': '2012-12-13', - # 'duration': 1800, - # 'splits': [ - # {'start': '2012-12-10', 'end': '2012-12-11'}, - # {'start': '2012-12-13', 'end': '2012-12-13'} - # ] + # "start_date": "2012-12-10", + # "due_date": "2012-12-13", + # "duration": 1800, + # "splits": [ + # {"start": "2012-12-10", "end": "2012-12-11"}, + # {"start": "2012-12-13", "end": "2012-12-13"}, + # ], # } Result: diff --git a/docs/cookbook/tasks/task_dependencies.rst b/docs/cookbook/tasks/task_dependencies.rst index bf6f5e610..89f6c63f3 100644 --- a/docs/cookbook/tasks/task_dependencies.rst +++ b/docs/cookbook/tasks/task_dependencies.rst @@ -17,46 +17,50 @@ Let's create a couple of Tasks and create dependencies between them. First we'll Task for our Shot:: data = { - 'project': {'type':'Project', 'id':65}, - 'content': 'Layout', - 'start_date': '2010-04-28', - 'due_date': '2010-05-05', - 'entity': {'type':'Shot', 'id':860} - } - result = sg.create(Task, data) + "project": {"type": "Project", "id": 65}, + "content": "Layout", + "start_date": "2010-04-28", + "due_date": "2010-05-05", + "entity": {"type": "Shot", "id": 860}, + } + result = sg.create("Task", data) Returns:: - {'content': 'Layout', - 'due_date': '2010-05-05', - 'entity': {'id': 860, 'name': 'bunny_010_0010', 'type': 'Shot'}, - 'id': 556, - 'project': {'id': 65, 'name': 'Demo Animation Project', 'type': 'Project'}, - 'start_date': '2010-04-28', - 'type': 'Task'} + { + "content": "Layout", + "due_date": "2010-05-05", + "entity": {"id": 860, "name": "bunny_010_0010", "type": "Shot"}, + "id": 556, + "project": {"id": 65, "name": "Demo Animation Project", "type": "Project"}, + "start_date": "2010-04-28", + "type": "Task", + } Now let's create an "Anm" Task for our Shot:: data = { - 'project': {'type':'Project', 'id':65}, - 'content': 'Anm', - 'start_date': '2010-05-06', - 'due_date': '2010-05-12', - 'entity': {'type':'Shot', 'id':860} - } - result = sg.create(Task, data) + "project": {"type": "Project", "id": 65}, + "content": "Anm", + "start_date": "2010-05-06", + "due_date": "2010-05-12", + "entity": {"type": "Shot", "id": 860}, + } + result = sg.create("Task", data) Returns:: - {'content': 'Anm', - 'due_date': '2010-05-12', - 'entity': {'id': 860, 'name': 'bunny_010_0010', 'type': 'Shot'}, - 'id': 557, - 'project': {'id': 65, 'name': 'Demo Animation Project', 'type': 'Project'}, - 'start_date': '2010-05-06, - 'type': 'Task'} + { + "content": "Anm", + "due_date": "2010-05-12", + "entity": {"id": 860, "name": "bunny_010_0010", "type": "Shot"}, + "id": 557, + "project": {"id": 65, "name": "Demo Animation Project", "type": "Project"}, + "start_date": "2010-05-06", + "type": "Task", + } ******************* @@ -70,19 +74,29 @@ There are four dependency types from which you can choose: ``finish-to-start-nex If no dependency type is provided the default ``finish-to-start-next-day`` will be used. Here is how to create a dependency between our "Layout" and "Anm" Tasks:: - # make 'Layout' an upstream Task to 'Anm'. (aka, make 'Anm' dependent on 'Layout') with finish-to-start-next-day dependency type + # make "Layout" an upstream Task to "Anm". (aka, make "Anm" dependent on "Layout") with finish-to-start-next-day dependency type data = { - 'upstream_tasks':[{'type':'Task','id':556, 'dependency_type': 'finish-to-start-next-day'}] + "upstream_tasks": [ + { + "type": "Task", + "id": 556, + "dependency_type": "finish-to-start-next-day", + } + ], } - result = sg.update('Task', 557, data) + result = sg.update("Task", 557, data) Returns:: - [{'id': 557, - 'type': 'Task', - 'upstream_tasks': [{'id': 556, 'name': 'Layout', 'type': 'Task'}]}] + [ + { + "id": 557, + "type": "Task", + "upstream_tasks": [{"id": 556, "name": "Layout", "type": "Task"}], + } + ] -This will also automatically update the `downstream_tasks` field on 'Layout' to include the 'Anm' Task. +This will also automatically update the `downstream_tasks` field on "Layout" to include the "Anm" Task. *********************** Query Task Dependencies From cc99aeefcfee572da6d7042d861ffdf159c2faf8 Mon Sep 17 00:00:00 2001 From: Julien Langlois <16244608+julien-lang@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:51:47 -0700 Subject: [PATCH 4/5] Update docs/reference.rst Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference.rst b/docs/reference.rst index 0b3786fac..01b76f2f0 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -814,7 +814,7 @@ Additional keys exist for local file links "name": "string" | None, }, "name": "string", - "relative_path": "string" | None + "relative_path": "string" | None, "url": "string", } API versions < v3.0.3: From 653c65786a9ecd046a97a51377fc1df2bcfdf682 Mon Sep 17 00:00:00 2001 From: Julien Langlois <16244608+julien-lang@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:51:53 -0700 Subject: [PATCH 5/5] Update docs/cookbook/examples/ami_version_packager.rst Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/examples/ami_version_packager.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook/examples/ami_version_packager.rst b/docs/cookbook/examples/ami_version_packager.rst index 429ff3350..739b111dc 100644 --- a/docs/cookbook/examples/ami_version_packager.rst +++ b/docs/cookbook/examples/ami_version_packager.rst @@ -227,7 +227,7 @@ It is intended to be used in conjunction with the script dicussed in :ref:`ami_h for e in entities: if not e[file_field]: logger.info( - "%s #%s: No file exists. Skippinsa." + "%s #%s: No file exists. Skipping." % (sa.params["entity_type"], e["id"]) ) else: