ftrack Python API¶
Welcome to the ftrack Python API documentation.
Important
This is the new Python client for the ftrack API. If you are migrating from the old client then please read the dedicated migration guide.
Introduction¶
This API allows developers to write Python scripts that talk directly with an ftrack server. The scripts can perform operations against that server depending on granted permissions.
With any API it is important to find the right balance between flexibility and usefulness. If an API is too low level then everyone ends up writing boilerplate code for common problems and usually in an non-uniform way making it harder to share scripts with others. It’s also harder to get started with such an API. Conversely, an API that attempts to be too smart can often become restrictive when trying to do more advanced functionality or optimise for performance.
With this API we have tried to strike the right balance between these two, providing an API that should be simple to use out-of-the-box, but also expose more flexibility and power when needed.
Nothing is perfect though, so please do provide feedback on ways that we can continue to improve this API for your specific needs.
Installing¶
Installation is simple with pip:
pip install ftrack-python-api
Building from source¶
You can also build manually from the source for more control. First obtain a copy of the source by either downloading the zipball or cloning the public repository:
git clone git@bitbucket.org:ftrack/ftrack-python-api.git
Then you can build and install the package into your current Python site-packages folder:
python setup.py install
Alternatively, just build locally and manage yourself:
python setup.py build
Building documentation from source¶
To build the documentation from source:
python setup.py build_sphinx
Then view in your browser:
file:///path/to/ftrack-python-api/build/doc/html/index.html
Running tests against the source¶
With a copy of the source it is also possible to run the unit tests:
python setup.py test
Dependencies¶
- ftrack server >= 3.3.11
- Python >= 2.7, < 3
- Requests >= 2, <3,
- Arrow >= 0.4.4, < 1,
- termcolor >= 1.1.0, < 2,
- pyparsing >= 2.0, < 3,
- Clique >= 1.2.0, < 2,
- websocket-client >= 0.40.0, < 1
Additional For building¶
- Sphinx >= 1.2.2, < 2
- sphinx_rtd_theme >= 0.1.6, < 1
- Lowdown >= 0.1.0, < 2
Additional For testing¶
- Pytest >= 2.3.5, < 3
- pytest-mock >= 0.4, < 1,
- pytest-catchlog >= 1, <=2
Tutorial¶
This tutorial provides a quick dive into using the API and the broad stroke concepts involved.
First make sure the ftrack Python API is installed.
Then start a Python session and import the ftrack API:
>>> import ftrack_api
The API uses sessions to manage communication with an ftrack server. Create a session that connects to your ftrack server (changing the passed values as appropriate):
>>> session = ftrack_api.Session(
... server_url='https://mycompany.ftrackapp.com',
... api_key='7545384e-a653-11e1-a82c-f22c11dd25eq',
... api_user='martin'
... )
Note
A session can use environment variables to configure itself.
Now print a list of the available entity types retrieved from the server:
>>> print session.types.keys()
[u'TypedContext', u'ObjectType', u'Priority', u'Project', u'Sequence',
u'Shot', u'Task', u'Status', u'Type', u'Timelog', u'User']
Now the list of possible entity types is known, query the
server to retrieve entities of a particular type by using the
Session.query()
method:
>>> projects = session.query('Project')
Each project retrieved will be an entity instance
that behaves much like a standard Python dictionary. For example, to find out
the available keys for an entity, call the
keys()
method:
>>> print projects[0].keys()
[u'status', u'is_global', u'name', u'end_date', u'context_type',
u'id', u'full_name', u'root', u'start_date']
Now, iterate over the retrieved entities and print each ones name:
>>> for project in projects:
... print project['name']
test
client_review
tdb
man_test
ftrack
bunny
Note
Many attributes for retrieved entities are loaded on demand when the attribute is first accessed. Doing this lots of times in a script can be inefficient, so it is worth using projections in queries or pre-populating entities where appropriate. You can also customise default projections to help others pre-load common attributes.
To narrow a search, add criteria to the query:
>>> active_projects = session.query('Project where status is active')
Combine criteria for more powerful queries:
>>> import arrow
>>>
>>> active_projects_ending_before_next_week = session.query(
... 'Project where status is active and end_date before "{0}"'
... .format(arrow.now().replace(weeks=+1))
... )
Some attributes on an entity will refer to another entity or collection of entities, such as children on a Project being a collection of Context entities that have the project as their parent:
>>> project = session.query('Project').first()
>>> print project['children']
<ftrack_api.collection.Collection object at 0x00000000045B1438>
And on each Context there is a corresponding parent attribute which is a link back to the parent:
>>> child = project['children'][0]
>>> print child['parent'] is project
True
These relationships can also be used in the criteria for a query:
>>> results = session.query(
... 'Context where parent.name like "te%"'
... )
To create new entities in the system use Session.create()
:
>>> new_sequence = session.create('Sequence', {
... 'name': 'Starlord Reveal'
... })
The created entity is not yet persisted to the server, but it is still possible to modify it.
>>> new_sequence['description'] = 'First hero character reveal.'
The sequence also needs a parent. This can be done in one of two ways:
Set the parent attribute on the sequence:
>>> new_sequence['parent'] = project
Add the sequence to a parent’s children attribute:
>>> project['children'].append(new_sequence)
When ready, persist to the server using Session.commit()
:
>>> session.commit()
When finished with a Session
, it is important to close()
it in order to release resources and properly unsubscribe any registered event
listeners. It is also possible to use the session as a context manager in order
to have it closed automatically after use:
>>> with ftrack_api.Session() as session:
... print session.query('User').first()
<User(0154901c-eaf9-11e5-b165-00505681ec7a)>
>>> print session.closed
True
Once a Session
is closed, any operations that attempt to use the closed
connection to the ftrack server will fail:
>>> session.query('Project').first()
ConnectionClosedError: Connection closed.
Continue to the next section to start learning more about the API in greater depth or jump over to the usage examples if you prefer to learn by example.
Understanding sessions¶
All communication with an ftrack server takes place through a Session
.
This allows more opportunity for configuring the connection, plugins etc. and
also makes it possible to connect to multiple ftrack servers from within the
same Python process.
Connection¶
A session can be manually configured at runtime to connect to a server with certain credentials:
>>> session = ftrack_api.Session(
... server_url='https://mycompany.ftrackapp.com',
... api_key='7545384e-a653-11e1-a82c-f22c11dd25eq',
... api_user='martin'
... )
Alternatively, a session can use the following environment variables to configure itself:
When using environment variables, no server connection arguments need to be passed manually:
>>> session = ftrack_api.Session()
Unit of work¶
Each session follows the unit of work pattern. This means that many of the
operations performed using a session will happen locally and only be persisted
to the server at certain times, notably when calling Session.commit()
.
This approach helps optimise calls to the server and also group related logic
together in a transaction:
user = session.create('User', {})
user['username'] = 'martin'
other_user = session.create('User', {'username': 'bjorn'})
other_user['email'] = 'bjorn@example.com'
Behind the scenes a series of operations
are recorded reflecting the changes made. You
can take a peek at these operations if desired by examining the
Session.recorded_operations
property:
>>> for operation in session.recorded_operations:
... print operation
<ftrack_api.operation.CreateEntityOperation object at 0x0000000003EC49B0>
<ftrack_api.operation.UpdateEntityOperation object at 0x0000000003E16898>
<ftrack_api.operation.CreateEntityOperation object at 0x0000000003E16240>
<ftrack_api.operation.UpdateEntityOperation object at 0x0000000003E16128>
Calling Session.commit()
persists all recorded operations to the server
and clears the operation log:
session.commit()
Note
The commit call will optimise operations to be as efficient as possible without breaking logical ordering. For example, a create followed by updates on the same entity will be compressed into a single create.
Queries are special and always issued on demand. As a result, a query may return unexpected results if the relevant local changes have not yet been sent to the server:
>>> user = session.create('User', {'username': 'some_unique_username'})
>>> query = 'User where username is "{0}"'.format(user['username'])
>>> print len(session.query(query))
0
>>> session.commit()
>>> print len(session.query(query))
1
Where possible, query results are merged in with existing data transparently with any local changes preserved:
>>> user = session.query('User').first()
>>> user['email'] = 'me@example.com' # Not yet committed to server.
>>> retrieved = session.query(
... 'User where id is "{0}"'.format(user['id'])
... ).one()
>>> print retrieved['email'] # Displays locally set value.
'me@example.com'
>>> print retrieved is user
True
This is possible due to the smart Caching layer in the session.
Auto-population¶
Another important concept in a session is that of auto-population. By default a session is configured to auto-populate missing attribute values on access. This means that the first time you access an attribute on an entity instance a query will be sent to the server to fetch the value:
user = session.query('User').first()
# The next command will issue a request to the server to fetch the
# 'username' value on demand at this is the first time it is accessed.
print user['username']
Once a value has been retrieved it is cached locally in the session and accessing it again will not issue more server calls:
# On second access no server call is made.
print user['username']
You can control the auto population behaviour of a session by either changing
the Session.auto_populate
attribute on a session or using the provided
context helper Session.auto_populating()
to temporarily change the
setting. When turned off you may see a special
NOT_SET
symbol that represents a value has not yet
been fetched:
>>> with session.auto_populating(False):
... print user['email']
NOT_SET
Whilst convenient for simple scripts, making many requests to the server for each attribute can slow execution of a script. To support optimisation the API includes methods for batch fetching attributes. Read about them in Optimising using projections and Populating entities.
Entity types¶
When a session has successfully connected to the server it will automatically download schema information and create appropriate classes for use. This is important as different servers can support different entity types and configurations.
This information is readily available and useful if you need to check that the entity types you expect are present. Here’s how to print a list of all entity types registered for use in the current API session:
>>> print session.types.keys()
[u'Task', u'Shot', u'TypedContext', u'Sequence', u'Priority',
u'Status', u'Project', u'User', u'Type', u'ObjectType']
Each entity type is backed by a customisable class that further describes the entity type and the attributes that are available.
Hint
If you need to use an isinstance()
check, always go through the
session as the classes are built dynamically:
>>> isinstance(entity, session.types['Project'])
Configuring plugins¶
Plugins are used by the API to extend it with new functionality, such as locations or adding convenience methods to Entity types. In addition to new API functionality, event plugins may also be used for event processing by listening to ftrack update events or adding custom functionality to ftrack by registering actions.
When starting a new Session
either pass the plugins_paths to search
explicitly or rely on the environment variable
FTRACK_EVENT_PLUGIN_PATH
. As each session is independent of others,
you can configure plugins per session.
The paths will be searched for plugins, python files which expose a register function. These functions will be evaluated and can be used extend the API with new functionality, such as locations or actions.
If you do not specify any override then the session will attempt to discover and use the default plugins.
Plugins are discovered using ftrack_api.plugin.discover()
with the
session instance passed as the sole positional argument. Most plugins should
take the form of a mount function that then subscribes to specific events on the session:
def configure_locations(event):
'''Configure locations for session.'''
session = event['data']['session']
# Find location(s) and customise instances.
def register(session):
'''Register plugin with *session*.'''
session.event_hub.subscribe(
'topic=ftrack.api.session.configure-location',
configure_locations
)
Additional keyword arguments can be passed as plugin_arguments to the
Session
on instantiation. These are passed to the plugin register
function if its signature supports them:
# a_plugin.py
def register(session, reticulate_splines=False):
'''Register plugin with *session*.'''
...
# main.py
session = ftrack_api.Session(
plugin_arguments={
'reticulate_splines': True,
'some_other_argument': 42
}
)
See also
Lists of events which you can subscribe to in your plugins are available both for synchronous event published by the python API and asynchronous events published by the server
Quick setup¶
1. Create a directory where plugins will be stored. Place any plugins you want loaded automatically in an API session here.

- Configure the
FTRACK_EVENT_PLUGIN_PATH
to point to the directory.
Detailed setup¶
Start out by creating a directory on your machine where you will store your
plugins. Download example_plugin.py
and place it in the directory.
Open up a terminal window, and ensure that plugin is picked up when instantiating the session and manually setting the plugin_paths:
>>> # Set up basic logging
>>> import logging
>>> logging.basicConfig()
>>> plugin_logger = logging.getLogger('com.example.example-plugin')
>>> plugin_logger.setLevel(logging.DEBUG)
>>>
>>> # Configure the API, loading plugins in the specified paths.
>>> import ftrack_api
>>> plugin_paths = ['/path/to/plugins']
>>> session = ftrack_api.Session(plugin_paths=plugin_paths)
If everything is working as expected, you should see the following in the output:
DEBUG:com.example.example-plugin:Plugin registered
Instead of specifying the plugin paths when instantiating the session, you can
also specify the FTRACK_EVENT_PLUGIN_PATH
to point to the directory.
To specify multiple directories, use the path separator for your operating
system.
Working with entities¶
Entity
instances are Python dict-like
objects whose keys correspond to attributes for that type in the system. They
may also provide helper methods to perform common operations such as replying to
a note:
note = session.query('Note').first()
print note.keys()
print note['content']
note['content'] = 'A different message!'
reply = note.create_reply(...)
Attributes¶
Each entity instance is typed according to its underlying entity type on the server and configured with appropriate attributes. For example, a task will be represented by a Task class and have corresponding attributes. You can customise entity classes to alter attribute access or provide your own helper methods.
To see the available attribute names on an entity use the
keys()
method on the instance:
>>> task = session.query('Task').first()
>>> print task.keys()
['id', 'name', ...]
If you need more information about the type of attribute, examine the
attributes
property on the corresponding class:
>>> for attribute in type(task).attributes:
... print attribute
<ftrack_api.attribute.ScalarAttribute(id) object at 66701296>
<ftrack_api.attribute.ScalarAttribute(name) object at 66702192>
<ftrack_api.attribute.ReferenceAttribute(status) object at 66701240>
<ftrack_api.attribute.CollectionAttribute(timelogs) object at 66701184>
<ftrack_api.attribute.KeyValueMappedCollectionAttribute(metadata) object at 66701632>
...
Notice that there are different types of attribute such as
ScalarAttribute
for plain values or
ReferenceAttribute
for relationships. These
different types are reflected in the behaviour on the entity instance when
accessing a particular attribute by key:
>>> # Scalar
>>> print task['name']
'model'
>>> task['name'] = 'comp'
>>> # Single reference
>>> print task['status']
<Status(e610b180-4e64-11e1-a500-f23c91df25eb)>
>>> new_status = session.query('Status').first()
>>> task['status'] = new_status
>>> # Collection
>>> print task['timelogs']
<ftrack_api.collection.Collection object at 0x00000000040D95C0>
>>> print task['timelogs'][:]
[<dynamic ftrack Timelog object 72322240>, ...]
>>> new_timelog = session.create('Timelog', {...})
>>> task['timelogs'].append(new_timelog)
Bi-directional relationships¶
Some attributes refer to different sides of a bi-directional relationship. In the current version of the API bi-directional updates are not propagated automatically to the other side of the relationship. For example, setting a parent will not update the parent entity’s children collection locally. There are plans to support this behaviour better in the future. For now, after commit, populate the reverse side attribute manually.
Creating entities¶
In order to create a new instance of an entity call Session.create()
passing in the entity type to create and any initial attribute values:
new_user = session.create('User', {'username': 'martin'})
If there are any default values that can be set client side then they will be applied at this point. Typically this will be the unique entity key:
>>> print new_user['id']
170f02a4-6656-4f15-a5cb-c4dd77ce0540
At this point no information has been sent to the server. However, you are free
to continue updating this object
locally until you are ready to persist the changes by calling
Session.commit()
.
If you are wondering about what would happen if you accessed an unset attribute on a newly created entity, go ahead and give it a go:
>>> print new_user['first_name']
NOT_SET
The session knows that it is a newly created entity that has not yet been
persisted so it doesn’t try to fetch any attributes on access even when
session.auto_populate
is turned on.
Updating entities¶
Updating an entity is as simple as modifying the values for specific keys on
the dict-like instance and calling Session.commit()
when ready. The entity
to update can either be a new entity or a retrieved entity:
task = session.query('Task').first()
task['bid'] = 8
Remember that, for existing entities, accessing an attribute will load it from the server automatically. If you are interested in just setting values without first fetching them from the server, turn auto-population off temporarily:
>>> with session.auto_populating(False):
... task = session.query('Task').first()
... task['bid'] = 8
Server side reset of entity attributes or settings.¶
Some entities support resetting of attributes, for example to reset a users api key:
session.reset_remote(
'api_key', entity=session.query('User where username is "test_user"').one()
)
Note
Currently the only attribute possible to reset is ‘api_key’ on the user entity type.
Deleting entities¶
To delete an entity you need an instance of the entity in your session (either
from having created one or retrieving one). Then call Session.delete()
on
the entity and Session.commit()
when ready:
task_to_delete = session.query('Task').first()
session.delete(task_to_delete)
...
session.commit()
Note
Even though the entity is deleted, you will still have access to the local instance and any local data stored on that instance whilst that instance remains in memory.
Keep in mind that some deletions, when propagated to the server, will cause other entities to be deleted also, so you don’t have to worry about deleting an entire hierarchy manually. For example, deleting a Task will also delete all Notes on that task.
Populating entities¶
When an entity is retrieved via Session.query()
or Session.get()
it
will have some attributes prepopulated. The rest are dynamically loaded when
they are accessed. If you need to access many attributes it can be more
efficient to request all those attributes be loaded in one go. One way to do
this is to use a projections in queries.
However, if you have entities that have been passed to you from elsewhere you
don’t have control over the query that was issued to get those entities. In this
case you can you can populate those entities in one go using
Session.populate()
which works exactly like projections in queries do, but operating against known entities:
>>> users = session.query('User')
>>> session.populate(users, 'first_name, last_name')
>>> with session.auto_populating(False): # Turn off for example purpose.
... for user in users:
... print 'Name: {0}'.format(user['first_name'])
... print 'Email: {0}'.format(user['email'])
Name: Martin
Email: NOT_SET
...
Note
You can populate a single or many entities in one call so long as they are all the same entity type.
Entity states¶
Operations on entities are recorded in the session as they happen. At any time you can inspect an entity to determine its current state from those pending operations.
To do this, use ftrack_api.inspection.state()
:
>>> import ftrack_api.inspection
>>> new_user = session.create('User', {})
>>> print ftrack_api.inspection.state(new_user)
CREATED
>>> existing_user = session.query('User').first()
>>> print ftrack_api.inspection.state(existing_user)
NOT_SET
>>> existing_user['email'] = 'martin@example.com'
>>> print ftrack_api.inspection.state(existing_user)
MODIFIED
>>> session.delete(new_user)
>>> print ftrack_api.inspection.state(new_user)
DELETED
Customising entity types¶
Each type of entity in the system is represented in the Python client by a dedicated class. However, because the types of entities can vary these classes are built on demand using schema information retrieved from the server.
Many of the default classes provide additional helper methods which are mixed into the generated class at runtime when a session is started.
In some cases it can be useful to tailor the custom classes to your own pipeline workflows. Perhaps you want to add more helper functions, change attribute access rules or even providing a layer of backwards compatibility for existing code. The Python client was built with this in mind and makes such customisations as easy as possible.
When a Session
is constructed it fetches schema details from the
connected server and then calls an Entity factory
to create classes from those schemas. It
does this by emitting a synchronous event,
ftrack.api.session.construct-entity-type, for each schema and expecting a
class object to be returned.
In the default setup, a construct_entity_type.py
plugin is placed on the
FTRACK_EVENT_PLUGIN_PATH
. This plugin will register a trivial subclass
of ftrack_api.entity.factory.StandardFactory
to create the classes in
response to the construct event. The simplest way to get started is to edit this
default plugin as required.
See also
Default projections¶
When a query is issued without any projections, the session will automatically add default projections according to the type of the entity.
For example, the following shows that for a User, only id is fetched by default when no projections added to the query:
>>> user = session.query('User').first()
>>> with session.auto_populating(False): # For demonstration purpose only.
... print user.items()
[
(u'id', u'59f0963a-15e2-11e1-a5f1-0019bb4983d8')
(u'username', Symbol(NOT_SET)),
(u'first_name', Symbol(NOT_SET)),
...
]
Note
These default projections are also used when you access a relationship attribute using the dictionary key syntax.
If you want to default to fetching username for a Task as well then you can change the default_projections* in your class factory plugin:
class Factory(ftrack_api.entity.factory.StandardFactory):
'''Entity class factory.'''
def create(self, schema, bases=None):
'''Create and return entity class from *schema*.'''
cls = super(Factory, self).create(schema, bases=bases)
# Further customise cls before returning.
if schema['id'] == 'User':
cls.default_projections = ['id', 'username']
return cls
Now a projection-less query will also query username by default:
Note
You will need to start a new session to pick up the change you made:
session = ftrack_api.Session()
>>> user = session.query('User').first()
>>> with session.auto_populating(False): # For demonstration purpose only.
... print user.items()
[
(u'id', u'59f0963a-15e2-11e1-a5f1-0019bb4983d8')
(u'username', u'martin'),
(u'first_name', Symbol(NOT_SET)),
...
]
Note that if any specific projections are applied in a query, those override the default projections entirely. This allows you to also reduce the data loaded on demand:
>>> session = ftrack_api.Session() # Start new session to avoid cache.
>>> user = session.query('select id from User').first()
>>> with session.auto_populating(False): # For demonstration purpose only.
... print user.items()
[
(u'id', u'59f0963a-15e2-11e1-a5f1-0019bb4983d8')
(u'username', Symbol(NOT_SET)),
(u'first_name', Symbol(NOT_SET)),
...
]
Helper methods¶
If you want to add additional helper methods to the constructed classes to better support your pipeline logic, then you can simply patch the created classes in your factory, much like with changing the default projections:
def get_full_name(self):
'''Return full name for user.'''
return '{0} {1}'.format(self['first_name'], self['last_name']).strip()
class Factory(ftrack_api.entity.factory.StandardFactory):
'''Entity class factory.'''
def create(self, schema, bases=None):
'''Create and return entity class from *schema*.'''
cls = super(Factory, self).create(schema, bases=bases)
# Further customise cls before returning.
if schema['id'] == 'User':
cls.get_full_name = get_full_name
return cls
Now you have a new helper method get_full_name on your User entities:
>>> session = ftrack_api.Session() # New session to pick up changes.
>>> user = session.query('User').first()
>>> print user.get_full_name()
Martin Pengelly-Phillips
If you’d rather not patch the existing classes, or perhaps have a lot of helpers
to mixin, you can instead inject your own class as the base class. The only
requirement is that it has the base Entity
class in its ancestor classes:
import ftrack_api.entity.base
class CustomUser(ftrack_api.entity.base.Entity):
'''Represent user.'''
def get_full_name(self):
'''Return full name for user.'''
return '{0} {1}'.format(self['first_name'], self['last_name']).strip()
class Factory(ftrack_api.entity.factory.StandardFactory):
'''Entity class factory.'''
def create(self, schema, bases=None):
'''Create and return entity class from *schema*.'''
# Alter base class for constructed class.
if bases is None:
bases = [ftrack_api.entity.base.Entity]
if schema['id'] == 'User':
bases = [CustomUser]
cls = super(Factory, self).create(schema, bases=bases)
return cls
The resulting effect is the same:
>>> session = ftrack_api.Session() # New session to pick up changes.
>>> user = session.query('User').first()
>>> print user.get_full_name()
Martin Pengelly-Phillips
Note
Your custom class is not the leaf class which will still be a dynamically generated class. Instead your custom class becomes the base for the leaf class:
>>> print type(user).__mro__
(<dynamic ftrack class 'User'>, <dynamic ftrack class 'CustomUser'>, ...)
Querying¶
The API provides a simple, but powerful query language in addition to iterating directly over entity attributes. Using queries can often substantially speed up your code as well as reduce the amount of code written.
A query is issued using Session.query()
and returns a list of matching
entities. The query always has a single target entity type that the query
is built against. This means that you cannot currently retrieve back a list of
different entity types in one query, though using projections does allow retrieving related entities of a different
type in one go.
The syntax for a query is:
select <projections> from <entity type> where <criteria>
However, both the selection of projections and criteria are optional. This means the most basic query is just to fetch all entities of a particular type, such as all projects in the system:
projects = session.query('Project')
A query always returns a QueryResult
instance that
acts like a list with some special behaviour. The main special behaviour is that
the actual query to the server is not issued until you iterate or index into the
query results:
for project in projects:
print project['name']
You can also explicitly call all()
on the
result set:
projects = session.query('Project').all()
Note
This behaviour exists in order to make way for efficient paging and other optimisations in future.
Using criteria to narrow results¶
Often you will have some idea of the entities you want to retrieve. In this case you can optimise your code by not fetching more data than you need. To do this, add criteria to your query:
projects = session.query('Project where status is active')
Each criteria follows the form:
<attribute> <operator> <value>
You can inspect the entity type or instance to find out which attributes are available to filter on for a particular entity type. The list of operators that can be applied and the types of values they expect is listed later on.
Combining criteria¶
Multiple criteria can be applied in a single expression by joining them with
either and
or or
:
projects = session.query(
'Project where status is active and name like "%thrones"'
)
You can use parenthesis to control the precedence when compound criteria are
used (by default and
takes precedence):
projects = session.query(
'Project where status is active and '
'(name like "%thrones" or full_name like "%thrones")'
)
Filtering on relationships¶
Filtering on relationships is also intuitively supported. Simply follow the relationship using a dotted notation:
tasks_in_project = session.query(
'Task where project.id is "{0}"'.format(project['id'])
)
This works even for multiple strides across relationships (though do note that excessive strides can affect performance):
tasks_completed_in_project = session.query(
'Task where project.id is "{0}" and '
'status.type.name is "Done"'
.format(project['id'])
)
The same works for collections (where each entity in the collection is compared against the subsequent condition):
import arrow
tasks_with_time_logged_today = session.query(
'Task where timelogs.start >= "{0}"'.format(arrow.now().floor('day'))
)
In the above query, each Task that has at least one Timelog with a start time greater than the start of today is returned.
When filtering on relationships, the conjunctions has
and any
can be
used to specify how the criteria should be applied. This becomes important when
querying using multiple conditions on collection relationships. The relationship
condition can be written against the following form:
<not?> <relationship> <has|any> (<criteria>)
For optimal performance has
should be used for scalar relationships when
multiple conditions are involved. For example, to find notes by a specific
author when only name is known:
notes_written_by_jane_doe = session.query(
'Note where author has (first_name is "Jane" and last_name is "Doe")'
)
This query could be written without has
, giving the same results:
notes_written_by_jane_doe = session.query(
'Note where author.first_name is "Jane" and author.last_name is "Doe"'
)
any
should be used for collection relationships. For example, to find all
projects that have at least one metadata instance that has key=some_key
and value=some_value the query would be:
projects_where_some_key_is_some_value = session.query(
'Project where metadata any (key=some_key and value=some_value)'
)
If the query was written without any
, projects with one metadata matching
key and another matching the value would be returned.
any
can also be used to query for empty relationship collections:
users_without_timelogs = session.query(
'User where not timelogs any ()'
)
Supported operators¶
This is the list of currently supported operators:
Operators | Description | Example |
---|---|---|
= is | Exactly equal. | name is “martin” |
!= is_not | Not exactly equal. | name is_not “martin” |
> after greater_than | Greater than exclusive. | start after “2015-06-01” |
< before less_than | Less than exclusive. | end before “2015-06-01” |
>= | Greater than inclusive. | bid >= 10 |
<= | Less than inclusive. | bid <= 10 |
in | One of. | status.type.name in (“In Progress”, “Done”) |
not_in | Not one of. | status.name not_in (“Omitted”, “On Hold”) |
like | Matches pattern. | name like “%thrones” |
not_like | Does not match pattern. | name not_like “%thrones” |
has | Test scalar relationship. | author has (first_name is “Jane” and last_name is “Doe”) |
any | Test collection relationship. | metadata any (key=some_key and value=some_value) |
Optimising using projections¶
In Understanding sessions we mentioned auto-population of attribute values on access. This meant that when iterating over a lot of entities and attributes a large number of queries were being sent to the server. Ultimately, this can cause your code to run slowly:
>>> projects = session.query('Project')
>>> for project in projects:
... print(
... # Multiple queries issued here for each attribute accessed for
... # each project in the loop!
... '{project[full_name]} - {project[status][name]})'
... .format(project=project)
... )
Fortunately, there is an easy way to optimise. If you know what attributes you are interested in ahead of time you can include them in your query string as projections in order to fetch them in one go:
>>> projects = session.query(
... 'select full_name, status.name from Project'
... )
>>> for project in projects:
... print(
... # No additional queries issued here as the values were already
... # loaded by the above query!
... '{project[full_name]} - {project[status][name]})'
... .format(project=project)
... )
Notice how this works for related entities as well. In the example above, we also fetched the name of each Status entity attached to a project in the same query, which meant that no further queries had to be issued when accessing those nested attributes.
Note
There are no arbitrary limits to the number (or depth) of projections, but do be aware that excessive projections can ultimately result in poor performance also. As always, it is about choosing the right tool for the job.
You can also customise the Default projections to use for each entity type when none are specified in the query string.
Handling events¶
Events are generated in ftrack when things happen such as a task being updated
or a new version being published. Each Session
automatically connects to the event server and can be used to subscribe to
specific events and perform an action as a result. That action could be updating
another related entity based on a status change or generating folders when a new
shot is created for example.
The EventHub
for each Session
is
accessible via Session.event_hub
.
Subscribing to events¶
To listen to events, you register a function against a subscription using
Session.event_hub.subscribe
. The subscription
uses the expression syntax and will filter
against each Event
instance to determine if the registered
function should receive that event. If the subscription matches, the registered
function will be called with the Event
instance as its sole
argument. The Event
instance is a mapping like structure and can
be used like a normal dictionary.
The following example subscribes a function to receive all ‘ftrack.update’ events and then print out the entities that were updated:
import ftrack_api
def my_callback(event):
'''Event callback printing all new or updated entities.'''
for entity in event['data'].get('entities', []):
# Print data for the entity.
print(entity)
# Subscribe to events with the update topic.
session = ftrack_api.Session()
session.event_hub.subscribe('topic=ftrack.update', my_callback)
At this point, if you run this, your code would exit almost immediately. This
is because the event hub listens for events in a background thread. Typically,
you only want to stay connected whilst using the session, but in some cases you
will want to block and listen for events solely - a dedicated event processor.
To do this, use the EventHub.wait
method:
# Wait for events to be received and handled.
session.event_hub.wait()
You cancel waiting for events by using a system interrupt (Ctrl-C). Alternatively, you can specify a duration to process events for:
# Only wait and process events for 5 seconds.
session.event_hub.wait(duration=5)
Note
Events are continually received and queued for processing in the background
as soon as the connection to the server is established. As a result you may
see a flurry of activity as soon as you call
wait()
for the first time.
Subscriber information¶
When subscribing, you can also specify additional information about your
subscriber. This contextual information can be useful when routing events,
particularly when targeting events. By default, the
EventHub
will set some default information, but it can be
useful to enhance this. To do so, simply pass in subscriber as a dictionary of
data to the subscribe()
method:
session.event_hub.subscribe(
'topic=ftrack.update',
my_callback,
subscriber={
'id': 'my-unique-subscriber-id',
'applicationId': 'maya'
}
)
Sending replies¶
When handling an event it is sometimes useful to be able to send information back to the source of the event. For example, ftrack.location.request-resolve would expect a resolved path to be sent back.
You can craft a custom reply event if you want, but an easier way is just to return the appropriate data from your handler. Any non None value will be automatically sent as a reply:
def on_event(event):
# Send following data in automatic reply.
return {'success': True, 'message': 'Cool!'}
session.event_hub.subscribe('topic=test-reply', on_event)
See also
Note
Some events are published synchronously. In this case, any returned data is passed back to the publisher directly.
Stopping events¶
The event instance passed to each event handler also provides a method for
stopping the event, Event.stop
.
Once an event has been stopped, no further handlers for that specific event will be called locally. Other handlers in other processes may still be called.
Combining this with setting appropriate priorities when subscribing to a topic allows handlers to prevent lower priority handlers running when desired.
>>> import ftrack_api
>>> import ftrack_api.event.base
>>>
>>> def callback_a(event):
... '''Stop the event!'''
... print('Callback A')
... event.stop()
>>>
>>> def callback_b(event):
... '''Never run.'''
... print('Callback B')
>>>
>>> session = ftrack_api.Session()
>>> session.event_hub.subscribe(
... 'topic=test-stop-event', callback_a, priority=10
... )
>>> session.event_hub.subscribe(
... 'topic=test-stop-event', callback_b, priority=20
... )
>>> session.event_hub.publish(
... ftrack_api.event.base.Event(topic='test-stop-event')
... )
>>> session.event_hub.wait(duration=5)
Callback A called.
Publishing events¶
So far we have looked at listening to events coming from ftrack. However, you are also free to publish your own events (or even publish relevant ftrack events).
To do this, simply construct an instance of ftrack_api.event.base.Event
and pass it to EventHub.publish
via the session:
import ftrack_api.event.base
event = ftrack_api.event.base.Event(
topic='my-company.some-topic',
data={'key': 'value'}
)
session.event_hub.publish(event)
The event hub will automatically add some information to your event before it gets published, including the source of the event. By default the event source is just the event hub, but you can customise this to provide more relevant information if you want. For example, if you were publishing from within Maya:
session.event_hub.publish(ftrack_api.event.base.Event(
topic='my-company.some-topic',
data={'key': 'value'},
source={
'applicationId': 'maya'
}
))
Remember that all supplied information can be used by subscribers to filter events so the more accurate the information the better.
Publish synchronously¶
It is also possible to call publish()
synchronously by
passing synchronous=True. In synchronous mode, only local handlers will be
called. The result from each called handler is collected and all the results
returned together in a list:
>>> import ftrack_api
>>> import ftrack_api.event.base
>>>
>>> def callback_a(event):
... return 'A'
>>>
>>> def callback_b(event):
... return 'B'
>>>
>>> session = ftrack_api.Session()
>>> session.event_hub.subscribe(
... 'topic=test-synchronous', callback_a, priority=10
... )
>>> session.event_hub.subscribe(
... 'topic=test-synchronous', callback_b, priority=20
... )
>>> results = session.event_hub.publish(
... ftrack_api.event.base.Event(topic='test-synchronous'),
... synchronous=True
... )
>>> print results
['A', 'B']
Handling replies¶
When publishing an event it is also possible to pass a callable that will be called with any reply event received in response to the published event.
To do so, simply pass in a callable as the on_reply parameter:
def handle_reply(event):
print 'Got reply', event
session.event_hub.publish(
ftrack_api.event.base.Event(topic='test-reply'),
on_reply=handle_reply
)
Targeting events¶
In addition to subscribers filtering events to receive, it is also possible to give an event a specific target to help route it to the right subscriber.
To do this, set the target value on the event to an expression. The expression will filter against registered subscriber information.
For example, if you have many subscribers listening for a event, but only want one of those subscribers to get the event, you can target the event to the subscriber using its registered subscriber id:
session.event_hub.publish(
ftrack_api.event.base.Event(
topic='my-company.topic',
data={'key': 'value'},
target='id=my-custom-subscriber-id'
)
)
Expressions¶
An expression is used to filter against a data structure, returning whether the structure fulfils the expression requirements. Expressions are currently used for subscriptions when subscribing to events and for targets when publishing targeted events.
The form of the expression is loosely groupings of ‘key=value’ with conjunctions to join them.
For example, a common expression for subscriptions is to filter against an event topic:
'topic=ftrack.location.component-added'
However, you can also perform more complex filtering, including accessing nested parameters:
'topic=ftrack.location.component-added and data.locationId=london'
Note
If the structure being tested does not have any value for the specified key reference then it is treated as not matching.
You can also use a single wildcard ‘*’ at the end of any value for matching multiple values. For example, the following would match all events that have a topic starting with ‘ftrack.’:
'topic=ftrack.*'
Caching¶
The API makes use of caching in order to provide more efficient retrieval of data by reducing the number of calls to the remote server:
# First call to retrieve user performs a request to the server.
user = session.get('User', 'some-user-id')
# A later call in the same session to retrieve the same user just gets
# the existing instance from the cache without a request to the server.
user = session.get('User', 'some-user-id')
It also seamlessly merges related data together regardless of how it was retrieved:
>>> timelog = user['timelogs'][0]
>>> with session.auto_populating(False):
>>> print timelog['comment']
NOT_SET
>>> session.query(
... 'select comment from Timelog where id is "{0}"'
... .format(timelog['id'])
... ).all()
>>> with session.auto_populating(False):
>>> print timelog['comment']
'Some comment'
By default, each Session
is configured with a
simple MemoryCache()
and the cache is lost as soon as
the session expires.
Configuring a session cache¶
It is possible to configure the cache that a session uses. An example would be a persistent auto-populated cache that survives between sessions:
import os
import ftrack_api.cache
# Specify where the file based cache should be stored.
cache_path = os.path.join(tempfile.gettempdir(), 'ftrack_session_cache.dbm')
# Define a cache maker that returns a file based cache. Note that a
# function is used because the file based cache should use the session's
# encode and decode methods to serialise the entity data to a format that
# can be written to disk (JSON).
def cache_maker(session):
'''Return cache to use for *session*.'''
return ftrack_api.cache.SerialisedCache(
ftrack_api.cache.FileCache(cache_path),
encode=session.encode,
decode=session.decode
)
# Create the session using the cache maker.
session = ftrack_api.Session(cache=cache_maker)
Note
There can be a performance penalty when using a more complex cache setup. For example, serialising data and also writing and reading from disk can be relatively slow operations.
Regardless of the cache specified, the session will always construct a
LayeredCache
with a
MemoryCache
at the top level and then your cache at
the second level. This is to ensure consistency of instances returned by the
session.
You can check (or even modify) at any time what cache configuration a session is
using by accessing the cache attribute on a
Session
:
>>> print session.cache
<ftrack_api.cache.LayeredCache object at 0x0000000002F64400>
Writing a new cache interface¶
If you have a custom cache backend you should be able to integrate it into the
system by writing a cache interface that matches the one defined by
ftrack_api.cache.Cache
. This typically involves a subclass and
overriding the get()
,
set()
and remove()
methods.
Managing what gets cached¶
The cache system is quite flexible when it comes to controlling what should be cached.
Consider you have a layered cache where the bottom layer cache should be persisted between sessions. In this setup you probably don’t want the persisted cache to hold non-persisted values, such as modified entity values or newly created entities not yet committed to the server. However, you might want the top level memory cache to hold onto these values.
Here is one way to set this up. First define a new proxy cache that is selective about what it sets:
import ftrack_api.inspection
class SelectiveCache(ftrack_api.cache.ProxyCache):
'''Proxy cache that won't cache newly created entities.'''
def set(self, key, value):
'''Set *value* for *key*.'''
if isinstance(value, ftrack_api.entity.base.Entity):
if (
ftrack_api.inspection.state(value)
is ftrack_api.symbol.CREATED
):
return
super(SelectiveCache, self).set(key, value)
Now use this custom cache to wrap the serialised cache in the setup above:
def cache_maker(session):
'''Return cache to use for *session*.'''
return SelectiveCache(
ftrack_api.cache.SerialisedCache(
ftrack_api.cache.FileCache(cache_path),
encode=session.encode,
decode=session.decode
)
)
Now to prevent modified attributes also being persisted, tweak the encode settings for the file cache:
import functools
def cache_maker(session):
'''Return cache to use for *session*.'''
return SelectiveCache(
ftrack_api.cache.SerialisedCache(
ftrack_api.cache.FileCache(cache_path),
encode=functools.partial(
session.encode,
entity_attribute_strategy='persisted_only'
),
decode=session.decode
)
)
And use the updated cache maker for your session:
session = ftrack_api.Session(cache=cache_maker)
Note
For some type of attributes that are computed, long term caching is not recommended and such values will not be encoded with the persisted_only strategy.
Locations¶
Learn how to access locations using the API and configure your own location plugins.
Overview¶
Locations provides a way to easily track and manage data (files, image sequences etc.) using ftrack.
With locations it is possible to see where published data is in the world and also to transfer data automatically between different locations, even different storage mechanisms, by defining a few simple Python plugins. By keeping track of the size of the data it also helps manage storage capacity better. In addition, the intrinsic links to production information makes assigning work to others and transferring only the relevant data much simpler as well as greatly reducing the burden on those responsible for archiving finished work.
Concepts¶
The system is implemented in layers using a few key concepts in order to provide a balance between out of the box functionality and custom configuration.
Locations¶
Data locations can be varied in scope and meaning - a facility, a laptop, a specific drive. As such, rather than place a hard limit on what can be considered a location, ftrack simply requires that a location be identifiable by a string and that string be unique to that location.
A global company with facilities in many different parts of the world might follow a location naming convention similar to the following:
- ‘ftrack.london.server01’
- ‘ftrack.london.server02’
- ‘ftrack.nyc.server01’
- ‘ftrack.amsterdam.server01’
- ‘<company>.<city>.<server#>’
Whereas, for a looser setup, the following might suit better:
- ‘bjorns-workstation’
- ‘fredriks-mobile’
- ‘martins-laptop’
- ‘cloud-backup’
Availability¶
When tracking data across several locations it is important to be able to quickly find out where data is available and where it is not. As such, ftrack provides simple mechanisms for retrieving information on the availability of a component in each location.
For a single file, the availability with be either 0% or 100%. For containers, such as file sequences, each file is tracked separately and the availability of the container calculated as an overall percentage (e.g. 47%).
Accessors¶
Due to the flexibility of what can be considered a location, the system must be able to cope with locations that represent different ways of storing data. For example, data might be stored on a local hard drive, a cloud service or even in a database.
In addition, the method of accessing that storage can change depending on perspective - local filesystem, FTP, S3 API etc.
To handle this, ftrack introduces the idea of an accessor that provides access to the data in a standard way. An accessor is implemented in Python following a set interface and can be configured at runtime to provide relevant access to a location.
With an accessor configured for a location, it becomes possible to not only track data, but also manage it through ftrack by using the accessor to add and remove data from the location.
At present, ftrack includes a disk accessor
for local filesystem access. More will be
added over time and developers are encouraged to contribute their own.
Structure¶
Another important consideration for locations is how data should be structured in the location (folder structure and naming conventions). For example, different facilities may want to use different folder structures, or different storage mechanisms may use different paths for the data.
For this, ftrack supports the use of a Python structure plugin. This plugin is called when adding a component to a location in order to determine the correct structure to use.
Note
A structure plugin accepts an ftrack entity as its input and so can be reused for generating general structures as well. For example, an action callback could be implemented to create the base folder structure for some selected shots by reusing a structure plugin.
Resource identifiers¶
When a component can be linked to multiple locations it becomes necessary to store information about the relationship on the link rather than directly on the component itself. The most important information is the path to the data in that location.
However, as seen above, not all locations may be filesystem based or accessed using standard filesystem protocols. For this reason, and to help avoid confusion, this path is referred to as a resource identifier and no limitations are placed on the format. Keep in mind though that accessors use this information (retrieved from the database) in order to work out how to access the data, so the format used must be compatible with all the accessors used for any one location. For this reason, most resource identifiers should ideally look like relative filesystem paths.
Transformer¶
To further support custom formats for resource identifiers, it is also possible to configure a resource identifier transformer plugin which will convert the identifiers before they are stored centrally and after they are retrieved.
A possible use case of this might be to store JSON encoded metadata about a path in the database and convert this to an actual filesystem path on retrieval.
Tutorial¶
This tutorial is a walkthrough on how you interact with Locations using the ftrack API. Before you read this tutorial, make sure you familiarize yourself with the location concepts by reading the Overview.
All examples assume you are using Python 2.x, have the ftrack_api
module imported and a session
created.
import ftrack_api
session = ftrack_api.Session()
Creating locations¶
Locations can be created just like any other entity using
Session.create
:
location = session.create('Location', dict(name='my.location'))
session.commit()
Note
Location names beginning with ftrack.
are reserved for internal use. Do
not use this prefix for your location names.
To create a location only if it doesn’t already exist use the convenience
method Session.ensure
. This will return
either an existing matching location or a newly created one.
Retrieving locations¶
You can retrieve existing locations using the standard session
get()
and
query()
methods:
# Retrieve location by unique id.
location_by_id = session.get('Location', 'unique-id')
# Retrieve location by name.
location_by_name = session.query(
'Location where name is "my.location"'
).one()
To retrieve all existing locations use a standard query:
all_locations = session.query('Location').all()
for existing_location in all_locations:
print existing_location['name']
Configuring locations¶
At this point you have created a custom location “my.location” in the database and have an instance to reflect that. However, the location cannot be used in this session to manage data unless it has been configured. To configure a location for the session, set the appropriate attributes for accessor and structure:
import tempfile
import ftrack_api.accessor.disk
import ftrack_api.structure.id
# Assign a disk accessor with *temporary* storage
location.accessor = ftrack_api.accessor.disk.DiskAccessor(
prefix=tempfile.mkdtemp()
)
# Assign using ID structure.
location.structure = ftrack_api.structure.id.IdStructure()
# Set a priority which will be used when automatically picking locations.
# Lower number is higher priority.
location.priority = 30
To learn more about how to configure locations automatically in a session, see Configuring locations.
Note
If a location is not configured in a session it can still be used as a standard entity and to find out availability of components
Using components with locations¶
The Locations API tries to use sane defaults to stay out of your way.
When creating components, a location is automatically picked
using Session.pick_location
:
(_, component_path) = tempfile.mkstemp(suffix='.txt')
component_a = session.create_component(path=component_path)
To override, specify a location explicitly:
(_, component_path) = tempfile.mkstemp(suffix='.txt')
component_b = session.create_component(
path=component_path, location=location
)
If you set the location to None
, the component will only be present in the
special origin location for the duration of the session:
(_, component_path) = tempfile.mkstemp(suffix='.txt')
component_c = session.create_component(path=component_path, location=None)
After creating a component in a location, it can be added to another
location by calling Location.add_component
and passing the location to
use as the source location:
origin_location = session.query(
'Location where name is "ftrack.origin"'
).one()
location.add_component(component_c, origin_location)
To remove a component from a location use Location.remove_component
:
location.remove_component(component_b)
Each location specifies whether to automatically manage data when adding or removing components. To ensure that a location does not manage data, mixin the relevant location mixin class before use:
import ftrack_api
import ftrack_api.entity.location
ftrack_api.mixin(location, ftrack_api.entity.location.UnmanagedLocationMixin)
Accessing paths¶
The locations system is designed to help avoid having to deal with filesystem paths directly. This is particularly important when you consider that a number of locations won’t provide any direct filesystem access (such as cloud storage).
However, it is useful to still be able to get a filesystem path from locations
that support them (typically those configured with a
DiskAccessor
). For example, you might need to
pass a filesystem path to another application or perform a copy using a faster
protocol.
To retrieve the path if available, use Location.get_filesystem_path
:
print location.get_filesystem_path(component_c)
Obtaining component availability¶
Components in locations have a notion of availability. For regular components, consisting of a single file, the availability would be either 0 if the component is unavailable or 100 percent if the component is available in the location. Composite components, like image sequences, have an availability which is proportional to the amount of child components that have been added to the location.
For example, an image sequence might currently be in a state of being
transferred to test.location
. If half of the images are transferred, it
might be possible to start working with the sequence. To check availability use
the helper Session.get_component_availability
method:
print session.get_component_availability(component_c)
There are also convenience methods on both components
and locations
for
retrieving availability as well:
print component_c.get_availability()
print location.get_component_availability(component_c)
Location events¶
If you want to receive event notifications when components are added to or
removed from locations, you can subscribe to the topics published,
ftrack_api.symbol.COMPONENT_ADDED_TO_LOCATION_TOPIC
or
ftrack_api.symbol.COMPONENT_REMOVED_FROM_LOCATION_TOPIC
and the callback
you want to be run.
Configuring locations¶
To allow management of data by a location or retrieval of filesystem paths where supported, a location instance needs to be configured in a session with an accessor and structure.
Note
The standard builtin locations require no further setup or configuration and it is not necessary to read the rest of this section to use them.
Before continuing, make sure that you are familiar with the general concepts of locations by reading the Overview.
Configuring manually¶
Locations can be configured manually when using a session by retrieving the location and setting the appropriate attributes:
location = session.query('Location where name is "my.location"').one()
location.structure = ftrack_api.structure.id.IdStructure()
location.priority = 50
Configuring automatically¶
Often the configuration of locations should be determined by developers looking after the core pipeline and so ftrack provides a way for a plugin to be registered to configure the necessary locations for each session. This can then be managed centrally if desired.
The configuration is handled through the standard events system via a topic
ftrack.api.session.configure-location. Set up an event listener plugin as normal with a register function that
accepts a Session
instance. Then register a
callback against the relevant topic to configure locations at the appropriate
time:
import ftrack_api
import ftrack_api.entity.location
import ftrack_api.accessor.disk
import ftrack_api.structure.id
def configure_locations(event):
'''Configure locations for session.'''
session = event['data']['session']
# Find location(s) and customise instances.
location = session.query('Location where name is "my.location"').one()
ftrack_api.mixin(location, ftrack_api.entity.location.UnmanagedLocationMixin)
location.accessor = ftrack_api.accessor.disk.DiskAccessor(prefix='')
location.structure = ftrack_api.structure.id.IdStructure()
location.priority = 50
def register(session):
'''Register plugin with *session*.'''
session.event_hub.subscribe(
'topic=ftrack.api.session.configure-location',
configure_locations
)
Note
If you expect the plugin to also be evaluated by the legacy API, remember to validate the arguments.
So long as the directory containing the plugin exists on your
FTRACK_EVENT_PLUGIN_PATH
, the plugin will run for each session
created and any configured locations will then remain configured for the
duration of that related session.
Be aware that you can configure many locations in one plugin or have separate plugins for different locations - the choice is entirely up to you!
Usage examples¶
The following examples show how to use the API to accomplish specific tasks using the default configuration.
Note
If you are using a server with a customised configuration you may need to alter the examples slightly to make them work correctly.
Most of the examples assume you have the ftrack_api package imported and have
already constructed a Session
:
import ftrack_api
session = ftrack_api.Session()
Working with projects¶
Creating a project¶
A project with sequences, shots and tasks can be created in one single transaction. Tasks need to have a type and status set on creation based on the project schema:
import uuid
# Create a unique name for the project.
name = 'projectname_{0}'.format(uuid.uuid1().hex)
# Naively pick the first project schema. For this example to work the
# schema must contain `Shot` and `Sequence` object types.
project_schema = session.query('ProjectSchema').first()
# Create the project with the chosen schema.
project = session.create('Project', {
'name': name,
'full_name': name + '_full',
'project_schema': project_schema
})
# Retrieve default types.
default_shot_status = project_schema.get_statuses('Shot')[0]
default_task_type = project_schema.get_types('Task')[0]
default_task_status = project_schema.get_statuses(
'Task', default_task_type['id']
)[0]
# Create sequences, shots and tasks.
for sequence_number in range(1, 5):
sequence = session.create('Sequence', {
'name': 'seq_{0}'.format(sequence_number),
'parent': project
})
for shot_number in range(1, 5):
shot = session.create('Shot', {
'name': '{0}0'.format(shot_number).zfill(3),
'parent': sequence,
'status': default_shot_status
})
for task_number in range(1, 5):
session.create('Task', {
'name': 'task_{0}'.format(task_number),
'parent': shot,
'status': default_task_status,
'type': default_task_type
})
# Commit all changes to the server.
session.commit()
Working with components¶
Components can be created manually or using the provide helper methods on a
session
or existing
asset version
:
component = version.create_component('/path/to/file_or_sequence.jpg')
session.commit()
When a component is created using the helpers it is automatically added to a location.
See also
Using review sessions¶
Client review sessions can either be queried manually or by using a project instance.
review_sessions = session.query(
'ReviewSession where name is "Weekly review"'
)
project_review_sessions = project['review_sessions']
To create a new review session on a specific project use Session.create()
.
review_session = session.create('ReviewSession', {
'name': 'Weekly review',
'description': 'See updates from last week.',
'project': project
})
To add objects to a review session create them using
Session.create()
and reference a review session and an asset version.
review_session = session.create('ReviewSessionObject', {
'name': 'Compositing',
'description': 'Fixed shadows.',
'version': 'Version 3',
'review_session': review_session,
'asset_version': asset_version
})
To list all objects in a review session.
review_session_objects = review_session['review_session_objects']
Listing and adding collaborators to review session can be done using
Session.create()
and the review_session_invitees relation on a
review session.
invitee = session.create('ReviewSessionInvitee', {
'name': 'John Doe',
'email': 'john.doe@example.com',
'review_session': review_session
})
session.commit()
invitees = review_session['review_session_invitees']
To remove a collaborator simply delete the object using
Session.delete()
.
session.delete(invitee)
To send out an invite email to a signle collaborator use
Session.send_review_session_invite()
.
session.send_review_session_invite(invitee)
Multiple invitees can have emails sent to them in one batch using
Session.send_review_session_invites()
.
session.send_review_session_invites(a_list_of_invitees)
Using metadata¶
Key/value metadata can be written to entities using the metadata property and also used to query entities.
The metadata property has a similar interface as a dictionary and keys can be printed using the keys method:
>>> print new_sequence['metadata'].keys()
['frame_padding', 'focal_length']
or items:
>>> print new_sequence['metadata'].items()
[('frame_padding': '4'), ('focal_length': '70')]
Read existing metadata:
>>> print new_sequence['metadata']['frame_padding']
'4'
Setting metadata can be done in a few ways where that later one will replace any existing metadata:
new_sequence['metadata']['frame_padding'] = '5'
new_sequence['metadata'] = {
'frame_padding': '4'
}
Entities can also be queried using metadata:
session.query(
'Sequence where metadata any (key is "frame_padding" and value is "4")'
)
Using custom attributes¶
Custom attributes can be written and read from entities using the
custom_attributes
property.
The custom_attributes
property provides a similar interface to a dictionary.
Keys can be printed using the keys method:
>>> task['custom_attributes'].keys()
[u'my_text_field']
or access keys and values as items:
>>> print task['custom_attributes'].items()
[(u'my_text_field', u'some text')]
Read existing custom attribute values:
>>> print task['custom_attributes']['my_text_field']
'some text'
Updating a custom attributes can also be done similar to a dictionary:
task['custom_attributes']['my_text_field'] = 'foo'
To query for tasks with a custom attribute, my_text_field
, you can use the
key from the configuration:
for task in session.query(
'Task where custom_attributes any '
'(key is "my_text_field" and value is "bar")'
):
print task['name']
Limitations¶
Set attributes on new entities¶
There are some limitations that are important to be aware of when creating an entity and updating custom attributes in one commit.
The following code does not work:
task = session.create('Task', {
...
'custom_attributes': {
'my_text_field': 'bar',
},
})
session.commit()
Instead, the entity must be created first, then we can set the custom attributes:
task = session.create('Task', {
...
})
task['custom_attributes']['my_text_field'] = 'bar'
session.commit()
After the commit the remote value is not automatically populated. This will cause an extra query to the server when a custom attribute is accessed:
# This will cause a server query.
print task['custom_attributes']['my_text_field']
Expression attributes¶
Expression attributes are not yet supported and the reported value will always be the non-evaluated expression.
Hierarchical attributes¶
Hierarchical attributes are not yet fully supported in the API. Hierarchical attributes support both read and write, but when read they are not calculated and instead the raw value is returned:
# The hierarchical attribute `my_attribute` is set on Shot but this will not
# be reflected on the children. Instead the raw value is returned.
print shot['custom_attributes']['my_attribute']
'foo'
print task['custom_attributes']['my_attribute']
None
To work around this limitation it is possible to use the legacy api for hierarchical attributes or to manually query the parents for values and use the first value that is set.
Validation¶
Custom attributes are validated on the ftrack server before persisted. The validation will check that the type of the data is correct for the custom attribute.
- number -
int
orfloat
- text -
str
orunicode
- enumerator -
list
- boolean -
bool
- date -
datetime.datetime
ordatetime.date
If the value set is not valid a ftrack_api.exception.ServerError
is
raised with debug information:
shot['custom_attributes']['fstart'] = 'test'
Traceback (most recent call last):
...
ftrack_api.exception.ServerError: Server reported error:
ValidationError(Custom attribute value for "fstart" must be of type number.
Got "test" of type <type 'unicode'>)
Managing custom attribute configurations¶
From the API it is not only possible to read and update custom attributes for entities, but also managing custom attribute configurations.
Existing custom attribute configurations can be queried as
# Print all existing custom attribute configurations.
print session.query('CustomAttributeConfiguration').all()
Use Session.create()
to create a new custom attribute configuration:
# Get the custom attribute type.
custom_attribute_type = session.query(
'CustomAttributeType where name is "text"'
).one()
# Create a custom attribute configuration.
session.create('CustomAttributeConfiguration', {
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Asset version text attribute',
'key': 'asset_version_text_attribute',
'default': 'bar',
'config': json.dumps({'markdown': False})
})
# Persist it to the ftrack instance.
session.commit()
Tip
The example above does not add security roles. This can be done either from System Settings in the ftrack web application, or by following the Security roles example.
Global or project specific¶
A custom attribute can be global or project specific depending on the project_id attribute:
# Create a custom attribute configuration.
session.create('CustomAttributeConfiguration', {
# Set the `project_id` and the custom attribute will only be available
# on `my_project`.
'project_id': my_project['id'],
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Asset version text attribute',
'key': 'asset_version_text_attribute',
'default': 'bar',
'config': json.dumps({'markdown': False})
})
session.commit()
A project specific custom attribute can be changed to a global:
custom_attribute_configuration['project_id'] = None
session.commit()
Changing a global custom attribute configuration to a project specific is not allowed.
Entity types¶
Custom attribute configuration entity types are using a legacy notation. A configuration can have one of the following as entity_type:
task: | Represents TypedContext (Folder, Shot, Sequence, Task, etc.) custom attribute configurations. When setting this as entity_type the object_type_id must be set as well. Creating a text custom attribute for Folder: custom_attribute_type = session.query(
'CustomAttributeType where name is "text"'
).one()
object_type = session.query('ObjectType where name is "Folder"').one()
session.create('CustomAttributeConfiguration', {
'entity_type': 'task',
'object_type_id': object_type['id'],
'type': custom_attribute_type,
'label': 'Foo',
'key': 'foo',
'default': 'bar',
})
session.commit()
Can be associated with a project_id. |
---|---|
show: | Represents Projects custom attribute configurations. Can be associated with a project_id. |
assetversion: | Represents AssetVersion custom attribute configurations. Can be associated with a project_id. |
user: | Represents User custom attribute configurations. Must be global and cannot be associated with a project_id. |
list: | Represents List custom attribute configurations. Can be associated with a project_id. |
asset: | Represents Asset custom attribute configurations. Note Asset custom attributes have limited support in the ftrack web interface. Can be associated with a project_id. |
It is not possible to change type after a custom attribute configuration has been created.
Custom attribute configuration types¶
Custom attributes can be of different data types depending on what type is set in the configuration. Some types requires an extra json encoded config to be set:
text: | A sting type custom attribute. The default value must be either Can be either presented as raw text or markdown formatted in applicaitons which support it. This is configured through a markwdown key: # Get the custom attribute type.
custom_attribute_type = session.query(
'CustomAttributeType where name is "text"'
).one()
# Create a custom attribute configuration.
session.create('CustomAttributeConfiguration', {
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Asset version text attribute',
'key': 'asset_version_text_attribute',
'default': 'bar',
'config': json.dumps({'markdown': False})
})
# Persist it to the ftrack instance.
session.commit()
|
---|---|
boolean: | A boolean type custom attribute. The default value must be a No config is required. |
date: | A date type custom attribute. The default value must be an arrow date - e.g. arrow.Arrow(2017, 2, 8). No config is required. |
enumerator: | An enumerator type custom attribute. The default value must be a list with either The enumerator can either be single or multi select. The config must a json dump of a dictionary containing multiSelect and data. Where multiSelect is True or False and data is a list of options. Each option should be a dictionary containing value and menu, where menu is meant to be used as label in a user interface. Create a custom attribute enumerator: custom_attribute_type = session.query(
'CustomAttributeType where name is "enumerator"'
).first()
session.create('CustomAttributeConfiguration', {
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Enumerator attribute',
'key': 'enumerator_attribute',
'default': ['bar'],
'config': json.dumps({
'multiSelect': True,
'data': json.dumps([
{'menu': 'Foo', 'value': 'foo'},
{'menu': 'Bar', 'value': 'bar'}
])
})
})
session.commit()
|
dynamic enumerator: | An enumerator type where available options are fetched from remote. Created in the same way as enumerator but without data. |
number: | A number custom attribute can be either decimal or integer for presentation. This can be configured through the isdecimal config option: custom_attribute_type = session.query(
'CustomAttributeType where name is "number"'
).first()
session.create('CustomAttributeConfiguration', {
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Number attribute',
'key': 'number_attribute',
'default': 42,
'config': json.dumps({
'isdecimal': True
})
})
session.commit()
|
Changing default¶
It is possible to update the default value of a custom attribute configuration. This will not change the value of any existing custom attributes:
# Change the default value of custom attributes. This will only affect
# newly created entities.
custom_attribute_configuration['default'] = 43
session.commit()
Security roles¶
By default new custom attribute configurations and the entity values are not readable or writable by any security role.
This can be configured through the read_security_roles and write_security_roles attributes:
# Pick random security role.
security_role = session.query('SecurityRole').first()
custom_attribute_type = session.query(
'CustomAttributeType where name is "date"'
).first()
session.create('CustomAttributeConfiguration', {
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Date attribute',
'key': 'date_attribute',
'default': arrow.Arrow(2017, 2, 8),
'write_security_roles': [security_role],
'read_security_roles': [security_role]
})
session.commit()
Note
Setting the correct security role is important and must be changed to whatever security role is appropriate for your configuration and intended purpose.
Custom attribute groups¶
A custom attribute configuration can be categorized using a CustomAttributeGroup:
group = session.query('CustomAttributeGroup').first()
security_role = session.query('SecurityRole').first()
custom_attribute_type = session.query(
'CustomAttributeType where name is "enumerator"'
).first()
session.create('CustomAttributeConfiguration', {
'entity_type': 'assetversion',
'type': custom_attribute_type,
'label': 'Enumerator attribute',
'key': 'enumerator_attribute',
'default': ['bar'],
'config': json.dumps({
'multiSelect': True,
'data': json.dumps([
{'menu': 'Foo', 'value': 'foo'},
{'menu': 'Bar', 'value': 'bar'}
])
}),
'group': group,
'write_security_roles': [security_role],
'read_security_roles': [security_role]
})
session.commit()
See also
Using link attributes¶
The link attribute can be used to retreive the ids and names of the parents of an object. It is particularly useful in cases where the path of an object must be presented in a UI, but can also be used to speedup certain query patterns.
You can use the link attribute on any entity inheriting from a Context or AssetVersion. Here we use it on the Task entity:
task = session.query(
'select link from Task where name is "myTask"'
).first()
print task['link']
It can also be used create a list of parent entities, including the task itself:
entities = []
for item in task['link']:
entities.append(session.get(item['type'], item['id']))
The link attribute is an ordered list of dictionaries containting data of the parents and the item itself. Each dictionary contains the following entries:
- id
- The id of the object and can be used to do a
Session.get()
.- name
- The name of the object.
- type
- The schema id of the object.
A more advanced use-case is to get the parent names and ids of all timelogs for a user:
for timelog in session.query(
'select context.link, start, duration from Timelog '
'where user.username is "john.doe"'
):
print timelog['context']['link'], timelog['start'], timelog['duration']
The attribute is also available from the AssetVersion asset relation:
for asset_version in session.query(
'select link from AssetVersion '
'where user.username is "john.doe"'
):
print asset_version['link']
Using scopes¶
Entities can be queried based on their scopes:
>>> tasks = session.query(
... 'Task where scopes.name is "London"'
... )
Scopes can be read and modified for entities:
>>> scope = session.query(
... 'Scope where name is "London"'
... )[0]
...
... if scope in task['scopes']:
... task['scopes'].remove(scope)
... else:
... task['scopes'].append(scope)
Managing jobs¶
Jobs can be used to display feedback to users in the ftrack web interface when performing long running tasks in the API.
To create a job use Session.create()
:
user = # Get a user from ftrack.
job = session.create('Job', {
'user': user,
'status': 'running'
})
The created job will appear as running in the jobs menu for the specified user. To set a description on the job, add a dictionary containing description as the data key:
Note
In the current version of the API the dictionary needs to be JSON serialised.
import json
job = session.create('Job', {
'user': user,
'status': 'running',
'data': json.dumps({
'description': 'My custom job description.'
})
})
When the long running task has finished simply set the job as completed and continue with the next task.
job['status'] = 'done'
session.commit()
Attachments¶
Job attachments are files that are attached to a job. In the ftrack web interface these attachments can be downloaded by clicking on a job in the Jobs menu.
To get a job’s attachments through the API you can use the job_components relation and then use the ftrack server location to get the download URL:
server_location = session.query(
'Location where name is "ftrack.server"'
).one()
for job_component in job['job_components']:
print 'Download URL: {0}'.format(
server_location.get_url(job_component['component'])
)
To add an attachment to a job you have to add it to the ftrack server location and create a jobComponent:
server_location = session.query(
'Location where name is "ftrack.server"'
).one()
# Create component and name it "My file".
component = session.create_component(
'/path/to/file',
data={'name': 'My file'},
location=server_location
)
# Attach the component to the job.
session.create(
'JobComponent',
{'component_id': component['id'], 'job_id': job['id']}
)
session.commit()
Note
The ftrack web interface does only support downloading one attachment so attaching more than one will have limited support in the web interface.
Using notes¶
Notes can be written on almost all levels in ftrack. To retrieve notes on an entity you can either query them or use the relation called notes:
task = session.query('Task').first()
# Retrieve notes using notes property.
notes_on_task = task['notes']
# Or query them.
notes_on_task = session.query('Note where parent_id is "{}"'.format(
task['id']
))
Note
It’s currently not possible to use the parent property when querying notes or to use the parent property on notes:
task = session.query('Task').first()
# This won't work in the current version of the API.
session.query('Note where parent.id is "{}"'.format(
task['id']
))
# Neither will this.
parent_of_note = note['parent']
To create new notes you can either use the helper method called
create_note()
on any entity that
can have notes or use Session.create()
to create them manually:
user = session.query('User').first()
# Create note using the helper method.
note = task.create_note('My new note', author=user)
# Manually create a note
note = session.create('Note', {
'content': 'My new note',
'author': user
})
task['notes'].append(note)
Replying to an existing note can also be done with a helper method or by
using Session.create()
:
# Create using helper method.
first_note_on_task = task['notes'][0]
first_note_on_task.create_reply('My new reply on note', author=user)
# Create manually
reply = session.create('Note', {
'content': 'My new note',
'author': user
})
first_note_on_task.replies.append(reply)
Notes can have labels. Use the label argument to set labels on the note using the helper method:
label = session.query(
'NoteLabel where name is "External Note"'
).first()
note = task.create_note(
'New note with external category', author=user, labels=[label]
)
Or add labels to notes when creating a note manually:
label = session.query(
'NoteLabel where name is "External Note"'
).first()
note = session.create('Note', {
'content': 'New note with external category',
'author': user
})
session.create('NoteLabelLink', {
'note_id': note['id],
'label_id': label['id']
})
task['notes'].append(note)
Note
Support for labels on notes was added in ftrack server version 4.3. For older versions of the server, NoteCategory can be used instead.
To specify a category when creating a note simply pass a NoteCategory instance to the helper method:
category = session.query(
'NoteCategory where name is "External Note"'
).first()
note = task.create_note(
'New note with external category', author=user, category=category
)
When writing notes you might want to direct the note to someone. This is done by adding users as recipients. If a user is added as a recipient the user will receive notifications and the note will be displayed in their inbox.
To add recipients pass a list of user or group instances to the helper method:
john = session.query('User where username is "john"').one()
animation_group = session.query('Group where name is "Animation"').first()
note = task.create_note(
'Note with recipients', author=user, recipients=[john, animation_group]
)
Attachments¶
Note attachments are files that are attached to a note. In the ftrack web interface these attachments appears next to the note and can be downloaded by the user.
To get a note’s attachments through the API you can use the note_components relation and then use the ftrack server location to get the download URL:
server_location = session.query(
'Location where name is "ftrack.server"'
).one()
for note_component in note['note_components']:
print 'Download URL: {0}'.format(
server_location.get_url(note_component['component'])
)
To add an attachment to a note you have to add it to the ftrack server location and create a NoteComponent:
server_location = session.query(
'Location where name is "ftrack.server"'
).one()
# Create component and name it "My file".
component = session.create_component(
'/path/to/file',
data={'name': 'My file'},
location=server_location
)
# Attach the component to the note.
session.create(
'NoteComponent',
{'component_id': component['id'], 'note_id': note['id']}
)
session.commit()
Using lists¶
Lists can be used to create a collection of asset versions or objects such as tasks. It could be a list of items that should be sent to client, be included in todays review session or items that belong together in way that is different from the project hierarchy.
There are two types of lists, one for asset versions and one for other objects such as tasks.
To create a list use Session.create()
:
user = # Get a user from ftrack.
project = # Get a project from ftrack.
list_category = # Get a list category from ftrack.
asset_version_list = session.create('AssetVersionList', {
'owner': user,
'project': project,
'category': list_category
})
task_list = session.create('TypedContextList', {
'owner': user,
'project': project,
'category': list_category
})
Then add items to the list like this:
asset_version_list['items'].append(asset_version)
task_list['items'].append(task)
And remove items from the list like this:
asset_version_list['items'].remove(asset_version)
task_list['items'].remove(task)
Using timers¶
Timers can be used to track how much time has been spend working on something.
To start a timer for a user:
user = # Get a user from ftrack.
task = # Get a task from ftrack.
user.start_timer(task)
A timer has now been created for that user and should show up in the ftrack web UI.
To stop the currently running timer for a user and create a timelog from it:
user = # Get a user from ftrack.
timelog = user.stop_timer()
Note
Starting a timer when a timer is already running will raise in an exception. Use the force parameter to automatically stop the running timer first.
user.start_timer(task, force=True)
Working with assignments and allocations¶
The API exposes assignments and allocations relationships on objects in the project hierarchy. You can use these to retrieve the allocated or assigned resources, which can be either groups or users.
Allocations can be used to allocate users or groups to a project team, while assignments are more explicit and is used to assign users to tasks. Both assignment and allocations are modelled as Appointment objects, with a type attribute indicating the type of the appoinment.
The following example retrieves all users part of the project team:
# Retrieve a project
project = session.query('Project').first()
# Set to hold all users part of the project team
project_team = set()
# Add all allocated groups and users
for allocation in project['allocations']:
# Resource may be either a group or a user
resource = allocation['resource']
# If the resource is a group, add its members
if isinstance(resource, session.types['Group']):
for membership in resource['memberships']:
user = membership['user']
project_team.add(user)
# The resource is a user, add it.
else:
user = resource
project_team.add(user)
The next example shows how to assign the current user to a task:
# Retrieve a task and the current user
task = session.query('Task').first()
current_user = session.query(
u'User where username is {0}'.format(session.api_user)
).one()
# Create a new Appointment of type assignment.
session.create('Appointment', {
'context': task,
'resource': current_user,
'type': 'assignment'
})
# Finally, persist the new assignment
session.commit()
To list all users assigned to a task, see the following example:
task = session.query('Task').first()
users = session.query(
'select first_name, last_name from User '
'where assignments any (context_id = "{0}")'.format(task['id'])
)
for user in users:
print user['first_name'], user['last_name']
To list the current user’s assigned tasks, see the example below:
assigned_tasks = session.query(
'select link from Task '
'where assignments any (resource.username = "{0}")'.format(session.api_user)
)
for task in assigned_tasks:
print u' / '.join(item['name'] for item in task['link'])
Working with thumbnails¶
Components can be used as thumbnails on various entities, including
Project, Task, AssetVersion and User. To create and set a thumbnail
you can use the helper method
create_thumbnail()
on
any entity that can have a thumbnail:
task = session.get('Task', my_task_id)
thumbnail_component = task.create_thumbnail('/path/to/image.jpg')
It is also possible to set an entity thumbnail by setting its thumbnail relation or thumbnail_id attribute to a component you would like to use as a thumbnail. For a component to be usable as a thumbnail, it should
- Be a FileComponent.
- Exist in the ftrack.server location.
- Be of an appropriate resolution and valid file type.
The following example creates a new component in the server location, and uses that as a thumbnail for a task:
task = session.get('Task', my_task_id)
server_location = session.query(
'Location where name is "ftrack.server"'
).one()
thumbnail_component = session.create_component(
'/path/to/image.jpg',
dict(name='thumbnail'),
location=server_location
)
task['thumbnail'] = thumbnail_component
session.commit()
The next example reuses a version’s thumbnail for the asset parent thumbnail:
asset_version = session.get('AssetVersion', my_asset_version_id)
asset_parent = asset_version['asset']['parent']
asset_parent['thumbnail_id'] = asset_version['thumbnail_id']
session.commit()
Retrieving thumbnail URL¶
To get an URL to a thumbnail, thumbnail_component, which can be used used to download or display the image in an interface, use the following:
import ftrack_api.symbol
server_location = session.get('Location', ftrack_api.symbol.SERVER_LOCATION_ID)
thumbnail_url = server_location.get_thumbnail_url(thumbnail_component)
thumbnail_url_tiny = server_location.get_thumbnail_url(
thumbnail_component, size=100
)
thumbnail_url_large = server_location.get_thumbnail_url(
thumbnail_component, size=500
)
See also
Encoding media¶
Media such as images and video can be encoded by the ftrack server to allow
playing it in the ftrack web interface. Media can be encoded using
ftrack_api.session.Session.encode_media()
which accepts a path to a file
or an existing component in the ftrack.server location.
Here is an example of how to encode a video and read the output:
job = session.encode_media('/PATH/TO/MEDIA')
job_data = json.loads(job['data'])
print 'Source component id', job_data['source_component_id']
print 'Keeping original component', job_data['keep_original']
for output in job_data['output']:
print u'Output component - id: {0}, format: {1}'.format(
output['component_id'], output['format']
)
You can also call the corresponding helper method on an asset version
, to have the
encoded components automatically associated with the version:
job = asset_version.encode_media('/PATH/TO/MEDIA')
It is also possible to get the URL to an encoded component once the job has finished:
job = session.encode_media('/PATH/TO/MEDIA')
# Wait for job to finish.
location = session.query('Location where name is "ftrack.server"').one()
for component in job['job_components']:
print location.get_url(component)
Media can also be an existing component in another location. Before encoding it, the component needs to be added to the ftrack.server location:
location = session.query('Location where name is "ftrack.server"').one()
location.add_component(component)
session.commit()
job = session.encode_media(component)
Using entity links¶
A link can be used to represent a dependency or another relation between two entities in ftrack.
There are two types of entities that can be linked:
- Versions can be linked to other asset versions, where the link entity type is AssetVersionLink.
- Objects like Task, Shot or Folder, where the link entity type is TypedContextLink.
Both AssetVersion and TypedContext objects have the same relations incoming_links and outgoing_links. To list the incoming links to a Shot we can use the relationship incoming_links:
for link in shot['incoming_links']:
print link['from'], link['to']
In the above example link[‘to’] is the shot and link[‘from’] could be an asset build or something else that is linked to the shot. There is an equivalent outgoing_links that can be used to access outgoing links on an object.
To create a new link between objects or asset versions create a new TypedContextLink or AssetVersionLink entity with the from and to properties set. In this example we will link two asset versions:
session.create('AssetVersionLink', {
'from': from_asset_version,
'to': to_asset_version
})
session.commit()
Using asset version link shortcut¶
Links on asset version can also be created by the use of the uses_versions and used_in_versions relations:
rig_version['uses_versions'].append(model_version)
session.commit()
This has the same result as creating the AssetVersionLink entity as in the previous section.
Which versions are using the model can be listed with:
for version in model_version['used_in_versions']:
print '{0} is using {1}'.format(version, model_version)
Publishing for web review¶
Follow the Encoding media example if you want to upload and encode media using ftrack.
If you already have a file encoded in the correct format and want to bypass the built-in encoding in ftrack, you can create the component manually and add it to the ftrack.server location:
# Retrieve or create version.
version = session.query('AssetVersion', 'SOME-ID')
server_location = session.query('Location where name is "ftrack.server"').one()
filepath = '/path/to/local/file.mp4'
component = version.create_component(
path=filepath,
data={
'name': 'ftrackreview-mp4'
},
location=server_location
)
# Meta data needs to contain *frameIn*, *frameOut* and *frameRate*.
component['metadata']['ftr_meta'] = json.dumps({
'frameIn': 0,
'frameOut': 150,
'frameRate': 25
})
component.session.commit()
To publish an image for review the steps are similar:
# Retrieve or create version.
version = session.query('AssetVersion', 'SOME-ID')
server_location = session.query('Location where name is "ftrack.server"').one()
filepath = '/path/to/image.jpg'
component = version.create_component(
path=filepath,
data={
'name': 'ftrackreview-image'
},
location=server_location
)
# Meta data needs to contain *format*.
component['metadata']['ftr_meta'] = json.dumps({
'format': 'image'
})
component.session.commit()
Here is a list of components names and how they should be used:
Component name | Use |
---|---|
ftrackreview-image | Images reviewable in the browser |
ftrackreview-mp4 | H.264/mp4 video reviewable in browser |
ftrackreview-webm | WebM video reviewable in browser |
Note
Make sure to use the pre-defined component names and set the ftr_meta on the components or review will not work.
Publishing versions¶
To know more about publishing and the concepts around publishing, read the ftrack article about publishing.
To publish an asset you first need to get the context where the asset should be published:
# Get a task from a given id.
task = session.get('Task', '423ac382-e61d-4802-8914-dce20c92b740')
And the parent of the task which will be used to publish the asset on:
asset_parent = task['parent']
Then we create an asset and a version on the asset:
asset_type = session.query('AssetType where name is "Geometry"').one()
asset = session.create('Asset', {
'name': 'My asset',
'type': asset_type,
'parent': asset_parent
})
asset_version = session.create('AssetVersion', {
'asset': asset,
'task': task
})
session.commit()
Note
The task is not used as the parent of the asset, instead the task is linked directly to the AssetVersion.
Then when we have a version where we can create the components:
asset_version.create_component(
'/path/to/a/file.mov', location='auto'
)
asset_version.create_component(
'/path/to/a/another-file.mov', location='auto'
)
session.commit()
This will automatically create a new component and add it to the location which has been configured as the first in priority.
Components can also be named and added to a custom location like this:
location = session.query('Location where name is "my-location"')
asset_version.create_component(
'/path/to/a/file.mov',
data={
'name': 'foobar'
},
location=location
)
Working with user security roles¶
The API exposes SecurityRole and UserSecurityRole that can be used to specify who should have access to certain data on different projects.
List all available security roles like this:
security_roles = session.query(
'select name from SecurityRole where type is "PROJECT"'
)
Note
We only query for project roles since those are the ones we can add to a user for certain projects. Other types include API and ASSIGNED. Type API can only be added to global API keys, which is currently not supported via the api and type ASSIGNED only applies to assigned tasks.
To get all security roles from a user we can either use relations like this:
for user_security_role in user['user_security_roles']:
if user_security_role['is_all_projects']:
result_string = 'all projects'
else:
result_string = ', '.join(
[project['full_name'] for project in user_security_role['projects']]
)
print 'User has security role "{0}" which is valid on {1}.'.format(
user_security_role['security_role']['name'],
result_string
)
or query them directly like this:
user_security_roles = session.query(
'UserSecurityRole where user.username is "{0}"'.format(session.api_user)
).all()
User security roles can also be added to a user for all projects like this:
project_manager_role = session.query(
'SecurityRole where name is "Project Manager"'
).one()
session.create('UserSecurityRole', {
'is_all_projects': True,
'user': user,
'security_role': project_manager_role
})
session.commit()
or for certain projects only like this:
projects = session.query(
'Project where full_name is "project1" or full_name is "project2"'
).all()[:]
session.create('UserSecurityRole', {
'user': user,
'security_role': project_manager_role,
'projects': projects
})
session.commit()
Working with Task Templates¶
Task templates can help you organize your workflows by building a collection of tasks to be applied for specific contexts. They can be applied to all Context objects for example Project, Sequences, Shots, etc…
Query task templates¶
Retrive all task templates and there tasks for a project:
project = session.query('Project').first()
for task_template in project['project_schema']['task_templates']:
print('\ntask template: {0}'.format(
task_template['name']
))
for task_type in [t['task_type'] for t in task_template['items']]:
print('\ttask type: {0}'.format(
task_type['name']
))
“Apply” a task template¶
Create all tasks in a random task template directly under the project:
project = session.query('Project').first()
task_template = random.choice(
project['project_schema']['task_templates']
)
for task_type in [t['task_type'] for t in task_template['items']]:
session.create(
'Task', {
'name': task_type['name'],
'type': task_type,
'parent': project
}
)
session.commit()
Sync users with LDAP¶
If ftrack is configured to connect to LDAP you may trigger a
synchronization through the api using the
ftrack_api.session.Session.call()
:
result = session.call([
dict(
action='delayed_job',
job_type='SYNC_USERS_LDAP'
)
])
job = result[0]['data]
You will get a ftrack_api.entity.job.Job instance back which can be used to check the success of the job:
if job.get('status') == 'failed':
# The job failed get the error.
logging.error(job.get('data'))
Invite user¶
Here we create a new user and send them a invitation through mail
Create a new user:
user_email = 'artist@mail.vfx-company.com'
new_user = session.create(
'User', {
'username':user_email,
'email':user_email,
'is_active':True
}
)
session.commit()
Invite our new user:
new_user.send_invite()
API Reference¶
ftrack_api¶
-
ftrack_api.
mixin
(instance, mixin_class, name=None)[source]¶ Mixin mixin_class to instance.
name can be used to specify new class name. If not specified then one will be generated.
ftrack_api.accessor¶
ftrack_api.accessor.base¶
-
class
ftrack_api.accessor.base.
Accessor
[source]¶ Provide data access to a location.
A location represents a specific storage, but access to that storage may vary. For example, both local filesystem and FTP access may be possible for the same storage. An accessor implements these different ways of accessing the same data location.
As different accessors may access the same location, only part of a data path that is commonly understood may be stored in the database. The format of this path should be a contract between the accessors that require access to the same location and is left as an implementation detail. As such, this system provides no guarantee that two different accessors can provide access to the same location, though this is a clear goal. The path stored centrally is referred to as the resource identifier and should be used when calling any of the accessor methods that accept a resource_identifier argument.
-
list
(resource_identifier)[source]¶ Return list of entries in resource_identifier container.
Each entry in the returned list should be a valid resource identifier.
Raise
AccessorResourceNotFoundError
if resource_identifier does not exist orAccessorResourceInvalidError
if resource_identifier is not a container.
-
is_container
(resource_identifier)[source]¶ Return whether resource_identifier refers to a container.
-
is_sequence
(resource_identifier)[source]¶ Return whether resource_identifier refers to a file sequence.
-
remove
(resource_identifier)[source]¶ Remove resource_identifier.
Raise
AccessorResourceNotFoundError
if resource_identifier does not exist.
-
make_container
(resource_identifier, recursive=True)[source]¶ Make a container at resource_identifier.
If recursive is True, also make any intermediate containers.
Should silently ignore existing containers and not recreate them.
-
get_container
(resource_identifier)[source]¶ Return resource_identifier of container for resource_identifier.
Raise
AccessorParentResourceNotFoundError
if container of resource_identifier could not be determined.
-
get_filesystem_path
(resource_identifier)[source]¶ Return filesystem path for resource_identifier.
Raise
AccessorFilesystemPathError
if filesystem path could not be determined from resource_identifier orAccessorUnsupportedOperationError
if retrieving filesystem paths is not supported by this accessor.
-
get_url
(resource_identifier)[source]¶ Return URL for resource_identifier.
Raise
AccessorFilesystemPathError
if URL could not be determined from resource_identifier orAccessorUnsupportedOperationError
if retrieving URL is not supported by this accessor.
-
ftrack_api.accessor.disk¶
-
class
ftrack_api.accessor.disk.
DiskAccessor
(prefix, **kw)[source]¶ Provide disk access to a location.
Expect resource identifiers to refer to relative filesystem paths.
-
__init__
(prefix, **kw)[source]¶ Initialise location accessor.
prefix specifies the base folder for the disk based structure and will be prepended to any path. It should be specified in the syntax of the current OS.
-
list
(resource_identifier)[source]¶ Return list of entries in resource_identifier container.
Each entry in the returned list should be a valid resource identifier.
Raise
AccessorResourceNotFoundError
if resource_identifier does not exist orAccessorResourceInvalidError
if resource_identifier is not a container.
-
is_container
(resource_identifier)[source]¶ Return whether resource_identifier refers to a container.
-
is_sequence
(resource_identifier)[source]¶ Return whether resource_identifier refers to a file sequence.
-
remove
(resource_identifier)[source]¶ Remove resource_identifier.
Raise
AccessorResourceNotFoundError
if resource_identifier does not exist.
-
make_container
(resource_identifier, recursive=True)[source]¶ Make a container at resource_identifier.
If recursive is True, also make any intermediate containers.
-
get_container
(resource_identifier)[source]¶ Return resource_identifier of container for resource_identifier.
Raise
AccessorParentResourceNotFoundError
if container of resource_identifier could not be determined.
-
get_filesystem_path
(resource_identifier)[source]¶ Return filesystem path for resource_identifier.
For example:
>>> accessor = DiskAccessor('my.location', '/mountpoint') >>> print accessor.get_filesystem_path('test.txt') /mountpoint/test.txt >>> print accessor.get_filesystem_path('/mountpoint/test.txt') /mountpoint/test.txt
Raise
ftrack_api.exception.AccessorFilesystemPathError
if filesystem path could not be determined from resource_identifier.
-
get_url
(resource_identifier)¶ Return URL for resource_identifier.
Raise
AccessorFilesystemPathError
if URL could not be determined from resource_identifier orAccessorUnsupportedOperationError
if retrieving URL is not supported by this accessor.
-
remove_container
(resource_identifier)¶ Remove container at resource_identifier.
-
ftrack_api.accessor.server¶
-
class
ftrack_api.accessor.server.
ServerFile
(resource_identifier, session, mode='rb')[source]¶ Representation of a server file.
-
close
()¶ Flush buffers and prevent further access.
-
seek
(offset, whence=0)¶ Move internal pointer by offset.
-
tell
()¶ Return current position of internal pointer.
-
write
(content)¶ Write content at current position.
-
ftrack_api.entity¶
ftrack_api.entity.asset_version¶
-
class
ftrack_api.entity.asset_version.
AssetVersion
(session, data=None, reconstructing=False)[source]¶ Represent asset version.
-
create_component
(path, data=None, location=None)[source]¶ Create a new component from path with additional data
Note
This is a helper method. To create components manually use the standard
Session.create()
method.path can be a string representing a filesystem path to the data to use for the component. The path can also be specified as a sequence string, in which case a sequence component with child components for each item in the sequence will be created automatically. The accepted format for a sequence is ‘{head}{padding}{tail} [{ranges}]’. For example:
'/path/to/file.%04d.ext [1-5, 7, 8, 10-20]'
See also
data should be a dictionary of any additional data to construct the component with (as passed to
Session.create()
). This version is automatically set as the component’s version.If location is specified then automatically add component to that location.
-
encode_media
(media, keep_original='auto')[source]¶ Return a new Job that encode media to make it playable in browsers.
media can be a path to a file or a FileComponent in the ftrack.server location.
The job will encode media based on the file type and job data contains information about encoding in the following format:
{ 'output': [{ 'format': 'video/mp4', 'component_id': 'e2dc0524-b576-11d3-9612-080027331d74' }, { 'format': 'image/jpeg', 'component_id': '07b82a97-8cf9-11e3-9383-20c9d081909b' }], 'source_component_id': 'e3791a09-7e11-4792-a398-3d9d4eefc294', 'keep_original': True }
The output components are associated with the job via the job_components relation.
An image component will always be generated if possible, and will be set as the version’s thumbnail.
The new components will automatically be associated with the version. A server version of 3.3.32 or higher is required for this to function properly.
If media is a file path, a new source component will be created and added to the ftrack server location and a call to
commit()
will be issued. If media is a FileComponent, it will be assumed to be in available in the ftrack.server location.If keep_original is not set, the original media will be kept if it is a FileComponent, and deleted if it is a file path. You can specify True or False to change this behavior.
-
__init__
(session, data=None, reconstructing=False)¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
ftrack_api.entity.base¶
-
class
ftrack_api.entity.base.
DynamicEntityTypeMetaclass
[source]¶ Custom metaclass to customise representation of dynamic classes.
Note
Derive from same metaclass as derived bases to avoid conflicts.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
mro
()¶ Return a type’s method resolution order.
-
register
(subclass)¶ Register a virtual subclass of an ABC.
Returns the subclass, to allow usage as a class decorator.
-
-
class
ftrack_api.entity.base.
Entity
(session, data=None, reconstructing=False)[source]¶ Base class for all entities.
-
entity_type
= 'Entity'¶
-
attributes
= None¶
-
primary_key_attributes
= None¶
-
default_projections
= None¶
-
__init__
(session, data=None, reconstructing=False)[source]¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
items
()[source]¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
merge
(entity, merged=None)[source]¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
ftrack_api.entity.component¶
-
class
ftrack_api.entity.component.
Component
(session, data=None, reconstructing=False)[source]¶ Represent a component.
-
get_availability
(locations=None)[source]¶ Return availability in locations.
If locations is None, all known locations will be checked.
Return a dictionary of {location_id:percentage_availability}
-
__init__
(session, data=None, reconstructing=False)¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
-
class
ftrack_api.entity.component.
CreateThumbnailMixin
[source]¶ Mixin to add create_thumbnail method on entity class.
-
create_thumbnail
(path, data=None)[source]¶ Set entity thumbnail from path.
Creates a thumbnail component using in the ftrack.server location
Session.create_component
The thumbnail component will be created using data if specified. If no component name is given, thumbnail will be used.The file is expected to be of an appropriate size and valid file type.
Note
A
Session.commit
will be automatically issued.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
ftrack_api.entity.factory¶
-
class
ftrack_api.entity.factory.
Factory
[source]¶ Entity class factory.
-
create
(schema, bases=None)[source]¶ Create and return entity class from schema.
bases should be a list of bases to give the constructed class. If not specified, default to
ftrack_api.entity.base.Entity
.
-
create_scalar_attribute
(class_name, name, mutable, computed, default, data_type)[source]¶ Return appropriate scalar attribute instance.
-
create_reference_attribute
(class_name, name, mutable, reference)[source]¶ Return appropriate reference attribute instance.
-
-
class
ftrack_api.entity.factory.
PerSessionDefaultKeyMaker
[source]¶ Generate key for defaults.
-
__init__
()¶ Initialise key maker.
-
key
(*items)¶ Return key for items.
-
-
ftrack_api.entity.factory.
memoise_session
(function)¶ Memoiser for use with callables that should be called once per session.
-
class
ftrack_api.entity.factory.
StandardFactory
[source]¶ Standard entity class factory.
-
create_mapped_collection_attribute
(class_name, name, mutable, reference)[source]¶ Return appropriate mapped collection attribute instance.
-
__init__
()¶ Initialise factory.
-
create_collection_attribute
(class_name, name, mutable)¶ Return appropriate collection attribute instance.
-
create_reference_attribute
(class_name, name, mutable, reference)¶ Return appropriate reference attribute instance.
-
create_scalar_attribute
(class_name, name, mutable, computed, default, data_type)¶ Return appropriate scalar attribute instance.
-
ftrack_api.entity.job¶
-
class
ftrack_api.entity.job.
Job
(session, data=None, reconstructing=False)[source]¶ Represent job.
-
__init__
(session, data=None, reconstructing=False)[source]¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
To set a job description visible in the web interface, data can contain a key called data which should be a JSON serialised dictionary containing description:
data = { 'status': 'running', 'data': json.dumps(dict(description='My job description.')), ... }
Will raise a
ValueError
if data contains type and type is set to something not equal to “api_job”.reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
ftrack_api.entity.location¶
-
class
ftrack_api.entity.location.
Location
(session, data=None, reconstructing=False)[source]¶ Represent storage for components.
-
__init__
(session, data=None, reconstructing=False)[source]¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
add_component
(component, source, recursive=True)[source]¶ Add component to location.
component should be a single component instance.
source should be an instance of another location that acts as the source.
Raise
ftrack_api.ComponentInLocationError
if the component already exists in this location.Raise
ftrack_api.LocationError
if managing data and the generated target structure for the component already exists according to the accessor. This helps prevent potential data loss by avoiding overwriting existing data. Note that there is a race condition between the check and the write so if another process creates data at the same target during that period it will be overwritten.Note
A
Session.commit
may be automatically issued as part of the component registration.
-
add_components
(components, sources, recursive=True, _depth=0)[source]¶ Add components to location.
components should be a list of component instances.
sources may be either a single source or a list of sources. If a list then each corresponding index in sources will be used for each component. A source should be an instance of another location.
Raise
ftrack_api.exception.ComponentInLocationError
if any component in components already exists in this location. In this case, no changes will be made and no data transferred.Raise
ftrack_api.exception.LocationError
if managing data and the generated target structure for the component already exists according to the accessor. This helps prevent potential data loss by avoiding overwriting existing data. Note that there is a race condition between the check and the write so if another process creates data at the same target during that period it will be overwritten.Note
A
Session.commit
may be automatically issued as part of the components registration.Important
If this location manages data then the components data is first transferred to the target prescribed by the structure plugin, using the configured accessor. If any component fails to transfer then
ftrack_api.exception.LocationError
is raised and none of the components are registered with the database. In this case it is left up to the caller to decide and act on manually cleaning up any transferred data using the ‘transferred’ detail in the raised error.Likewise, after transfer, all components are registered with the database in a batch call. If any component causes an error then all components will remain unregistered and
ftrack_api.exception.LocationError
will be raised detailing issues and any transferred data under the ‘transferred’ detail key.
-
remove_component
(component, recursive=True)[source]¶ Remove component from location.
Note
A
Session.commit
may be automatically issued as part of the component deregistration.
-
remove_components
(components, recursive=True)[source]¶ Remove components from location.
Note
A
Session.commit
may be automatically issued as part of the components deregistration.
-
get_component_availability
(component)[source]¶ Return availability of component in this location as a float.
-
get_component_availabilities
(components)[source]¶ Return availabilities of components in this location.
Return list of float values corresponding to each component.
-
get_resource_identifier
(component)[source]¶ Return resource identifier for component.
Raise
ftrack_api.exception.ComponentNotInLocationError
if the component is not present in this location.
-
get_resource_identifiers
(components)[source]¶ Return resource identifiers for components.
Raise
ftrack_api.exception.ComponentNotInLocationError
if any of the components are not present in this location.
-
get_url
(component)[source]¶ Return url for component.
Raise
AccessorFilesystemPathError
if URL could not be determined from component orAccessorUnsupportedOperationError
if retrieving URL is not supported by the location’s accessor.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
-
class
ftrack_api.entity.location.
MemoryLocationMixin
[source]¶ Represent storage for components.
Unlike a standard location, only store metadata for components in this location in memory rather than persisting to the database.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
-
class
ftrack_api.entity.location.
UnmanagedLocationMixin
[source]¶ Location that does not manage data.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
-
class
ftrack_api.entity.location.
OriginLocationMixin
[source]¶ Special origin location that expects sources as filepaths.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
-
class
ftrack_api.entity.location.
ServerLocationMixin
[source]¶ Location representing ftrack server.
Adds convenience methods to location, specific to ftrack server.
-
get_thumbnail_url
(component, size=None)[source]¶ Return thumbnail url for component.
Optionally, specify size to constrain the downscaled image to size x size pixels.
Raise
AccessorFilesystemPathError
if URL could not be determined from resource_identifier orAccessorUnsupportedOperationError
if retrieving URL is not supported by the location’s accessor.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
ftrack_api.entity.note¶
-
class
ftrack_api.entity.note.
Note
(session, data=None, reconstructing=False)[source]¶ Represent a note.
-
create_reply
(content, author)[source]¶ Create a reply with content and author.
Note
This is a helper method. To create replies manually use the standard
Session.create()
method.
-
__init__
(session, data=None, reconstructing=False)¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
-
class
ftrack_api.entity.note.
CreateNoteMixin
[source]¶ Mixin to add create_note method on entity class.
-
create_note
(content, author, recipients=None, category=None, labels=None)[source]¶ Create note with content, author.
NoteLabels can be set by including labels.
Note category can be set by including category.
recipients can be specified as a list of user or group instances.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
ftrack_api.entity.project_schema¶
-
class
ftrack_api.entity.project_schema.
ProjectSchema
(session, data=None, reconstructing=False)[source]¶ Class representing ProjectSchema.
-
get_statuses
(schema, type_id=None)[source]¶ Return statuses for schema and optional type_id.
type_id is the id of the Type for a TypedContext and can be used to get statuses where the workflow has been overridden.
-
__init__
(session, data=None, reconstructing=False)¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
ftrack_api.entity.user¶
-
class
ftrack_api.entity.user.
User
(session, data=None, reconstructing=False)[source]¶ Represent a user.
-
start_timer
(context=None, comment='', name=None, force=False)[source]¶ Start a timer for context and return it.
force can be used to automatically stop an existing timer and create a timelog for it. If you need to get access to the created timelog, use
stop_timer()
instead.comment and name are optional but will be set on the timer.
Note
This method will automatically commit the changes and if force is False then it will fail with a
ftrack_api.exception.NotUniqueError
exception if a timer is already running.
-
stop_timer
()[source]¶ Stop the current timer and return a timelog created from it.
If a timer is not running, a
ftrack_api.exception.NoResultFoundError
exception will be raised.Note
This method will automatically commit the changes.
-
__init__
(session, data=None, reconstructing=False)¶ Initialise entity.
session is an instance of
ftrack_api.session.Session
that this entity instance is bound to.data is a mapping of key, value pairs to apply as initial attribute values.
reconstructing indicates whether this entity is being reconstructed, such as from a query, and therefore should not have any special creation logic applied, such as initialising defaults for missing data.
-
attributes
= None¶
-
clear
()¶ Reset all locally modified attribute values.
-
default_projections
= None¶
-
entity_type
= 'Entity'¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
()¶ Return list of tuples of (key, value) pairs.
Note
Will fetch all values from the server if not already fetched or set locally.
-
keys
() → a set-like object providing a view on D's keys¶
-
merge
(entity, merged=None)¶ Merge entity attribute values and other data into this entity.
Only merge values from entity that are not
ftrack_api.symbol.NOT_SET
.Return a list of changes made with each change being a mapping with the keys:
- type - Either ‘remote_attribute’, ‘local_attribute’ or ‘property’.
- name - The name of the attribute / property modified.
- old_value - The previous value.
- new_value - The new merged value.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
primary_key_attributes
= None¶
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
()¶ Return list of values.
-
ftrack_api.event¶
ftrack_api.event.base¶
-
class
ftrack_api.event.base.
Event
(topic, id=None, data=None, sent=None, source=None, target='', in_reply_to_event=None)[source]¶ Represent a single event.
-
__init__
(topic, id=None, data=None, sent=None, source=None, target='', in_reply_to_event=None)[source]¶ Initialise event.
topic is the required topic for the event. It can use a dotted notation to demarcate groupings. For example, ‘ftrack.update’.
id is the unique id for this event instance. It is primarily used when replying to an event. If not supplied a default uuid based value will be used.
data refers to event specific data. It should be a mapping structure and defaults to an empty dictionary if not supplied.
sent is the timestamp the event is sent. It will be set automatically as send time unless specified here.
source is information about where the event originated. It should be a mapping and include at least a unique id value under an ‘id’ key. If not specified, senders usually populate the value automatically at publish time.
target can be an expression that targets this event. For example, a reply event would target the event to the sender of the source event. The expression will be tested against subscriber information only.
in_reply_to_event is used when replying to an event and should contain the unique id of the event being replied to.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
ftrack_api.event.expression¶
-
class
ftrack_api.event.expression.
Parser
[source]¶ Parse string based expression into
Expression
instance.-
parse
(expression)[source]¶ Parse string expression into
Expression
.Raise
ftrack_api.exception.ParseError
if expression could not be parsed.
-
-
class
ftrack_api.event.expression.
Expression
[source]¶ Represent a structured expression to test candidates against.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
-
class
ftrack_api.event.expression.
All
(expressions=None)[source]¶ Match candidate that matches all of the specified expressions.
Note
If no expressions are supplied then will always match.
-
class
ftrack_api.event.expression.
Any
(expressions=None)[source]¶ Match candidate that matches any of the specified expressions.
Note
If no expressions are supplied then will never match.
-
class
ftrack_api.event.expression.
Condition
(key, operator, value)[source]¶ Represent condition.
-
__init__
(key, operator, value)[source]¶ Initialise condition.
key is the key to check on the data when matching. It can be a nested key represented by dots. For example, ‘data.eventType’ would attempt to match candidate[‘data’][‘eventType’]. If the candidate is missing any of the requested keys then the match fails immediately.
operator is the operator function to use to perform the match between the retrieved candidate value and the conditional value.
If value is a string, it can use a wildcard ‘*’ at the end to denote that any values matching the substring portion are valid when matching equality only.
-
ftrack_api.event.hub¶
-
class
ftrack_api.event.hub.
SocketIoSession
(id, heartbeatTimeout, supportedTransports)¶ -
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
count
()¶ Return number of occurrences of value.
-
heartbeatTimeout
¶ Alias for field number 1
-
id
¶ Alias for field number 0
-
index
()¶ Return first index of value.
Raises ValueError if the value is not present.
-
supportedTransports
¶ Alias for field number 2
-
-
class
ftrack_api.event.hub.
ServerDetails
(scheme, hostname, port)¶ -
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
count
()¶ Return number of occurrences of value.
-
hostname
¶ Alias for field number 1
-
index
()¶ Return first index of value.
Raises ValueError if the value is not present.
-
port
¶ Alias for field number 2
-
scheme
¶ Alias for field number 0
-
-
class
ftrack_api.event.hub.
EventHub
(server_url, api_user, api_key)[source]¶ Manage routing of events.
-
__init__
(server_url, api_user, api_key)[source]¶ Initialise hub, connecting to ftrack server_url.
api_user is the user to authenticate as and api_key is the API key to authenticate with.
-
secure
¶ Return whether secure connection used.
-
connect
()[source]¶ Initialise connection to server.
Raise
ftrack_api.exception.EventHubConnectionError
if already connected or connection fails.
-
connected
¶ Return if connected.
-
disconnect
(unsubscribe=True)[source]¶ Disconnect from server.
Raise
ftrack_api.exception.EventHubConnectionError
if not currently connected.If unsubscribe is True then unsubscribe all current subscribers automatically before disconnecting.
-
reconnect
(attempts=10, delay=5)[source]¶ Reconnect to server.
Make attempts number of attempts with delay in seconds between each attempt.Note
All current subscribers will be automatically resubscribed after successful reconnection.
Raise
ftrack_api.exception.EventHubConnectionError
if fail to reconnect.
-
wait
(duration=None)[source]¶ Wait for events and handle as they arrive.
If duration is specified, then only process events until duration is reached. duration is in seconds though float values can be used for smaller values.
-
get_subscriber_by_identifier
(identifier)[source]¶ Return subscriber with matching identifier.
Return None if no subscriber with identifier found.
-
subscribe
(subscription, callback, subscriber=None, priority=100)[source]¶ Register callback for subscription.
A subscription is a string that can specify in detail which events the callback should receive. The filtering is applied against each event object. Nested references are supported using ‘.’ separators. For example, ‘topic=foo and data.eventType=Shot’ would match the following event:
<Event {'topic': 'foo', 'data': {'eventType': 'Shot'}}>
The callback should accept an instance of
ftrack_api.event.base.Event
as its sole argument.Callbacks are called in order of priority. The lower the priority number the sooner it will be called, with 0 being the first. The default priority is 100. Note that priority only applies against other callbacks registered with this hub and not as a global priority.
An earlier callback can prevent processing of subsequent callbacks by calling
Event.stop()
on the passed event before returning.Warning
Handlers block processing of other received events. For long running callbacks it is advisable to delegate the main work to another process or thread.
A callback can be attached to subscriber information that details the subscriber context. A subscriber context will be generated automatically if not supplied.
Note
The subscription will be stored locally, but until the server receives notification of the subscription it is possible the callback will not be called.
Return subscriber identifier.
Raise
ftrack_api.exception.NotUniqueError
if a subscriber with the same identifier already exists.
-
unsubscribe
(subscriber_identifier)[source]¶ Unsubscribe subscriber with subscriber_identifier.
Note
If the server is not reachable then it won’t be notified of the unsubscription. However, the subscriber will be removed locally regardless.
-
publish
(event, synchronous=False, on_reply=None, on_error='raise')[source]¶ Publish event.
If synchronous is specified as True then this method will wait and return a list of results from any called callbacks.
Note
Currently, if synchronous is True then only locally registered callbacks will be called and no event will be sent to the server. This may change in future.
on_reply is an optional callable to call with any reply event that is received in response to the published event.
Note
Will not be called when synchronous is True.
If on_error is set to ‘ignore’ then errors raised during publish of event will be caught by this method and ignored.
-
ftrack_api.event.subscriber¶
ftrack_api.event.subscription¶
ftrack_api.resource_identifier_transformer¶
ftrack_api.resource_identifier_transformer.base¶
-
class
ftrack_api.resource_identifier_transformer.base.
ResourceIdentifierTransformer
(session)[source]¶ Transform resource identifiers.
Provide ability to modify resource identifier before it is stored centrally (
encode()
), or after it has been retrieved, but before it is used locally (decode()
).For example, you might want to decompose paths into a set of key, value pairs to store centrally and then compose a path from those values when reading back.
Note
This is separate from any transformations an
ftrack_api.accessor.base.Accessor
may perform and is targeted towards common transformations.-
__init__
(session)[source]¶ Initialise resource identifier transformer.
session should be the
ftrack_api.session.Session
instance to use for communication with the server.
-
ftrack_api.structure¶
ftrack_api.structure.base¶
ftrack_api.structure.id¶
-
class
ftrack_api.structure.id.
IdStructure
(prefix='')[source]¶ Id based structure supporting Components only.
A components unique id will be used to form a path to store the data at. To avoid millions of entries in one directory each id is chunked into four prefix directories with the remainder used to name the file:
/prefix/1/2/3/4/56789
If the component has a defined filetype it will be added to the path:
/prefix/1/2/3/4/56789.exr
Components that are children of container components will be placed inside the id structure of their parent:
/prefix/1/2/3/4/56789/355827648d.exr /prefix/1/2/3/4/56789/ajf24215b5.exr
However, sequence children will be named using their label as an index and a common prefix of ‘file.’:
/prefix/1/2/3/4/56789/file.0001.exr /prefix/1/2/3/4/56789/file.0002.exr
-
get_resource_identifier
(entity, context=None)[source]¶ Return a resource identifier for supplied entity.
context can be a mapping that supplies additional information.
-
__init__
(prefix='')¶ Initialise structure.
-
ftrack_api.structure.origin¶
-
class
ftrack_api.structure.origin.
OriginStructure
(prefix='')[source]¶ Origin structure that passes through existing resource identifier.
-
get_resource_identifier
(entity, context=None)[source]¶ Return a resource identifier for supplied entity.
context should be a mapping that includes at least a ‘source_resource_identifier’ key that refers to the resource identifier to pass through.
-
__init__
(prefix='')¶ Initialise structure.
-
ftrack_api.structure.standard¶
-
class
ftrack_api.structure.standard.
StandardStructure
(project_versions_prefix=None, illegal_character_substitute='_')[source]¶ Project hierarchy based structure that only supports Components.
The resource identifier is generated from the project code, the name of objects in the project structure, asset name and version number:
my_project/folder_a/folder_b/asset_name/v003
If the component is a FileComponent then the name of the component and the file type are used as filename in the resource_identifier:
my_project/folder_a/folder_b/asset_name/v003/foo.jpg
If the component is a SequenceComponent then a sequence expression, %04d, is used. E.g. a component with the name foo yields:
my_project/folder_a/folder_b/asset_name/v003/foo.%04d.jpg
For the member components their index in the sequence is used:
my_project/folder_a/folder_b/asset_name/v003/foo.0042.jpg
The name of the component is added to the resource identifier if the component is a ContainerComponent. E.g. a container component with the name bar yields:
my_project/folder_a/folder_b/asset_name/v003/bar
For a member of that container the file name is based on the component name and file type:
my_project/folder_a/folder_b/asset_name/v003/bar/baz.pdf
-
__init__
(project_versions_prefix=None, illegal_character_substitute='_')[source]¶ Initialise structure.
If project_versions_prefix is defined, insert after the project code for versions published directly under the project:
my_project/<project_versions_prefix>/v001/foo.jpg
Replace illegal characters with illegal_character_substitute if defined.
Note
Nested component containers/sequences are not supported.
-
sanitise_for_filesystem
(value)[source]¶ Return value with illegal filesystem characters replaced.
An illegal character is one that is not typically valid for filesystem usage, such as non ascii characters, or can be awkward to use in a filesystem, such as spaces. Replace these characters with the character specified by illegal_character_substitute on initialisation. If no character was specified as substitute then return value unmodified.
-
get_resource_identifier
(entity, context=None)[source]¶ Return a resource identifier for supplied entity.
context can be a mapping that supplies additional information, but is unused in this implementation.
Raise a
ftrack_api.exeption.StructureError
if entity is not attached to a committed version and a committed asset with a parent context.
-
ftrack_api.attribute¶
-
ftrack_api.attribute.
merge_references
(function)[source]¶ Decorator to handle merging of references / collections.
-
class
ftrack_api.attribute.
Attributes
(attributes=None)[source]¶ Collection of properties accessible by name.
-
class
ftrack_api.attribute.
Attribute
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)[source]¶ A name and value pair persisted remotely.
-
__init__
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)[source]¶ Initialise attribute with name.
default_value represents the default value for the attribute. It may be a callable. It is not used within the attribute when providing values, but instead exists for other parts of the system to reference.
If mutable is set to False then the local value of the attribute on an entity can only be set when both the existing local and remote values are
ftrack_api.symbol.NOT_SET
. The exception to this is when the target value is alsoftrack_api.symbol.NOT_SET
.If computed is set to True the value is a remote side computed value and should not be long-term cached.
-
name
¶ Return name.
-
mutable
¶ Return whether attribute is mutable.
-
computed
¶ Return whether attribute is computed.
-
get_value
(entity)[source]¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
-
get_remote_value
(entity)[source]¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
set_remote_value
(entity, value)[source]¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
-
class
ftrack_api.attribute.
ScalarAttribute
(name, data_type, **kw)[source]¶ Represent a scalar value.
-
computed
¶ Return whether attribute is computed.
-
get_entity_storage
(entity)¶ Return attribute storage on entity creating if missing.
-
get_local_value
(entity)¶ Return locally set value for entity.
-
get_remote_value
(entity)¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
get_value
(entity)¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
-
is_modified
(entity)¶ Return whether local value set and differs from remote.
Note
Will not fetch remote value so may report True even when values are the same on the remote.
-
is_set
(entity)¶ Return whether a value is set for entity.
-
mutable
¶ Return whether attribute is mutable.
-
name
¶ Return name.
-
populate_remote_value
(entity)¶ Populate remote value for entity.
-
set_local_value
(entity, value)¶ Set local value for entity.
-
set_remote_value
(entity, value)¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
-
class
ftrack_api.attribute.
ReferenceAttribute
(name, entity_type, **kw)[source]¶ Reference another entity.
-
populate_remote_value
(entity)[source]¶ Populate remote value for entity.
As attribute references another entity, use that entity’s configured default projections to auto populate useful attributes when loading.
-
is_modified
(entity)[source]¶ Return whether a local value has been set and differs from remote.
Note
Will not fetch remote value so may report True even when values are the same on the remote.
-
get_value
(entity)[source]¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
-
computed
¶ Return whether attribute is computed.
-
get_entity_storage
(entity)¶ Return attribute storage on entity creating if missing.
-
get_local_value
(entity)¶ Return locally set value for entity.
-
get_remote_value
(entity)¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
is_set
(entity)¶ Return whether a value is set for entity.
-
mutable
¶ Return whether attribute is mutable.
-
name
¶ Return name.
-
set_local_value
(entity, value)¶ Set local value for entity.
-
set_remote_value
(entity, value)¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
-
class
ftrack_api.attribute.
AbstractCollectionAttribute
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)[source]¶ Base class for collection attributes.
-
collection_class
= None¶ Collection class used by attribute.
-
get_value
(entity)[source]¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
Note
As value is a collection that is mutable, will transfer a remote value into the local value on access if no local value currently set.
-
set_remote_value
(entity, value)[source]¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
__init__
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)¶ Initialise attribute with name.
default_value represents the default value for the attribute. It may be a callable. It is not used within the attribute when providing values, but instead exists for other parts of the system to reference.
If mutable is set to False then the local value of the attribute on an entity can only be set when both the existing local and remote values are
ftrack_api.symbol.NOT_SET
. The exception to this is when the target value is alsoftrack_api.symbol.NOT_SET
.If computed is set to True the value is a remote side computed value and should not be long-term cached.
-
computed
¶ Return whether attribute is computed.
-
get_entity_storage
(entity)¶ Return attribute storage on entity creating if missing.
-
get_local_value
(entity)¶ Return locally set value for entity.
-
get_remote_value
(entity)¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
is_modified
(entity)¶ Return whether local value set and differs from remote.
Note
Will not fetch remote value so may report True even when values are the same on the remote.
-
is_set
(entity)¶ Return whether a value is set for entity.
-
mutable
¶ Return whether attribute is mutable.
-
name
¶ Return name.
-
populate_remote_value
(entity)¶ Populate remote value for entity.
-
-
class
ftrack_api.attribute.
CollectionAttribute
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)[source]¶ Represent a collection of other entities.
-
collection_class
¶ alias of
ftrack_api.collection.Collection
-
__init__
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)¶ Initialise attribute with name.
default_value represents the default value for the attribute. It may be a callable. It is not used within the attribute when providing values, but instead exists for other parts of the system to reference.
If mutable is set to False then the local value of the attribute on an entity can only be set when both the existing local and remote values are
ftrack_api.symbol.NOT_SET
. The exception to this is when the target value is alsoftrack_api.symbol.NOT_SET
.If computed is set to True the value is a remote side computed value and should not be long-term cached.
-
computed
¶ Return whether attribute is computed.
-
get_entity_storage
(entity)¶ Return attribute storage on entity creating if missing.
-
get_local_value
(entity)¶ Return locally set value for entity.
-
get_remote_value
(entity)¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
get_value
(entity)¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
Note
As value is a collection that is mutable, will transfer a remote value into the local value on access if no local value currently set.
-
is_modified
(entity)¶ Return whether local value set and differs from remote.
Note
Will not fetch remote value so may report True even when values are the same on the remote.
-
is_set
(entity)¶ Return whether a value is set for entity.
-
mutable
¶ Return whether attribute is mutable.
-
name
¶ Return name.
-
populate_remote_value
(entity)¶ Populate remote value for entity.
-
set_local_value
(entity, value)¶ Set local value for entity.
-
set_remote_value
(entity, value)¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
-
class
ftrack_api.attribute.
KeyValueMappedCollectionAttribute
(name, creator, key_attribute, value_attribute, **kw)[source]¶ Represent a mapped key, value collection of entities.
-
collection_class
¶ alias of
ftrack_api.collection.KeyValueMappedCollectionProxy
-
__init__
(name, creator, key_attribute, value_attribute, **kw)[source]¶ Initialise attribute with name.
creator should be a function that accepts a dictionary of data and is used by the referenced collection to create new entities in the collection.
key_attribute should be the name of the attribute on an entity in the collection that represents the value for ‘key’ of the dictionary.
value_attribute should be the name of the attribute on an entity in the collection that represents the value for ‘value’ of the dictionary.
-
computed
¶ Return whether attribute is computed.
-
get_entity_storage
(entity)¶ Return attribute storage on entity creating if missing.
-
get_local_value
(entity)¶ Return locally set value for entity.
-
get_remote_value
(entity)¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
get_value
(entity)¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
Note
As value is a collection that is mutable, will transfer a remote value into the local value on access if no local value currently set.
-
is_modified
(entity)¶ Return whether local value set and differs from remote.
Note
Will not fetch remote value so may report True even when values are the same on the remote.
-
is_set
(entity)¶ Return whether a value is set for entity.
-
mutable
¶ Return whether attribute is mutable.
-
name
¶ Return name.
-
populate_remote_value
(entity)¶ Populate remote value for entity.
-
set_local_value
(entity, value)¶ Set local value for entity.
-
set_remote_value
(entity, value)¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
-
class
ftrack_api.attribute.
CustomAttributeCollectionAttribute
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)[source]¶ Represent a mapped custom attribute collection of entities.
-
collection_class
¶ alias of
ftrack_api.collection.CustomAttributeCollectionProxy
-
__init__
(name, default_value=Symbol(NOT_SET), mutable=True, computed=False)¶ Initialise attribute with name.
default_value represents the default value for the attribute. It may be a callable. It is not used within the attribute when providing values, but instead exists for other parts of the system to reference.
If mutable is set to False then the local value of the attribute on an entity can only be set when both the existing local and remote values are
ftrack_api.symbol.NOT_SET
. The exception to this is when the target value is alsoftrack_api.symbol.NOT_SET
.If computed is set to True the value is a remote side computed value and should not be long-term cached.
-
computed
¶ Return whether attribute is computed.
-
get_entity_storage
(entity)¶ Return attribute storage on entity creating if missing.
-
get_local_value
(entity)¶ Return locally set value for entity.
-
get_remote_value
(entity)¶ Return remote value for entity.
Note
Only return locally stored remote value, do not fetch from remote.
-
get_value
(entity)¶ Return current value for entity.
If a value was set locally then return it, otherwise return last known remote value. If no remote value yet retrieved, make a request for it via the session and block until available.
Note
As value is a collection that is mutable, will transfer a remote value into the local value on access if no local value currently set.
-
is_modified
(entity)¶ Return whether local value set and differs from remote.
Note
Will not fetch remote value so may report True even when values are the same on the remote.
-
is_set
(entity)¶ Return whether a value is set for entity.
-
mutable
¶ Return whether attribute is mutable.
-
name
¶ Return name.
-
populate_remote_value
(entity)¶ Populate remote value for entity.
-
set_local_value
(entity, value)¶ Set local value for entity.
-
set_remote_value
(entity, value)¶ Set remote value.
Note
Only set locally stored remote value, do not persist to remote.
-
ftrack_api.cache¶
Caching framework.
Defines a standardised Cache
interface for storing data against
specific keys. Key generation is also standardised using a KeyMaker
interface.
Combining a Cache and KeyMaker allows for memoisation of function calls with
respect to the arguments used by using a Memoiser
.
As a convenience a simple memoise()
decorator is included for quick
memoisation of function using a global cache and standard key maker.
-
class
ftrack_api.cache.
Cache
[source]¶ Cache interface.
Derive from this to define concrete cache implementations. A cache is centered around the concept of key:value pairings where the key is unique across the cache.
-
keys
()[source]¶ Return list of keys at this current time.
Warning
Actual keys may differ from those returned due to timing of access.
-
clear
(pattern=None)[source]¶ Remove all keys matching pattern.
pattern should be a regular expression string.
If pattern is None then all keys will be removed.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
-
class
ftrack_api.cache.
ProxyCache
(proxied)[source]¶ Proxy another cache.
-
keys
()[source]¶ Return list of keys at this current time.
Warning
Actual keys may differ from those returned due to timing of access.
-
clear
(pattern=None)¶ Remove all keys matching pattern.
pattern should be a regular expression string.
If pattern is None then all keys will be removed.
-
values
()¶ Return values for current keys.
-
-
class
ftrack_api.cache.
LayeredCache
(caches)[source]¶ Layered cache.
-
get
(key)[source]¶ Return value for key.
Raise
KeyError
if key not found.Attempt to retrieve from cache layers in turn, starting with shallowest. If value retrieved, then also set the value in each higher level cache up from where retrieved.
-
keys
()[source]¶ Return list of keys at this current time.
Warning
Actual keys may differ from those returned due to timing of access.
-
clear
(pattern=None)¶ Remove all keys matching pattern.
pattern should be a regular expression string.
If pattern is None then all keys will be removed.
-
values
()¶ Return values for current keys.
-
-
class
ftrack_api.cache.
MemoryCache
[source]¶ Memory based cache.
-
keys
()[source]¶ Return list of keys at this current time.
Warning
Actual keys may differ from those returned due to timing of access.
-
clear
(pattern=None)¶ Remove all keys matching pattern.
pattern should be a regular expression string.
If pattern is None then all keys will be removed.
-
values
()¶ Return values for current keys.
-
-
class
ftrack_api.cache.
FileCache
(path)[source]¶ File based cache that uses
anydbm
module.Note
No locking of the underlying file is performed.
-
keys
()[source]¶ Return list of keys at this current time.
Warning
Actual keys may differ from those returned due to timing of access.
-
clear
(pattern=None)¶ Remove all keys matching pattern.
pattern should be a regular expression string.
If pattern is None then all keys will be removed.
-
values
()¶ Return values for current keys.
-
-
class
ftrack_api.cache.
SerialisedCache
(proxied, encode=None, decode=None)[source]¶ Proxied cache that stores values as serialised data.
-
__init__
(proxied, encode=None, decode=None)[source]¶ Initialise cache with encode and decode callables.
proxied is the underlying cache to use for storage.
-
clear
(pattern=None)¶ Remove all keys matching pattern.
pattern should be a regular expression string.
If pattern is None then all keys will be removed.
-
keys
()¶ Return list of keys at this current time.
Warning
Actual keys may differ from those returned due to timing of access.
-
values
()¶ Return values for current keys.
-
-
class
ftrack_api.cache.
StringKeyMaker
[source]¶ Generate string key.
-
__init__
()¶ Initialise key maker.
-
key
(*items)¶ Return key for items.
-
-
class
ftrack_api.cache.
ObjectKeyMaker
[source]¶ Generate unique keys for objects.
-
key
(*items)¶ Return key for items.
-
-
class
ftrack_api.cache.
Memoiser
(cache=None, key_maker=None, return_copies=True)[source]¶ Memoise function calls using a
KeyMaker
andCache
.Example:
>>> memoiser = Memoiser(MemoryCache(), ObjectKeyMaker()) >>> def add(x, y): ... "Return sum of *x* and *y*." ... print 'Called' ... return x + y ... >>> memoiser.call(add, (1, 2), {}) Called >>> memoiser.call(add, (1, 2), {}) >>> memoiser.call(add, (1, 3), {}) Called
-
__init__
(cache=None, key_maker=None, return_copies=True)[source]¶ Initialise with cache and key_maker to use.
If cache is not specified a default
MemoryCache
will be used. Similarly, if key_maker is not specified a defaultObjectKeyMaker
will be used.If return_copies is True then all results returned from the cache will be deep copies to avoid indirect mutation of cached values.
-
-
ftrack_api.cache.
memoise_decorator
(memoiser)[source]¶ Decorator to memoise function calls using memoiser.
-
ftrack_api.cache.
memoiser
= <ftrack_api.cache.Memoiser object>¶ Default memoiser.
-
ftrack_api.cache.
memoise
(function)¶ Default memoise decorator using standard cache and key maker.
ftrack_api.collection¶
-
class
ftrack_api.collection.
Collection
(entity, attribute, mutable=True, data=None)[source]¶ A collection of entities.
-
append
(value)¶ S.append(value) – append value to the end of the sequence
-
clear
() → None -- remove all items from S¶
-
count
(value) → integer -- return number of occurrences of value¶
-
extend
(values)¶ S.extend(iterable) – extend sequence by appending elements from the iterable
-
index
(value[, start[, stop]]) → integer -- return first index of value.¶ Raises ValueError if the value is not present.
Supporting start and stop arguments is optional, but recommended.
-
pop
([index]) → item -- remove and return item at index (default last).¶ Raise IndexError if list is empty or index is out of range.
-
remove
(value)¶ S.remove(value) – remove first occurrence of value. Raise ValueError if the value is not present.
-
reverse
()¶ S.reverse() – reverse IN PLACE
-
-
class
ftrack_api.collection.
MappedCollectionProxy
(collection)[source]¶ Common base class for mapped collection of entities.
-
mutable
¶ Return whether collection is mutable.
-
attribute
¶ Return attribute bound to.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
-
class
ftrack_api.collection.
KeyValueMappedCollectionProxy
(collection, creator, key_attribute, value_attribute)[source]¶ A mapped collection of key, value entities.
Proxy a standard
Collection
as a mapping where certain attributes from the entities in the collection are mapped to key, value pairs.For example:
>>> collection = [Metadata(key='foo', value='bar'), ...] >>> mapped = KeyValueMappedCollectionProxy( ... collection, create_metadata, ... key_attribute='key', value_attribute='value' ... ) >>> print mapped['foo'] 'bar' >>> mapped['bam'] = 'biz' >>> print mapped.collection[-1] Metadata(key='bam', value='biz')
-
attribute
¶ Return attribute bound to.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
mutable
¶ Return whether collection is mutable.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
-
class
ftrack_api.collection.
PerSessionDefaultKeyMaker
[source]¶ Generate key for session.
-
__init__
()¶ Initialise key maker.
-
key
(*items)¶ Return key for items.
-
-
ftrack_api.collection.
memoise_session
(function)¶ Memoiser for use with callables that should be called once per session.
-
class
ftrack_api.collection.
CustomAttributeCollectionProxy
(collection)[source]¶ A mapped collection of custom attribute value entities.
-
get_configuration_id_from_key
(key)[source]¶ Return id of configuration with matching key.
Raise
KeyError
if no configuration with matching key found.
-
attribute
¶ Return attribute bound to.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
mutable
¶ Return whether collection is mutable.
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
ftrack_api.exception¶
-
exception
ftrack_api.exception.
Error
(message=None, details=None)[source]¶ ftrack specific error.
-
default_message
= 'Unspecified error occurred.'¶
-
__init__
(message=None, details=None)[source]¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AuthenticationError
(message=None, details=None)[source]¶ Raise when an authentication error occurs.
-
default_message
= 'Authentication error.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ServerError
(message=None, details=None)[source]¶ Raise when the server reports an error.
-
default_message
= 'Server reported error processing request.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ServerCompatibilityError
(message=None, details=None)[source]¶ Raise when server appears incompatible.
-
default_message
= 'Server incompatible.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
NotFoundError
(message=None, details=None)[source]¶ Raise when something that should exist is not found.
-
default_message
= 'Not found.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
NotUniqueError
(message=None, details=None)[source]¶ Raise when unique value required and duplicate detected.
-
default_message
= 'Non-unique value detected.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
IncorrectResultError
(message=None, details=None)[source]¶ Raise when a result is incorrect.
-
default_message
= 'Incorrect result detected.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
NoResultFoundError
(message=None, details=None)[source]¶ Raise when a result was expected but no result was found.
-
default_message
= 'Expected result, but no result was found.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
MultipleResultsFoundError
(message=None, details=None)[source]¶ Raise when a single result expected, but multiple results found.
-
default_message
= 'Expected single result, but received multiple results.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
EntityTypeError
(message=None, details=None)[source]¶ Raise when an entity type error occurs.
-
default_message
= 'Entity type error.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
UnrecognisedEntityTypeError
(entity_type, **kw)[source]¶ Raise when an unrecognised entity type detected.
-
default_message
= 'Entity type "{entity_type}" not recognised.'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
OperationError
(message=None, details=None)[source]¶ Raise when an operation error occurs.
-
default_message
= 'Operation error.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
InvalidStateError
(message=None, details=None)[source]¶ Raise when an invalid state detected.
-
default_message
= 'Invalid state.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
InvalidStateTransitionError
(current_state, target_state, entity, **kw)[source]¶ Raise when an invalid state transition detected.
-
default_message
= 'Invalid transition from {current_state!r} to {target_state!r} state for entity {entity!r}'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AttributeError
(message=None, details=None)[source]¶ Raise when an error related to an attribute occurs.
-
default_message
= 'Attribute error.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ImmutableAttributeError
(attribute, **kw)[source]¶ Raise when modification of immutable attribute attempted.
-
default_message
= 'Cannot modify value of immutable {attribute.name!r} attribute.'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
CollectionError
(collection, **kw)[source]¶ Raise when an error related to collections occurs.
-
default_message
= 'Collection error.'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ImmutableCollectionError
(collection, **kw)[source]¶ Raise when modification of immutable collection attempted.
-
default_message
= 'Cannot modify value of immutable collection {collection!r}.'¶
-
__init__
(collection, **kw)¶ Initialise error.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
DuplicateItemInCollectionError
(item, collection, **kw)[source]¶ Raise when duplicate item in collection detected.
-
default_message
= 'Item {item!r} already exists in collection {collection!r}.'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ParseError
(message=None, details=None)[source]¶ Raise when a parsing error occurs.
-
default_message
= 'Failed to parse.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
EventHubError
(message=None, details=None)[source]¶ Raise when issues related to event hub occur.
-
default_message
= 'Event hub error occurred.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
EventHubConnectionError
(message=None, details=None)[source]¶ Raise when event hub encounters connection problem.
-
default_message
= 'Event hub is not connected.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
EventHubPacketError
(message=None, details=None)[source]¶ Raise when event hub encounters an issue with a packet.
-
default_message
= 'Invalid packet.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
PermissionDeniedError
(message=None, details=None)[source]¶ Raise when permission is denied.
-
default_message
= 'Permission denied.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
LocationError
(message=None, details=None)[source]¶ Base for errors associated with locations.
-
default_message
= 'Unspecified location error'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ComponentNotInAnyLocationError
(message=None, details=None)[source]¶ Raise when component not available in any location.
-
default_message
= 'Component not available in any location.'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ComponentNotInLocationError
(components, location, **kw)[source]¶ Raise when component(s) not in location.
-
default_message
= 'Component(s) {formatted_components} not found in location {location}.'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ComponentInLocationError
(components, location, **kw)[source]¶ Raise when component(s) already exists in location.
-
default_message
= 'Component(s) {formatted_components} already exist in location {location}.'¶
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorError
(message=None, details=None)[source]¶ Base for errors associated with accessors.
-
default_message
= 'Unspecified accessor error'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorOperationFailedError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Base for failed operations on accessors.
-
default_message
= 'Operation {operation} failed: {error}'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorUnsupportedOperationError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Raise when operation is unsupported.
-
default_message
= 'Operation {operation} unsupported.'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorPermissionDeniedError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Raise when permission denied.
-
default_message
= 'Cannot {operation} {resource_identifier}. Permission denied.'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorResourceIdentifierError
(resource_identifier, **kw)[source]¶ Raise when a error related to a resource_identifier occurs.
-
default_message
= 'Resource identifier is invalid: {resource_identifier}.'¶
-
__init__
(resource_identifier, **kw)[source]¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorFilesystemPathError
(resource_identifier, **kw)[source]¶ Raise when a error related to an accessor filesystem path occurs.
-
default_message
= 'Could not determine filesystem path from resource identifier: {resource_identifier}.'¶
-
__init__
(resource_identifier, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorResourceError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Base for errors associated with specific resource.
-
default_message
= 'Unspecified resource error: {resource_identifier}'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorResourceNotFoundError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Raise when a required resource is not found.
-
default_message
= 'Resource not found: {resource_identifier}'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorParentResourceNotFoundError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Raise when a parent resource (such as directory) is not found.
-
default_message
= 'Parent resource is missing: {resource_identifier}'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorResourceInvalidError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Raise when a resource is not the right type.
-
default_message
= 'Resource invalid: {resource_identifier}'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
AccessorContainerNotEmptyError
(operation='', resource_identifier=None, error=None, **kw)[source]¶ Raise when container is not empty.
-
default_message
= 'Container is not empty: {resource_identifier}'¶
-
__init__
(operation='', resource_identifier=None, error=None, **kw)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
StructureError
(message=None, details=None)[source]¶ Base for errors associated with structures.
-
default_message
= 'Unspecified structure error'¶
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
exception
ftrack_api.exception.
ConnectionClosedError
(message=None, details=None)[source]¶ Raise when attempt to use closed connection detected.
-
__init__
(message=None, details=None)¶ Initialise exception with message.
If message is None, the class ‘default_message’ will be used.
details should be a mapping of extra information that can be used in the message and also to provide more context.
-
args
¶
-
default_message
= 'Connection closed.'¶
-
with_traceback
()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
ftrack_api.formatter¶
-
ftrack_api.formatter.
FILTER
= {'ignore_unset': <function <lambda>>}¶ Useful filters to pass to
format()
.`
-
ftrack_api.formatter.
format
(entity, formatters=None, attribute_filter=None, recursive=False, indent=0, indent_first_line=True, _seen=None)[source]¶ Return formatted string representing entity.
formatters can be used to customise formatting of elements. It should be a mapping with one or more of the following keys:
- header - Used to format entity type.
- label - Used to format attribute names.
Specify an attribute_filter to control which attributes to include. By default all attributes are included. The attribute_filter should be a callable that accepts (entity, attribute_name, attribute_value) and returns True if the attribute should be included in the output. For example, to filter out all unset values:
attribute_filter=ftrack_api.formatter.FILTER['ignore_unset']
If recursive is True then recurse into Collections and format each entity present.
indent specifies the overall indentation in spaces of the formatted text, whilst indent_first_line determines whether to apply that indent to the first generated line.
Warning
Iterates over all entity attributes which may cause multiple queries to the server. Turn off auto populating in the session to prevent this.
ftrack_api.inspection¶
-
ftrack_api.inspection.
primary_key
(entity)[source]¶ Return primary key of entity as an ordered mapping of {field: value}.
To get just the primary key values:
primary_key(entity).values()
ftrack_api.logging¶
ftrack_api.operation¶
-
class
ftrack_api.operation.
Operation
[source]¶ Represent an operation.
-
__init__
¶ Initialize self. See help(type(self)) for accurate signature.
-
-
class
ftrack_api.operation.
CreateEntityOperation
(entity_type, entity_key, entity_data)[source]¶ Represent create entity operation.
-
__init__
(entity_type, entity_key, entity_data)[source]¶ Initialise operation.
entity_type should be the type of entity in string form (as returned from
ftrack_api.entity.base.Entity.entity_type
).entity_key should be the unique key for the entity and should follow the form returned from
ftrack_api.inspection.primary_key()
.entity_data should be a mapping of the initial data to populate the entity with when creating.
Note
Shallow copies will be made of each value in entity_data.
-
-
class
ftrack_api.operation.
UpdateEntityOperation
(entity_type, entity_key, attribute_name, old_value, new_value)[source]¶ Represent update entity operation.
-
__init__
(entity_type, entity_key, attribute_name, old_value, new_value)[source]¶ Initialise operation.
entity_type should be the type of entity in string form (as returned from
ftrack_api.entity.base.Entity.entity_type
).entity_key should be the unique key for the entity and should follow the form returned from
ftrack_api.inspection.primary_key()
.attribute_name should be the string name of the attribute being modified and old_value and new_value should reflect the change in value.
Note
Shallow copies will be made of both old_value and new_value.
-
-
class
ftrack_api.operation.
DeleteEntityOperation
(entity_type, entity_key)[source]¶ Represent delete entity operation.
-
__init__
(entity_type, entity_key)[source]¶ Initialise operation.
entity_type should be the type of entity in string form (as returned from
ftrack_api.entity.base.Entity.entity_type
).entity_key should be the unique key for the entity and should follow the form returned from
ftrack_api.inspection.primary_key()
.
-
ftrack_api.plugin¶
-
ftrack_api.plugin.
discover
(paths, positional_arguments=None, keyword_arguments=None)[source]¶ Find and load plugins in search paths.
Each discovered module should implement a register function that accepts positional_arguments and keyword_arguments as *args and **kwargs respectively.
If a register function does not accept variable arguments, then attempt to only pass accepted arguments to the function by inspecting its signature.
ftrack_api.query¶
-
class
ftrack_api.query.
QueryResult
(session, expression, page_size=500)[source]¶ Results from a query.
-
OFFSET_EXPRESSION
= re.compile('(?P<offset>offset (?P<value>\\d+))')¶
-
LIMIT_EXPRESSION
= re.compile('(?P<limit>limit (?P<value>\\d+))')¶
-
__init__
(session, expression, page_size=500)[source]¶ Initialise result set.
session should be an instance of
ftrack_api.session.Session
that will be used for executing the query expression.page_size should be an integer specifying the maximum number of records to fetch in one request allowing the results to be fetched incrementally in a transparent manner for optimal performance. Any offset or limit specified in expression are honoured for final result set, but intermediate queries may be issued with different offsets and limits in order to fetch pages. When an embedded limit is smaller than the given page_size it will be used instead and no paging will take place.
Warning
Setting page_size to a very large amount may negatively impact performance of not only the caller, but the server in general.
-
count
(value) → integer -- return number of occurrences of value¶
-
index
(value[, start[, stop]]) → integer -- return first index of value.¶ Raises ValueError if the value is not present.
Supporting start and stop arguments is optional, but recommended.
-
one
()[source]¶ Return exactly one single result from query by applying a limit.
Raise
ValueError
if an existing limit is already present in the expression.Raise
ValueError
if an existing offset is already present in the expression as offset is inappropriate when expecting a single item.Raise
MultipleResultsFoundError
if more than one result was available orNoResultFoundError
if no results were available.Note
Both errors subclass
IncorrectResultError
if you want to catch only one error type.
-
first
()[source]¶ Return first matching result from query by applying a limit.
Raise
ValueError
if an existing limit is already present in the expression.If no matching result available return None.
-
ftrack_api.session¶
-
class
ftrack_api.session.
SessionAuthentication
(api_key, api_user)[source]¶ Attach ftrack session authentication information to requests.
-
class
ftrack_api.session.
Session
(server_url=None, api_key=None, api_user=None, auto_populate=True, plugin_paths=None, cache=None, cache_key_maker=None, auto_connect_event_hub=False, schema_cache_path=None, plugin_arguments=None)[source]¶ An isolated session for interaction with an ftrack server.
-
__init__
(server_url=None, api_key=None, api_user=None, auto_populate=True, plugin_paths=None, cache=None, cache_key_maker=None, auto_connect_event_hub=False, schema_cache_path=None, plugin_arguments=None)[source]¶ Initialise session.
server_url should be the URL of the ftrack server to connect to including any port number. If not specified attempt to look up from
FTRACK_SERVER
.api_key should be the API key to use for authentication whilst api_user should be the username of the user in ftrack to record operations against. If not specified, api_key should be retrieved from
FTRACK_API_KEY
and api_user fromFTRACK_API_USER
.If auto_populate is True (the default), then accessing entity attributes will cause them to be automatically fetched from the server if they are not already. This flag can be changed on the session directly at any time.
plugin_paths should be a list of paths to search for plugins. If not specified, default to looking up
FTRACK_EVENT_PLUGIN_PATH
.cache should be an instance of a cache that fulfils the
ftrack_api.cache.Cache
interface and will be used as the cache for the session. It can also be a callable that will be called with the session instance as sole argument. The callable should returnNone
if a suitable cache could not be configured, but session instantiation can continue safely.Note
The session will add the specified cache to a pre-configured layered cache that specifies the top level cache as a
ftrack_api.cache.MemoryCache
. Therefore, it is unnecessary to construct a separate memory cache for typical behaviour. Working around this behaviour or removing the memory cache can lead to unexpected behaviour.cache_key_maker should be an instance of a key maker that fulfils the
ftrack_api.cache.KeyMaker
interface and will be used to generate keys for objects being stored in the cache. If not specified, aStringKeyMaker
will be used.If auto_connect_event_hub is True then embedded event hub will be automatically connected to the event server and allow for publishing and subscribing to non-local events. If False, then only publishing and subscribing to local events will be possible until the hub is manually connected using
EventHub.connect
.Note
The event hub connection is performed in a background thread to improve session startup time. If a registered plugin requires a connected event hub then it should check the event hub connection status explicitly. Subscribing to events does not require a connected event hub.
Enable schema caching by setting schema_cache_path to a folder path. If not set,
FTRACK_API_SCHEMA_CACHE_PATH
will be used to determine the path to store cache in. If the environment variable is also not specified then a temporary directory will be used. Set to False to disable schema caching entirely.plugin_arguments should be an optional mapping (dict) of keyword arguments to pass to plugin register functions upon discovery. If a discovered plugin has a signature that is incompatible with the passed arguments, the discovery mechanism will attempt to reduce the passed arguments to only those that the plugin accepts. Note that a warning will be logged in this case.
-
closed
¶ Return whether session has been closed.
-
server_information
¶ Return server information such as server version.
-
server_url
¶ Return server ulr used for session.
-
api_user
¶ Return username used for session.
-
api_key
¶ Return API key used for session.
-
event_hub
¶ Return event hub.
-
close
()[source]¶ Close session.
Close connections to server. Clear any pending operations and local cache.
Use this to ensure that session is cleaned up properly after use.
-
reset
()[source]¶ Reset session clearing local state.
Clear all pending operations and expunge all entities from session.
Also clear the local cache. If the cache used by the session is a
LayeredCache
then only clear top level cache. Otherwise, clear the entire cache.Plugins are not rediscovered or reinitialised, but certain plugin events are re-emitted to properly configure session aspects that are dependant on cache (such as location plugins).
Warning
Previously attached entities are not reset in memory and will retain their state, but should not be used. Doing so will cause errors.
-
auto_populating
(auto_populate)[source]¶ Temporarily set auto populate to auto_populate.
The current setting will be restored automatically when done.
Example:
with session.auto_populating(False): print entity['name']
-
operation_recording
(record_operations)[source]¶ Temporarily set operation recording to record_operations.
The current setting will be restored automatically when done.
Example:
with session.operation_recording(False): entity['name'] = 'change_not_recorded'
-
created
¶ Return list of newly created entities.
-
modified
¶ Return list of locally modified entities.
-
deleted
¶ Return list of deleted entities.
-
reset_remote
(reset_type, entity=None)[source]¶ Perform a server side reset.
reset_type is a server side supported reset type, passing the optional entity to perform the option upon.
Please refer to ftrack documentation for a complete list of supported server side reset types.
-
create
(entity_type, data=None, reconstructing=False)[source]¶ Create and return an entity of entity_type with initial data.
If specified, data should be a dictionary of key, value pairs that should be used to populate attributes on the entity.
If reconstructing is False then create a new entity setting appropriate defaults for missing data. If True then reconstruct an existing entity.
Constructed entity will be automatically
merged
into the session.
-
ensure
(entity_type, data, identifying_keys=None)[source]¶ Retrieve entity of entity_type with data, creating if necessary.
data should be a dictionary of the same form passed to
create()
.By default, check for an entity that has matching data. If identifying_keys is specified as a list of keys then only consider the values from data for those keys when searching for existing entity. If data is missing an identifying key then raise
KeyError
.If no identifying_keys specified then use all of the keys from the passed data. Raise
ValueError
if no identifying_keys can be determined.Each key should be a string.
Note
Currently only top level scalars supported. To ensure an entity by looking at relationships, manually issue the
query()
andcreate()
calls.If more than one entity matches the determined filter criteria then raise
MultipleResultsFoundError
.If no matching entity found then create entity using supplied data.
If a matching entity is found, then update it if necessary with data.
Note
If entity created or updated then a
commit()
will be issued automatically. If this behaviour is undesired, perform thequery()
andcreate()
calls manually.Return retrieved or created entity.
Example:
# First time, a new entity with `username=martin` is created. entity = session.ensure('User', {'username': 'martin'}) # After that, the existing entity is retrieved. entity = session.ensure('User', {'username': 'martin'}) # When existing entity retrieved, entity may also be updated to # match supplied data. entity = session.ensure( 'User', {'username': 'martin', 'email': 'martin@example.com'} )
-
get
(entity_type, entity_key)[source]¶ Return entity of entity_type with unique entity_key.
First check for an existing entry in the configured cache, otherwise issue a query to the server.
If no matching entity found, return None.
-
query
(expression, page_size=500)[source]¶ Query against remote data according to expression.
expression is not executed directly. Instead return an
ftrack_api.query.QueryResult
instance that will execute remote call on access.page_size specifies the maximum page size that the returned query result object should be configured with.
See also
-
merge
(value, merged=None)[source]¶ Merge value into session and return merged value.
merged should be a mapping to record merges during run and should be used to avoid infinite recursion. If not set will default to a dictionary.
-
populate
(entities, projections)[source]¶ Populate entities with attributes specified by projections.
Any locally set values included in the projections will not be overwritten with the retrieved remote value. If this ‘synchronise’ behaviour is required, first clear the relevant values on the entity by setting them to
ftrack_api.symbol.NOT_SET
. Deleting the key will have the same effect:>>> print(user['username']) martin >>> del user['username'] >>> print(user['username']) Symbol(NOT_SET)
Note
Entities that have been created and not yet persisted will be skipped as they have no remote values to fetch.
-
rollback
()[source]¶ Clear all recorded operations and local state.
Typically this would be used following a failed
commit()
in order to revert the session to a known good state.Newly created entities not yet persisted will be detached from the session / purged from cache and no longer contribute, but the actual objects are not deleted from memory. They should no longer be used and doing so could cause errors.
-
encode
(data, entity_attribute_strategy='set_only')[source]¶ Return data encoded as JSON formatted string.
entity_attribute_strategy specifies how entity attributes should be handled. The following strategies are available:
- all - Encode all attributes, loading any that are currently NOT_SET.
- set_only - Encode only attributes that are currently set without loading any from the remote.
- modified_only - Encode only attributes that have been modified locally.
- persisted_only - Encode only remote (persisted) attribute values.
-
entity_reference
(entity)[source]¶ Return entity reference that uniquely identifies entity.
Return a mapping containing the __entity_type__ of the entity along with the key, value pairs that make up it’s primary key.
-
pick_location
(component=None)[source]¶ Return suitable location to use.
If no component specified then return highest priority accessible location. Otherwise, return highest priority accessible location that component is available in.
Return None if no suitable location could be picked.
-
pick_locations
(components)[source]¶ Return suitable locations for components.
Return list of locations corresponding to components where each picked location is the highest priority accessible location for that component. If a component has no location available then its corresponding entry will be None.
-
create_component
(path, data=None, location='auto')[source]¶ Create a new component from path with additional data
Note
This is a helper method. To create components manually use the standard
Session.create()
method.path can be a string representing a filesystem path to the data to use for the component. The path can also be specified as a sequence string, in which case a sequence component with child components for each item in the sequence will be created automatically. The accepted format for a sequence is ‘{head}{padding}{tail} [{ranges}]’. For example:
'/path/to/file.%04d.ext [1-5, 7, 8, 10-20]'
See also
data should be a dictionary of any additional data to construct the component with (as passed to
Session.create()
).If location is specified then automatically add component to that location. The default of ‘auto’ will automatically pick a suitable location to add the component to if one is available. To not add to any location specifiy locations as None.
Note
A
Session.commit
may be automatically issued as part of the components registration in the location.
-
get_component_availability
(component, locations=None)[source]¶ Return availability of component.
If locations is set then limit result to availability of component in those locations.
Return a dictionary of {location_id:percentage_availability}
-
get_component_availabilities
(components, locations=None)[source]¶ Return availabilities of components.
If locations is set then limit result to availabilities of components in those locations.
Return a list of dictionaries of {location_id:percentage_availability}. The list indexes correspond to those of components.
-
get_widget_url
(name, entity=None, theme=None)[source]¶ Return an authenticated URL for widget with name and given options.
The returned URL will be authenticated using a token which will expire after 6 minutes.
name should be the name of the widget to return and should be one of ‘info’, ‘tasks’ or ‘tasks_browser’.
Certain widgets require an entity to be specified. If so, specify it by setting entity to a valid entity instance.
theme sets the theme of the widget and can be either ‘light’ or ‘dark’ (defaulting to ‘dark’ if an invalid option given).
-
encode_media
(media, version_id=None, keep_original='auto')[source]¶ Return a new Job that encode media to make it playable in browsers.
media can be a path to a file or a FileComponent in the ftrack.server location.
The job will encode media based on the file type and job data contains information about encoding in the following format:
{ 'output': [{ 'format': 'video/mp4', 'component_id': 'e2dc0524-b576-11d3-9612-080027331d74' }, { 'format': 'image/jpeg', 'component_id': '07b82a97-8cf9-11e3-9383-20c9d081909b' }], 'source_component_id': 'e3791a09-7e11-4792-a398-3d9d4eefc294', 'keep_original': True }
The output components are associated with the job via the job_components relation.
An image component will always be generated if possible that can be used as a thumbnail.
If media is a file path, a new source component will be created and added to the ftrack server location and a call to
commit()
will be issued. If media is a FileComponent, it will be assumed to be in available in the ftrack.server location.If version_id is specified, the new components will automatically be associated with the AssetVersion. Otherwise, the components will not be associated to a version even if the supplied media belongs to one. A server version of 3.3.32 or higher is required for the version_id argument to function properly.
If keep_original is not set, the original media will be kept if it is a FileComponent, and deleted if it is a file path. You can specify True or False to change this behavior.
-
get_upload_metadata
(component_id, file_name, file_size, checksum=None)[source]¶ Return URL and headers used to upload data for component_id.
file_name and file_size should match the components details.
The returned URL should be requested using HTTP PUT with the specified headers.
The checksum is used as the Content-MD5 header and should contain the base64-encoded 128-bit MD5 digest of the message (without the headers) according to RFC 1864. This can be used as a message integrity check to verify that the data is the same data that was originally sent.
-
send_user_invites
(users)[source]¶ Send a invitation to the provided user.
users is a list of User instances
-
-
class
ftrack_api.session.
AutoPopulatingContext
(session, auto_populate)[source]¶ Context manager for temporary change of session auto_populate value.
-
class
ftrack_api.session.
OperationRecordingContext
(session, record_operations)[source]¶ Context manager for temporary change of session record_operations.
-
class
ftrack_api.session.
OperationPayload
(*args, **kwargs)[source]¶ Represent operation payload.
-
clear
() → None. Remove all items from D.¶
-
get
(k[, d]) → D[k] if k in D, else d. d defaults to None.¶
-
items
() → a set-like object providing a view on D's items¶
-
keys
() → a set-like object providing a view on D's keys¶
-
pop
(k[, d]) → v, remove specified key and return the corresponding value.¶ If key is not found, d is returned if given, otherwise KeyError is raised.
-
popitem
() → (k, v), remove and return some (key, value) pair¶ as a 2-tuple; but raise KeyError if D is empty.
-
setdefault
(k[, d]) → D.get(k,d), also set D[k]=d if k not in D¶
-
update
([E, ]**F) → None. Update D from mapping/iterable E and F.¶ If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v
-
values
() → an object providing a view on D's values¶
-
ftrack_api.symbol¶
-
ftrack_api.symbol.
NOT_SET
= Symbol(NOT_SET)¶ Symbol representing that no value has been set or loaded.
-
ftrack_api.symbol.
CREATED
= Symbol(CREATED)¶ Symbol representing created state.
-
ftrack_api.symbol.
MODIFIED
= Symbol(MODIFIED)¶ Symbol representing modified state.
-
ftrack_api.symbol.
DELETED
= Symbol(DELETED)¶ Symbol representing deleted state.
-
ftrack_api.symbol.
COMPONENT_ADDED_TO_LOCATION_TOPIC
= 'ftrack.location.component-added'¶ Topic published when component added to a location.
-
ftrack_api.symbol.
COMPONENT_REMOVED_FROM_LOCATION_TOPIC
= 'ftrack.location.component-removed'¶ Topic published when component removed from a location.
-
ftrack_api.symbol.
ORIGIN_LOCATION_ID
= 'ce9b348f-8809-11e3-821c-20c9d081909b'¶ Identifier of builtin origin location.
-
ftrack_api.symbol.
UNMANAGED_LOCATION_ID
= 'cb268ecc-8809-11e3-a7e2-20c9d081909b'¶ Identifier of builtin unmanaged location.
-
ftrack_api.symbol.
REVIEW_LOCATION_ID
= 'cd41be70-8809-11e3-b98a-20c9d081909b'¶ Identifier of builtin review location.
-
ftrack_api.symbol.
CONNECT_LOCATION_ID
= '07b82a97-8cf9-11e3-9383-20c9d081909b'¶ Identifier of builtin connect location.
-
ftrack_api.symbol.
SERVER_LOCATION_ID
= '3a372bde-05bc-11e4-8908-20c9d081909b'¶ Identifier of builtin server location.
-
ftrack_api.symbol.
CHUNK_SIZE
= 1048576¶ Chunk size used when working with data, default to 1Mb.
-
ftrack_api.symbol.
JOB_SYNC_USERS_LDAP
= Symbol(SYNC_USERS_LDAP)¶ Symbol representing syncing users with ldap
Event list¶
The following is a consolidated list of events published directly by this API.
For some events, a template plugin file is also listed for download (Download template plugin) to help get you started with writing your own plugin for a particular event.
See also
ftrack.api.session.construct-entity-type¶
Synchronous. Published by the session to retrieve constructed class for specified schema:
Event(
topic='ftrack.api.session.construct-entity-type',
data=dict(
schema=schema,
schemas=schemas
)
)
Expects returned data to be:
A Python class.
See also
ftrack.api.session.configure-location¶
Synchronous. Published by the session to allow configuring of location instances:
Event(
topic='ftrack.api.session.configure-location',
data=dict(
session=self
)
)
See also
ftrack.location.component-added¶
Published whenever a component is added to a location:
Event(
topic='ftrack.location.component-added',
data=dict(
component_id='e2dc0524-b576-11d3-9612-080027331d74',
location_id='07b82a97-8cf9-11e3-9383-20c9d081909b'
)
)
ftrack.location.component-removed¶
Published whenever a component is removed from a location:
Event(
topic='ftrack.location.component-removed',
data=dict(
component_id='e2dc0524-b576-11d3-9612-080027331d74',
location_id='07b82a97-8cf9-11e3-9383-20c9d081909b'
)
)
ftrack.api.session.ready¶
Synchronous. Published after
a Session
has been initialized and
is ready to be used:
Event(
topic='ftrack.api.session.ready',
data=dict(
session=<Session instance>,
)
)
Warning
Since the event is synchronous and blocking, avoid doing any unnecessary work as it will slow down session initialization.
See also
Also see example usage in example_plugin_using_session.py
.
ftrack.api.session.reset¶
Synchronous. Published after
a Session
has been reset and is ready to be used
again:
Event(
topic='ftrack.api.session.reset',
data=dict(
session=<Session instance>,
)
)
Environment variables¶
The following is a consolidated list of environment variables that this API can reference:
-
FTRACK_SERVER
¶ The full url of the ftrack server to connect to. For example “https://mycompany.ftrackapp.com”
-
FTRACK_API_USER
¶ The username of the ftrack user to act on behalf of when performing actions in the system.
Note
When this environment variable is not set, the API will typically also check other standard operating system variables that hold the username of the current logged in user. To do this it uses
getpass.getuser()
.
-
FTRACK_API_KEY
¶ The API key to use when performing actions in the system. The API key is used to determine the permissions that a script has in the system.
-
FTRACK_APIKEY
¶ For backwards compatibility. See
FTRACK_API_KEY
.
-
FTRACK_EVENT_PLUGIN_PATH
¶ Paths to search recursively for plugins to load and use in a session. Multiple paths can be specified by separating with the value of
os.pathsep
(e.g. ‘:’ or ‘;’).
-
FTRACK_API_SCHEMA_CACHE_PATH
¶ Path to a directory that will be used for storing and retrieving a cache of the entity schemas fetched from the server.
-
http_proxy / https_proxy
¶ If you need to use a proxy to connect to ftrack you can use the “standard”
http_proxy
andhttps_proxy
. Please note that they are lowercase.For example “export https_proxy=http://proxy.mycompany.com:8080”
Security and authentication¶
Self signed SSL certificate¶
When using a self signed SSL certificate the API may fail to connect if it
cannot verify the SSL certificate. Under the hood the
requests library is used and it
must be specified where the trusted certificate authority can be found using the
environment variable REQUESTS_CA_BUNDLE
.
See also
InsecurePlatformWarning¶
When using this API you may sometimes see a warning:
InsecurePlatformWarning: A true SSLContext object is not available. This
prevents urllib3 from configuring SSL appropriately and may cause certain
SSL connections to fail.
If you encounter this warning, its recommended you upgrade to Python 2.7.9, or use pyOpenSSL. To use pyOpenSSL simply:
pip install pyopenssl ndg-httpsclient pyasn1
and the requests library used by this API will use pyOpenSSL instead.
See also
Release and migration notes¶
Find out information about what has changed between versions and any important migration notes to be aware of when switching to a new version.
Release Notes¶
2.0.0¶
6 May 2020- new
DocumentationAdded new section Set attributes on new entities to custom attributes known limitation.
- fixed
testsNote reply randomly fails.
- fixed
eventssessionEvent server wait method will sometimes raise a connection error because of a race condition.
- changed
websocketSelect highest available protocol version when connecting to websocket.
- changed
versionReplace fixed version with automatic versioning from git repository.
- changed
eventsNotify users on remote event server connection status while waiting.
- fix
Transfer component from server storage breaks due to different string/byte handling.
- changed
SessionPrivate method
Session._entity_reference()
has been deprecated.Note
You should now use the public method
Session.entity_reference()
. - changed
SessionPrivate method
Session._call()
has been deprecated.Note
You should now use the public method
Session.call()
. - changed
SessionPublic method
Session.delayed_job()
has been deprecated.Note
You should now use
Session.call()
. - fixed
ApiLocationLocationMixins are not compatible with Python 3.
- fixed
TestEntity test fails due to missing parents.
- new
TestsAdd support for flaky tests to improve test reliability.
- changed
SessionDo not auto connect by default to event server hub.
- new
Provide support for python3.X.
1.8.2¶
14 January 2020- fixed
Testtest_ensure_entity_with_non_string_data_types test fails due to missing parents.
- changed
sessionUse WeakMethod when registering atexit handler to prevent memory leak.
1.8.1¶
30 October 2019- changed
LocationIncrease chunk size for file operations to 1 Megabyte. This value can now also be set from the environment variable:
FTRACK_API_FILE_CHUNK_SIZE
- new
setupAdd check for correct python version when installing with pip.
- new
NotesAdd support for note labels in create_note helper method.
- changed
sessionEnsure errors from server are fully reported with stack trace.
1.8.0¶
21 February 2019- fixed
documentationEvent description component-removed report component-added event signature.
- new
attributesessionAdd new scalar type object to factory.
- new
attributesessionAdd support for list of computed attributes as part of schema definition. A computed attribute is derived on the server side, and can be time dependentant and differ between users. As such a computed attribute is not suitable for long term encoding and will not be encoded with the persisted_only stragey.
- changed
The delayed_job method has been deprecated in favour of a direct Session.call. See Sync users with LDAP for example usage.
- changed
Private method
Session._call()
has been converted to a public method,Session.call()
.The private method will continue to work, but a pending deprecation warning will be issued when used. The private method will be removed entirely in version 2.0.
- changed
eventssessionEvent server connection error is too generic, the actual error is now reported to users.
1.7.1¶
13 November 2018- fixed
eventssessionMeta events for event hub connect and disconnect does not include source.
- fixed
locationsessionMissing context argument to
ResourceIdentifierTransformer.decode()
inLocation.get_resource_identifier()
.
1.7.0¶
27 July 2018- new
eventssessionAdded new events ftrack.api.session.ready and ftrack.api.session.reset which can be used to perform operations after the session is ready or has been reset, respectively.
- changed
Private method
Session._entity_reference()
has been converted to a public method,Session.entity_reference()
.The private method will continue to work, but a pending deprecation warning will be issued when used. The private method will be removed entirely in version 2.0.
- fixed
eventssession
Session.close()
raises an exception if event hub was explicitly connected after session initialization.
1.6.0¶
17 May 2018- new
depreciationeventsIn version 2.0.0 of the ftrack-python-api the default behavior for the
Session
class will change for the argument auto_connect_event_hub, the default value will switch from True to False.A warning will now be emitted if async events are published or subscribed to without auto_connect_event_hub has not explicitly been set to True.
- fixed
documentationEvent payload not same as what is being emitted for ftrack.location.component-added and ftrack.location.component-removed.
- fixed
eventsPyparsing is causing random errors in a threaded environment.
1.5.0¶
19 April 2018- fixed
cachesessionCached entities not updated correctly when fetched in a nested query.
1.4.0¶
5 February 2018- fixed
cachesessionCollection attributes not merged correctly when fetched from server.
- new
api keysessionuserNew function
ftrack_api.session.Session.reset_remote()
allows resetting of attributes to their default value. A convenience method for resetting a users api key utalizing this was also addedftrack_api.entity.user.User.reset_api_key()
. - new
Add support for sending out invitation emails to users. See Invite user for example usage.
- changed
cacheperformanceEntities fetched from cache are now lazily merged. Improved performance when dealing with highly populated caches.
1.3.3¶
16 November 2017- new
ldapusersAdd support for triggering a synchronization of users between ldap and ftrack. See Sync users with LDAP for example usage.
Note
This requires that you run ftrack 3.5.10 or later.
- fixed
Not possible to set metadata on creation.
1.3.2¶
18 September 2017- new
task templateAdded example for managing task templates through the API. See Working with Task Templates for example usage.
- fixed
custom attributes
Not possible to set hierarchical custom attributes on an entity that has not been committed.
- fixed
custom attributes
Not possible to set custom attributes on an Asset that has not been committed.
- fixed
Not possible to set metadata on creation.
1.3.1¶
21 July 2017- fixed
eventssessionCalling disconnect on the event hub is slow.
1.3.0¶
17 July 2017- new
sessionSupport using a
Session
as a context manager to aid closing of session after use:with ftrack_api.Session() as session: # Perform operations with session.
- new
session
Session.close()
automatically called on Python exit if session not already closed. - new
sessionAdded
Session.close()
to properly close a session’s connections to the server(s) as well as ensure event listeners are properly unsubscribed. - new
Added
ftrack_api.exception.ConnectionClosedError
to represent error caused when trying to access servers over closed connection.
1.2.0¶
16 June 2017- changed
eventsUpdated the websocket-client dependency to version >= 0.40.0 to allow for http proxies.
- fixed
documentationThe Publishing versions example incorrectly stated that a location would be automatically picked if the location keyword argument was omitted.
1.1.1¶
27 April 2017- fixed
custom attributesCannot use custom attributes for Asset in ftrack versions prior to 3.5.0.
- fixed
documentationThe example section for managing text custom attributes is not correct.
1.1.0¶
8 March 2017- new
server locationthumbnailAdded method
get_thumbnail_url()
to server location, which can be used to retrieve a thumbnail URL. See Retrieving thumbnail URL for example usage. - new
documentationAdded example on how to manage entity links from the API.
- new
documentationAdded example on how to manage custom attribute configurations from the API.
- new
documentationAdded example on how to use SecurityRole and UserSecurityRole to manage security roles for users.
- new
documentationAdded examples to show how to list a user’s assigned tasks and all users assigned to a task.
- changed
pluginssessionAdded plugin_arguments to
Session
to allow passing of optional keyword arguments to discovered plugin register functions. Only arguments defined in a plugin register function signature are passed so existing plugin register functions do not need updating if the new functionality is not desired. - fixed
documentationThe Working with projects example can be confusing since the project schema may not contain the necessary object types.
- fixed
documentationQuery tutorial article gives misleading information about the
has
operator. - fixed
sessionSize is not set on sequence components when using
Session.create_component()
.
1.0.4¶
13 January 2017- fixed
custom attributesCustom attribute values cannot be set on entities that are not persisted.
- fixed
eventsusername in published event’s source data is set to the operating system user and not the API user.
1.0.3¶
4 January 2017- changed
custom attributessessionIncreased performance of custom attributes and better support for filtering when using a version of ftrack that supports non-sparse attribute values.
- changed
custom attributessessionCustom attributes can no longer be set by mutating entire dictionary.
See also
1.0.2¶
17 November 2016- changed
sessionRemoved version restriction for higher server versions.
1.0.1¶
11 November 2016- fixed
EventHub.publish
on_reply callback only called for first received reply. It should be called for all relevant replies received.
1.0.0¶
28 October 2016- new
session
Session.get_upload_metadata()
has been added. - changed
backwards-incompatiblelocationsData transfer between locations using accessors is now chunked to avoid reading large files into memory.
See also
- changed
server accessor
ftrack_api.accessor.server.ServerFile
has been refactored to work with large files more efficiently. - changed
server accessor
ftrack_api.accessor.server.ServerFile
has been updated to use the get_upload_metadata API endpoint instead of /component/getPutMetadata. - changed
locations
ftrack_api.data.String
is now using a temporary file instead of StringIO to avoid reading large files into memory. - fixed
locationssessionftrack.centralized-storage does not properly validate location selection during user configuration.
0.16.0¶
18 October 2016- new
encode mediasession
Session.encode_media()
can now automatically associate the output with a version by specifying a version_id keyword argument. A new helper method on versions,AssetVersion.encode_media
, can be used to make versions playable in a browser. A server version of 3.3.32 or higher is required for it to function properly.See also
- changed
encode mediasessionYou can now decide if
Session.encode_media()
should keep or delete the original component by specifying the keep_original keyword argument. - changed
backwards-incompatiblecollectionCollection mutation now stores collection instance in operations rather than underlying data structure.
- changed
performanceImprove performance of commit operations by optimising encoding and reducing payload sent to server.
- fixed
documentationAsset parent variable is declared but never used in Publishing versions.
- fixed
documentationDocumentation of hierarchical attributes and their limitations are misleading. See Using custom attributes.
0.15.5¶
12 August 2016- new
documentationAdded two new examples for Publishing versions and Publishing for web review.
- fixed
availabilitysession
Session.get_component_availabilities()
ignores passed locations shortlist and includes all locations in returned availability mapping. - fixed
documentationSource distribution of ftrack-python-api does not include ftrack.css in the documentation.
0.15.4¶
12 July 2016- fixedCustom offset not respected by
- changed
Using a custom offset withQueryResult.one
helper method now raises an exception as an offset is inappropriate when expecting to select a single item. - fixed
caching
LayeredCache.remove
incorrectly raisesKeyError
if key only exists in sub-layer cache.
0.15.3¶
30 June 2016- fixed
cachingsessionA newly created entity now has the correct
ftrack_api.symbol.CREATED
state when checked in caching layer. Previously the state wasftrack_api.symbol.NOT_SET
. Note that this fix causes a change in logic and the storedftrack_api.operation.CreateEntityOperation
might hold data that has not been fullymerged
. - fixed
documentationThe second example in the assignments article is not working.
- changed
cachingsessionA callable cache maker can now return
None
to indicate that it could not create a suitable cache, butSession
instantiation can continue safely.
0.15.2¶
2 June 2016- new
documentationAdded an example on how to work with assignments and allocations Working with assignments and allocations.
- new
documentationAdded Using entity links article with examples of how to manage asset version dependencies.
- fixed
performanceImprove performance of large collection management.
- fixed
Entities are not hashable because
ftrack_api.entity.base.Entity.__hash__()
raises TypeError.
0.15.1¶
2 May 2016- fixed
attributecollectionperformanceCustom attribute configurations does not cache necessary keys, leading to performance issues.
- fixed
locationsstructureStandard structure does not work if version relation is not set on the Component.
0.15.0¶
4 April 2016- new
locationssessionftrack.centralized-storage not working properly on Windows.
0.14.0¶
14 March 2016- changed
locationssessionThe ftrack.centralized-storage configurator now validates that name, label and description for new locations are filled in.
- new
client reviewsessionAdded
Session.send_review_session_invite()
andSession.send_review_session_invites()
that can be used to inform review session invitees about a review session.See also
- new
locationssessionAdded ftrack.centralized-storage configurator as a private module. It implements a wizard like interface used to configure a centralised storage scenario.
- new
locationssessionftrack.centralized-storage storage scenario is automatically configured based on information passed from the server with the query_server_information action.
- new
structureAdded
ftrack_api.structure.standard.StandardStructure
with hierarchy based resource identifier generation. - new
documentationAdded more information to the Configuring plugins article.
- fixed
start_timer()
arguments comment and name are ignored. - fixed
stop_timer()
calculates the wrong duration when the server is not running in UTC.For the duration to be calculated correctly ftrack server version >= 3.3.15 is required.
0.13.0¶
10 February 2016- new
componentthumbnailAdded improved support for handling thumbnails.
See also
- new
encode mediasessionAdded
Session.encode_media()
that can be used to encode media to make it playable in a browser.See also
- fixed
Session.commit()
fails when setting a custom attribute on an asset version that has been created and committed in the same session. - new
locationsAdded
ftrack_api.entity.location.Location.get_url()
to retrieve a URL to a component in a location if supported by theftrack_api.accessor.base.Accessor
. - new
documentationUpdated Using notes and Managing jobs articles with examples of how to use note and job components.
- changed
loggingperformanceLogged messages now evaluated lazily using
ftrack_api.logging.LazyLogMessage
as optimisation. - changed
eventssessionAuto connection of event hub for
Session
now takes place in background to improve session startup time. - changed
eventssessionEvent hub connection timeout is now 60 seconds instead of 10.
- changed
server versionftrack server version >= 3.3.11, < 3.4 required.
- changed
performance
ftrack_api.query.QueryResult
now pages internally using a specified page size in order to optimise record retrieval for large query results.Session.query()
has also been updated to allow passing a custom page size at runtime if desired.
0.12.0¶
17 December 2015- new
session Added
ftrack_api.session.Session.get_widget_url()
to retrieve an authenticated URL to info or tasks widgets.
0.11.0¶
4 December 2015- new
documentationUpdated Migrating from old API with new link attribute and added a usage example.
- new
cachingperformanceschemasCaching of schemas for increased performance.
ftrack_api.session.Session()
now accepts schema_cache_path argument to specify location of schema cache. If not set it will use a temporary folder.
0.10.0¶
24 November 2015- changed
testsUpdated session test to use mocked schemas for encoding tests.
- fixed
Documentation specifies Python 2.6 instead of Python 2.7 as minimum interpreter version.
- fixed
Documentation does not reflect current dependencies.
- changed
componentlocationsperformancesessionImproved performance of
ftrack_api.entity.location.Location.add_components()
by batching database operations.As a result it is no longer possible to determine progress of transfer for container components in realtime as events will be emitted in batch at end of operation.
In addition, it is now the callers responsibility to clean up any transferred data should an error occur during either data transfer or database registration.
- changed
exceptionlocations
ftrack_api.exception.ComponentInLocationError
now accepts either a single component or multiple components and makes them available as components in its details parameter. - changed
testsUpdated session test to not fail on the new private link attribute.
- changed
sessionInternal method
_fetch_schemas()
has beed renamed toSession._load_schemas()
and now requires a schema_cache_path argument.
0.9.0¶
30 October 2015- new
cachingAdded
ftrack_api.cache.Cache.values()
as helper for retrieving all values in cache. - fixed
cachingsession
Session.merge()
redundantly attempts to expand entity references that have already been expanded causing performance degradation. - new
session
Session.rollback()
has been added to support cleanly reverting session state to last good state following a failed commit. - changed
eventsEvent hub will no longer allow unverified SSL connections.
See also
- changed
session
Session.reset()
no longer resets the connection. It also clears all local state and re-configures certain aspects that are cache dependant, such as location plugins. - fixed
factoryDebug logging messages using incorrect index for formatting leading to misleading exception.
0.8.4¶
8 October 2015- new
Added initial support for custom attributes.
See also
- new
attributecollectionAdded
ftrack_api.collection.CustomAttributeCollectionProxy
andftrack_api.attribute.CustomAttributeCollectionAttribute
to handle custom attributes. - changed
attributecollection
ftrack_api.attribute.MappedCollectionAttribute
renamed toftrack_api.attribute.KeyValueMappedCollectionAttribute
to more closely reflect purpose. - changed
collection
ftrack_api.collection.MappedCollectionProxy
has been refactored as a generic base class with key, value specialisation handled in new dedicated classftrack_api.collection.KeyValueMappedCollectionProxy
. This is done to avoid confusion following introduction of newftrack_api.collection.CustomAttributeCollectionProxy
class. - fixed
eventsThe event hub does not always reconnect after computer has come back from sleep.
0.8.3¶
28 September 2015- changed
server versionftrack server version >= 3.2.1, < 3.4 required.
- changed
Updated ftrack.server location implementation. A server version of 3.3 or higher is required for it to function properly.
- fixed
ftrack_api.entity.factory.StandardFactory.create()
not respecting bases argument.
0.8.2¶
16 September 2015- fixed
sessionWrong file type set on component when publishing image sequence using
Session.create_component()
.
0.8.1¶
8 September 2015- fixed
session
Session.ensure()
not implemented.
0.8.0¶
28 August 2015- changed
server versionftrack server version >= 3.2.1, < 3.3 required.
- new
Added lists example.
See also
- new
Added convenience methods for handling timers
start_timer
andstop_timer
. - changed
The dynamic API classes Type, Status, Priority and StatusType have been renamed to Type, Status, Priority and State.
- changed
Session.reset()
now also clears the top most level cache (by default aMemoryCache
). - fixed
Some invalid server url formats not detected.
- fixed
Reply events not encoded correctly causing them to be misinterpreted by the server.
0.7.0¶
24 August 2015- changed
server versionftrack server version >= 3.2, < 3.3 required.
- changed
Removed automatic set of default statusid, priorityid and typeid on objects as that is now either not mandatory or handled on server.
- changed
Updated
get_statuses()
andget_types()
to handle custom objects.
0.6.0¶
19 August 2015- changed
server versionftrack server version >= 3.1.8, < 3.2 required.
- changed
documentation Updated documentation with details on new operators
has
andany
for querying relationships.See also
0.5.2¶
29 July 2015- changed
server versionftrack server version 3.1.5 or greater required.
- changed
Server reported errors are now more readable and are no longer sometimes presented as an HTML page.
0.5.1¶
6 July 2015- changed
Defaults computed by
StandardFactory
are now memoised per session to improve performance. - changed
Memoiser
now supports a return_copies parameter to control whether deep copies should be returned when a value was retrieved from the cache.
0.5.0¶
2 July 2015- changed
Now checks for server compatibility and requires an ftrack server version of 3.1 or greater.
- new
Added convenience methods to
QueryResult
to fetchfirst()
or exactlyone()
result. - new
notesAdded support for handling notes.
See also
- changed
Collection attributes generate empty collection on first access when no remote value available. This allows interacting with a collection on a newly created entity before committing.
- fixed
sessionAmbiguous error raised when
Session
is started with an invalid user or key. - fixed
cachingsession
Session.merge()
fails againstSerialisedCache
when circular reference encountered due to entity identity not being prioritised in merge.
0.4.3¶
29 June 2015- fixed
entity typespluginssessionEntity types not constructed following standard install.
This is because the discovery of the default plugins is unreliable across Python installation processes (pip, wheel etc). Instead, the default plugins have been added as templates to the Event list documentation and the
StandardFactory
used to create any missing classes onSession
startup.
0.4.2¶
26 June 2015- fixed
Setting exact same metadata twice can causeImmutableAttributeError
to be incorrectly raised. - fixed
sessionCalling
Session.commit()
does not clear locally set attribute values leading to immutability checks being bypassed in certain cases.
0.4.1¶
25 June 2015- fixed
Setting metadata twice in one session causes KeyError.
0.4.0¶
22 June 2015- changed
documentationDocumentation extensively updated.
- new
Client reviewAdded support for handling review sessions.
See also
- fixed
Metadata property not working in line with rest of system, particularly the caching framework.
- new
collectionAdded
ftrack_api.collection.MappedCollectionProxy
class for providing a dictionary interface to a standardftrack_api.collection.Collection
. - new
attributecollectionAdded
ftrack_api.attribute.MappedCollectionAttribute
class for describing an attribute that should use theftrack_api.collection.MappedCollectionProxy
. - new
Entities that use composite primary keys are now fully supported in the session, including for
Session.get()
andSession.populate()
. - change
Base
ftrack_api.entity.factory.Factory
refactored to separate out attribute instantiation into dedicated methods to make extending simpler. - change
attributecollection
ftrack_api.attribute.DictionaryAttribute
andftrack_api.attribute.DictionaryAttributeCollection
removed. They have been replaced by the newftrack_api.attribute.MappedCollectionAttribute
andftrack_api.collection.MappedCollectionProxy
respectively. - new
events
Session
now supports an auto_connect_event_hub argument to control whether the built in event hub should connect to the server on session initialisation. This is useful for when only local events should be supported or when the connection should be manually controlled.
0.3.0¶
14 June 2015- fixed
Session operations may be applied server side in invalid order resulting in unexpected error.
- fixed
Creating and deleting an entity in single commit causes error as create operation never persisted to server.
Now all operations for the entity are ignored on commit when this case is detected.
- changed
Internally moved from differential state to operation tracking for determining session changes when persisting.
- new
Session.recorded_operations
attribute for examining current pending operations on aSession
. - new
Session.operation_recording()
context manager for suspending recording operations temporarily. Can also manually controlSession.record_operations
boolean. - new
Operation classes to track individual operations occurring in session.
- new
Public
Session.merge()
method for merging arbitrary values into the session manually. - changed
An entity’s state is now computed from the operations performed on it and is no longer manually settable.
- changed
Entity.state
attribute removed. Instead use the new inspectionftrack_api.inspection.state()
.Previously:
print entity.state
Now:
import ftrack_api.inspection print ftrack_api.inspection.state(entity)
There is also an optimised inspection,
ftrack_api.inspection.states()
. for determining state of many entities at once. - changed
Shallow copying a
ftrack_api.symbol.Symbol
instance now returns same instance.
0.2.0¶
4 June 2015- changed
Changed name of API from ftrack to ftrack_api.
See also
- new
caching
Session.get()
now tries to retrieve matching entity from configured cache first. - new
cachingserialisation
Session.encode()
supports a new mode persisted_only that will only encode persisted attribute values. - changed
Session.merge method is now private (
Session._merge()
) until it is qualified for general usage. - changed
entity stateEntity states are now
ftrack_api.symbol.Symbol
instances rather than strings.Previously:
entity.state = 'created'
Now:
entity.state = ftrack_api.symbol.CREATED
- fixed
entity stateIt is now valid to transition from most entity states to an
ftrack_api.symbol.NOT_SET
state. - changed
caching
EntityKeyMaker
removed and replaced byStringKeyMaker
. Entity identity now computed separately and passed to key maker to allow key maker to work with non entity instances. - fixed
entityInternal data keys ignored when re/constructing entities reducing distracting and irrelevant warnings in logs.
- fixed
entity
Entity
equality test raises error when other is not an entity instance. - changed
cachingentity
merge()
now also merges state and local attributes. In addition, it ensures values being merged have also been merged into the session and outputs more log messages. - fixed
inspection
ftrack_api.inspection.identity()
returns different result for same entity depending on whether entity type is unicode or string. - fixed
ftrack_api.mixin()
causes method resolution failure when same class mixed in multiple times. - changed
Representations of objects now show plain id rather than converting to hex.
- fixed
eventsEvent hub raises TypeError when listening to ftrack.update events.
- fixed
events
ftrack_api.event.hub.EventHub.subscribe()
fails when subscription argument contains special characters such as @ or +. - fixed
collection
ftrack_api.collection.Collection()
incorrectly modifies entity state on initialisation.
0.1.0¶
25 March 2015- changed
Moved standardised construct entity type logic to core package (as part of the
StandardFactory
) for easier reuse and extension.
0.1.0-beta.2¶
17 March 2015- new
locationsSupport for ftrack.server location. The corresponding server build is required for it to function properly.
- new
locationsSupport for managing components in locations has been added. Check out the dedicated tutorial.
- new
A new inspection API (
ftrack_api.inspection
) has been added for extracting useful information from objects in the system, such as the identity of an entity. - changed
Entity.primary_key
andEntity.identity
have been removed. Instead, use the newftrack_api.inspection.primary_key()
andftrack_api.inspection.identity()
functions. This was done to make it clearer the the extracted information is determined from the current entity state and modifying the returned object will have no effect on the entity instance itself. - changed
ftrack_api.inspection.primary_key()
now returns a mapping of the attribute names and values that make up the primary key, rather than the previous behaviour of returning a tuple of just the values. To emulate previous behaviour do:ftrack_api.inspection.primary_key(entity).values()
- changed
Session.encode()
now supports different strategies for encoding entities via the entity_attribute_strategy* keyword argument. This makes it possible to use this method for general serialisation of entity instances. - changed
Encoded referenced entities are now a mapping containing __entity_type__ and then each key, value pair that makes up the entity’s primary key. For example:
{ '__entity_type__': 'User', 'id': '8b90a444-4e65-11e1-a500-f23c91df25eb' }
- changed
Session.decode()
no longer automatically adds decoded entities to theSession
cache making it possible to use decode independently. - new
Added
Session.merge()
for merging entities recursively into the session cache. - fixed
Replacing an entity in a
ftrack_api.collection.Collection
with an identical entity no longer raisesftrack_api.exception.DuplicateItemInCollectionError
.
Migration notes¶
Note
Migrating from the old ftrack API? Read the dedicated guide.
Migrate to 2.0.0¶
Default behavior for connecting to event hub¶
The default behavior for the ftrack_api.Session class has changed for the argument auto_connect_event_hub, the default value has switched from True to False. In order for code relying on the event hub to continue functioning as expected you must modify your code to explicitly set the argument to True or that you manually call session.event_hub.connect().
Note
If you rely on the ftrack.location.component-added or ftrack.location.component-removed events to further process created or deleted components remember that your session must be connected to the event hub for the events to be published.
Migrate to 1.0.3¶
Mutating custom attribute dictionary¶
Custom attributes can no longer be set by mutating entire dictionary:
# This will result in an error.
task['custom_attributes'] = dict(foo='baz', bar=2)
session.commit()
Instead the individual values should be changed:
# This works better.
task['custom_attributes']['foo'] = 'baz'
task['custom_attributes']['bar'] = 2
session.commit()
Migrate to 1.0.0¶
Chunked accessor transfers¶
Data transfers between accessors is now buffered using smaller chunks instead of
all data at the same time. Included accessor file representations such as
ftrack_api.data.File
and ftrack_api.accessor.server.ServerFile
are built to handle that. If you have written your own accessor and file
representation you may have to update it to support multiple reads using the
limit parameter and multiple writes.
Migrate to 0.2.0¶
New API name¶
In this release the API has been renamed from ftrack to ftrack_api. This is to allow both the old and new API to co-exist in the same environment without confusion.
As such, any scripts using this new API need to be updated to import ftrack_api instead of ftrack. For example:
Previously:
import ftrack
import ftrack.formatter
...
Now:
import ftrack_api
import ftrack_api.formatter
...
Migrating from old API¶
Why a new API?¶
With the introduction of Workflows, ftrack is capable of supporting a greater diversity of industries. We’re enabling teams to closely align the system with their existing practices and naming conventions, resulting in a tool that feels more natural and intuitive. The old API was locked to specific workflows, making it impractical to support this new feature naturally.
We also wanted this new flexibility to extend to developers, so we set about redesigning the API to fully leverage the power in the system. And while we had the wrenches out, we figured why not go that extra mile and build in some of the features that we see developers having to continually implement in-house across different companies - features such as caching and support for custom pipeline extensions. In essence, we decided to build the API that, as pipeline developers, we had always wanted from our production tracking and asset management systems. We think we succeeded, and we hope you agree.
Installing¶
Before, you used to download the API package from your ftrack instance. With each release of the new API we make it available on PyPi, and installing is super simple:
pip install ftrack-python-api
Before installing, it is always good to check the latest Release Notes to see which version of the ftrack server is required.
See also
Overview¶
An API needs to be approachable, so we built the new API to feel intuitive and familiar. We bundle all the core functionality into one place – a session – with consistent methods for interacting with entities in the system:
import ftrack_api
session = ftrack_api.Session()
The session is responsible for loading plugins and communicating with the ftrack
server and allows you to use multiple simultaneous sessions. You will no longer
need to explicitly call ftrack.setup()
to load plugins.
The core methods are straightforward:
- Session.create
- create a new entity, like a new version.
- Session.query
- fetch entities from the server using a powerful query language.
- Session.delete
- delete existing entities.
- Session.commit
- commit all changes in one efficient call.
Note
The new API batches create, update and delete operations by default for
efficiency. To synchronise local changes with the server you need to call
Session.commit()
.
In addition all entities in the API now act like simple Python dictionaries, with some additional helper methods where appropriate. If you know a little Python (or even if you don’t) getting up to speed should be a breeze:
>>> print user.keys()
['first_name', 'last_name', 'email', ...]
>>> print user['email']
'old@example.com'
>>> user['email'] = 'new@example.com'
And of course, relationships between entities are reflected in a natural way as well:
new_timelog = session.create('Timelog', {...})
task['timelogs'].append(new_timelog)
See also
The new API also makes use of caching in order to provide more efficient retrieval of data by reducing the number of calls to the remote server.
See also
Open source and standard code style¶
The new API is open source software and developed in public at Bitbucket. We welcome you to join us in the development and create pull requests there.
In the new API, we also follow the standard code style for Python,
PEP-8. This means that you will now find that methods and variables are
written using snake_case
instead of camelCase
, amongst other things.
Package name¶
The new package is named ftrack_api
. By using a new package name, we
enable you to use the old API and the new side-by-side in the same process.
Old API:
import ftrack
New API:
import ftrack_api
Specifying your credentials¶
The old API used three environment variables to authenticate with your ftrack instance. While these continue to work as before, you now also have the option to specify them when initializing the session:
>>> import ftrack_api
>>> session = ftrack_api.Session(
... server_url='https://mycompany.ftrackapp.com',
... api_key='7545384e-a653-11e1-a82c-f22c11dd25eq',
... api_user='martin'
... )
In the examples below, will assume that you have imported the package and created a session.
See also
Querying objects¶
The old API relied on predefined methods for querying objects and constructors which enabled you to get an entity by it’s id or name.
Old API:
project = ftrack.getProject('dev_tutorial')
task = ftrack.Task('8923b7b3-4bf0-11e5-8811-3c0754289fd3')
user = ftrack.User('jane')
New API:
project = session.query('Project where name is "dev_tutorial"').one()
task = session.get('Task', '8923b7b3-4bf0-11e5-8811-3c0754289fd3')
user = session.query('User where username is "jane"').one()
While the new API can be a bit more verbose for simple queries, it is much more powerful and allows you to filter on any field and preload related data:
tasks = session.query(
'select name, parent.name from Task '
'where project.full_name is "My Project" '
'and status.type.short is "DONE" '
'and not timelogs any ()'
).all()
The above fetches all tasks for “My Project” that are done but have no timelogs. It also pre-fetches related information about the tasks parent – all in one efficient query.
See also
Creating objects¶
In the old API, you create objects using specialized methods, such as
ftrack.createProject()
, Project.createSequence()
and
Task.createShot()
.
In the new API, you can create any object using Session.create()
. In
addition, there are a few helper methods to reduce the amount of boilerplate
necessary to create certain objects. Don’t forget to call Session.commit()
once you have issued your create statements to commit your changes.
As an example, let’s look at populating a project with a few entities.
Old API:
project = ftrack.getProject('migration_test')
# Get default task type and status from project schema
taskType = project.getTaskTypes()[0]
taskStatus = project.getTaskStatuses(taskType)[0]
sequence = project.createSequence('001')
# Create five shots with one task each
for shot_number in xrange(10, 60, 10):
shot = sequence.createShot(
'{0:03d}'.format(shot_number)
)
shot.createTask(
'Task name',
taskType,
taskStatus
)
New API:
project = session.query('Project where name is "migration_test"').one()
# Get default task type and status from project schema
project_schema = project['project_schema']
default_shot_status = project_schema.get_statuses('Shot')[0]
default_task_type = project_schema.get_types('Task')[0]
default_task_status = project_schema.get_statuses(
'Task', default_task_type['id']
)[0]
# Create sequence
sequence = session.create('Sequence', {
'name': '001',
'parent': project
})
# Create five shots with one task each
for shot_number in xrange(10, 60, 10):
shot = session.create('Shot', {
'name': '{0:03d}'.format(shot_number),
'parent': sequence,
'status': default_shot_status
})
session.create('Task', {
'name': 'Task name',
'parent': shot,
'status': default_task_status,
'type': default_task_type
})
# Commit all changes to the server.
session.commit()
If you test the example above, one thing you might notice is that the new API is much more efficient. Thanks to the transaction-based architecture in the new API only a single call to the server is required to create all the objects.
See also
Updating objects¶
Updating objects in the new API works in a similar way to the old API. Instead
of using the set()
method on objects, you simply set the key of the
entity to the new value, and call Session.commit()
to persist the
changes to the database.
The following example adjusts the duration and comment of a timelog for a user using the old and new API, respectively.
Old API:
import ftrack
user = ftrack.User('john')
user.set('email', 'john@example.com')
New API:
import ftrack_api
session = ftrack_api.Session()
user = session.query('User where username is "john"').one()
user['email'] = 'john@example.com'
session.commit()
See also
Date and datetime attributes¶
In the old API, date and datetime attributes where represented using a standard
datetime
object. In the new API we have opted to use the arrow
library instead. Datetime attributes are represented in the server timezone,
but with the timezone information stripped.
Old API:
>>> import datetime
>>> task_old_api = ftrack.Task(task_id)
>>> task_old_api.get('startdate')
datetime.datetime(2015, 9, 2, 0, 0)
>>> # Updating a datetime attribute
>>> task_old_api.set('startdate', datetime.date.today())
New API:
>>> import arrow
>>> task_new_api = session.get('Task', task_id)
>>> task_new_api['start_date']
<Arrow [2015-09-02T00:00:00+00:00]>
>>> # In the new API, utilize the arrow library when updating a datetime.
>>> task_new_api['start_date'] = arrow.utcnow().floor('day')
>>> session.commit()
Custom attributes¶
In the old API, custom attributes could be retrieved from an entity by using
the methods get()
and set()
, like standard attributes. In the new
API, custom attributes can be written and read from entities using the
custom_attributes
property, which provides a dictionary-like interface.
Old API:
>>> task_old_api = ftrack.Task(task_id)
>>> task_old_api.get('my_custom_attribute')
>>> task_old_api.set('my_custom_attribute', 'My new value')
New API:
>>> task_new_api = session.get('Task', task_id)
>>> task_new_api['custom_attributes']['my_custom_attribute']
>>> task_new_api['custom_attributes']['my_custom_attribute'] = 'My new value'
For more information on working with custom attributes and existing limitations, please see:
See also
Using both APIs side-by-side¶
With so many powerful new features and the necessary support for more flexible workflows, we chose early on to not limit the new API design by necessitating backwards compatibility. However, we also didn’t want to force teams using the existing API to make a costly all-or-nothing switchover. As such, we have made the new API capable of coexisting in the same process as the old API:
import ftrack
import ftrack_api
In addition, the old API will continue to be supported for some time, but do note that it will not support the new Workflows and will not have new features back ported to it.
In the first example, we obtain a task reference using the old API and then use the new API to assign a user to it:
import ftrack
import ftrack_api
# Create session for new API, authenticating using envvars.
session = ftrack_api.Session()
# Obtain task id using old API
shot = ftrack.getShot(['migration_test', '001', '010'])
task = shot.getTasks()[0]
task_id = task.getId()
user = session.query(
'User where username is "{0}"'.format(session.api_user)
).one()
session.create('Appointment', {
'resource': user,
'context_id': task_id,
'type': 'assignment'
})
The second example fetches a version using the new API and uploads and sets a thumbnail using the old API:
import arrow
import ftrack
# fetch a version published today
version = session.query(
'AssetVersion where date >= "{0}"'.format(
arrow.now().floor('day')
)
).first()
# Create a thumbnail using the old api.
thumbnail_path = '/path/to/thumbnail.jpg'
version_old_api = ftrack.AssetVersion(version['id'])
thumbnail = version_old_api.createThumbnail(thumbnail_path)
# Also set the same thumbnail on the task linked to the version.
task_old_api = ftrack.Task(version['task_id'])
task_old_api.setThumbnail(thumbnail)
Note
It is now possible to set thumbnails using the new API as well, for more info see Working with thumbnails.
Plugin registration¶
To make event and location plugin register functions work with both old and new
API the function should be updated to validate the input arguments. For old
plugins the register method should validate that the first input is of type
ftrack.Registry
, and for the new API it should be of type
ftrack_api.session.Session
.
If the input parameter is not validated, a plugin might be mistakenly registered twice, since both the new and old API will look for plugins the same directories.
Example: publishing a new version¶
In the following example, we look at migrating a script which publishes a new version with two components.
Old API:
# Query a shot and a task to create the asset against.
shot = ftrack.getShot(['dev_tutorial', '001', '010'])
task = shot.getTasks()[0]
# Create new asset.
asset = shot.createAsset(name='forest', assetType='geo')
# Create a new version for the asset.
version = asset.createVersion(
comment='Added more leaves.',
taskid=task.getId()
)
# Get the calculated version number.
print version.getVersion()
# Add some components.
previewPath = '/path/to/forest_preview.mov'
previewComponent = version.createComponent(path=previewPath)
modelPath = '/path/to/forest_mode.ma'
modelComponent = version.createComponent(name='model', path=modelPath)
# Publish.
asset.publish()
# Add thumbnail to version.
thumbnail = version.createThumbnail('/path/to/forest_thumbnail.jpg')
# Set thumbnail on other objects without duplicating it.
task.setThumbnail(thumbnail)
New API:
# Query a shot and a task to create the asset against.
shot = session.query(
'Shot where project.name is "dev_tutorial" '
'and parent.name is "001" and name is "010"'
).one()
task = shot['children'][0]
# Create new asset.
asset_type = session.query('AssetType where short is "geo"').first()
asset = session.create('Asset', {
'parent': shot,
'name': 'forest',
'type': asset_type
})
# Create a new version for the asset.
status = session.query('Status where name is "Pending"').one()
version = session.create('AssetVersion', {
'asset': asset,
'status': status,
'comment': 'Added more leaves.',
'task': task
})
# In the new API, the version number is not set until we persist the changes
print 'Version number before commit: {0}'.format(version['version'])
session.commit()
print 'Version number after commit: {0}'.format(version['version'])
# Add some components.
preview_path = '/path/to/forest_preview.mov'
preview_component = version.create_component(preview_path, location='auto')
model_path = '/path/to/forest_mode.ma'
model_component = version.create_component(model_path, {
'name': 'model'
}, location='auto')
# Publish. Newly created version defaults to being published in the new api,
# but if set to false you can update it by setting the key on the version.
version['is_published'] = True
# Persist the changes
session.commit()
# Add thumbnail to version.
thumbnail = version.create_thumbnail(
'/path/to/forest_thumbnail.jpg'
)
# Set thumbnail on other objects without duplicating it.
task['thumbnail'] = thumbnail
session.commit()
Workarounds for missing convenience methods¶
Query object by path¶
In the old API, there existed a convenience methods to get an object by referencing the path (i.e object and parent names).
Old API:
shot = ftrack.getShot(['dev_tutorial', '001', '010'])
New API:
shot = session.query(
'Shot where project.name is "dev_tutorial" '
'and parent.name is "001" and name is "010"'
)
Retrieving an object’s parents¶
To retrieve a list of an object’s parents, you could call the method
getParents()
in the old API. Currently, it is not possible to fetch this
in a single call using the new API, so you will have to traverse the ancestors
one-by-one and fetch each object’s parent.
Old API:
parents = task.getParents()
New API:
parents = []
for item in task['link'][:-1]:
parents.append(session.get(item['type'], item['id']))
Note that link includes the task itself so [:-1] is used to only retreive the parents. To learn more about the link attribute, see Using link attributes example.
Limitations in the current version of the API¶
The new API is still quite young and in active development and there are a few limitations currently to keep in mind when using it.
Missing schemas¶
The following entities are as of the time of writing not currently available in the new API. Let us know if you depend on any of them.
- Booking
- Calendar and Calendar Type
- Dependency
- Manager and Manager Type
- Phase
- Role
- Task template
- Temp data
Action base class¶
There is currently no helper class for creating actions using the new API. We will add one in the near future.
In the meantime, it is still possible to create actions without the base class by listening and responding to the ftrack.action.discover and ftrack.action.launch events.
Legacy location¶
The ftrack legacy disk locations utilizing the
InternalResourceIdentifierTransformer
has been deprecated.
Glossary¶
- accessor
An implementation (typically a Python plugin) for accessing a particular type of storage using a specific protocol.
See also
- action
Actions in ftrack provide a standardised way to integrate other tools, either off-the-shelf or custom built, directly into your ftrack workflow.
See also
- api
- Application programming interface.
- arrow
- A Python library that offers a sensible, human-friendly approach to creating, manipulating, formatting and converting dates, times, and timestamps. Read more at http://crsmithdev.com/arrow/
- asset
- A container for asset versions, typically representing the output from an artist. For example, ‘geometry’ from a modeling artist. Has an asset type that categorises the asset.
- asset type
- Category for a particular asset.
- asset version
- A specific version of data for an asset. Can contain multiple components.
- component
- A container to hold any type of data (such as a file or file sequence). An asset version can have any number of components, each with a specific name. For example, a published version of geometry might have two components containing the high and low resolution files, with the component names as ‘hires’ and ‘lowres’ respectively.
- PEP-8
- Style guide for Python code. Read the guide at https://www.python.org/dev/peps/pep-0008/
- plugin
Python plugins are used by the API to extend it with new functionality, such as locations or actions.
See also
- python
- A programming language that lets you work more quickly and integrate your systems more effectively. Often used in creative industries. Visit the language website at http://www.python.org
- PyPi
- Python package index. The Python Package Index or PyPI is the official third-party software repository for the Python programming language. Visit the website at https://pypi.python.org/pypi
- resource identifier
A string that is stored in ftrack as a reference to a resource (such as a file) in a specific location. Used by accessors to determine how to access data.
See also