18. Expression Language

18.1. TACTIC Expression Language Introduction

TACTIC Expression Language Introduction

Introduction

This document describes the construct of the TEL TACTIC Expression Language. This language is a shorthand form to quickly retrieve information about related Search Objects. The expression either starts with a list of Search Objects and the operations of the expression language operate on these lists (this is quite similar to LISP in concept) or it can be used as an absolute search.

The expression language also borrows from spreadsheet syntax which is familiar to many people. The reason behind using an expression language is that it is much simpler and compact that using code or direct SQL. The TACTIC expression language is designed to be able to easily retrieve data in a single command that would otherwise take many lines of code.

Simple Example

The expression often starts with a list of Search Objects and then operates on these Search Objects.

If you have a list of "prod/sequence" Search Objects, then the following:

@GET(prod/shot.code)

will return a list of codes of these prod/shot Search Objects related to the starting "prod/sequence". The notation for the method GET is of the form <search_type>.<column>. As will be shown below, multiple search_types can be strung together to navigate across related search_types. The @GET function operate on a list and returned a list.

If no starting sObject is given, this expression will return a list of codes for every shot in the project. In the python or javascript API, you can control whether there is a starting sobject with the kwarg search_keys

With the above example, here is how to get the shot codes for the given sequence with the code "seq001":

Python API

server = TacticServerStub.get()

expr = "@GET(prod/shot.code)"

result = server.eval(expr, search_keys=[prod/sequence?project=vfx&code=seq001])

By default, the result returned is a list unless you specify the kwarg single=True in server.eval()

result = server.eval(expr, search_keys=[prod/sequence?project=vfx&code=seq001], single=True)

In Javascript, via the Script Edtor, you can achieve the same result with these scripts:

Javascript API

var server = TacticServerStub.get();

var expr = "@GET(prod/shot.code)"

var result = server.eval(expr, {search_keys: [prod/sequence?project=vfx&code=seq001], single: true});

In certain places, like in a Custom Layout Element, Expression Element in a Table, or notification set-up, there is an assumed starting sObject, which is the one you are viewing or the notification event refers to during an update or insert action.

Searching

The expression language can be used as a shorthand for search for Search Objects. This is often convenient because the expression language is a pure string and can be stored a number of formats, including XML.

The @SOBJECT method will retrieve entire Search Objects.

Search for all assets

@SOBJECT(prod/asset)

Search only for characters by applying a filter

@SOBJECT(prod/asset['asset_library','chr']

You can also apply multiple filters. And operation is implied

@SOBJECT(prod/asset['asset_library','chr']['timestamp','>','2009-01-01'])

You can also apply multiple filters. To use OR operation with more than 2 filters. For example, with code containing the word prop1, OR asset_library is chr, OR timestamp after 2009-01-01. Note: EQ stands for case-sensitive match.

@SOBJECT(prod/asset['begin']['asset_library','chr']['timestamp','>','2009-01-01']['code','EQ','prop1']['or'])

To use OR operation with 2 filters followed by an AND operation. For example, with asset_library is chr OR timestamp after 2009-01-01 AND code containing the word prop1. If there are only 2 filters, there is no need to sandwich it with begin.

@SOBJECT(prod/asset['begin']['asset_library','chr']['timestamp','>','2009-01-01']['or']['code','EQ','prop1'])

To

@SOBJECT(prod/asset['begin']['asset_library','chr']['timestamp','>','2009-01-01']['or']['code','EQ','prop1'])

Note that full filter operations from the Client API are supported.

Navigating Search Types

One of the true powers of the expression language is the simplicity in which it can navigate between various related Search Types using a navigational syntax. The expression language makes use of the project schema to navigate dependencies between the search_types. For example a sequence is related to a shot.

The navigational syntax is used as arguments for many aggregate methods. When detected, the expression language will perform a search through the hierarchy to retrieve the desired search results.

A simple example of the navigation syntax in the expression language is as follows:

@GET(prod/sequence.code)

This expression will get all of the codes of the sequences of related to each Search Object.

The expression can also navigate multiple levels of search types to dig deeply into the hierarchy. For example, this will get all of the descriptions of all of the episodes that belong to the sequences of the original shots.

@GET(prod/sequence.prod/episode.description)

Another useful illustration is to get all of the tasks of all of the shots:

@SOBJECT(prod/shot.sthpw/task)

Get the last 50 tasks ordered by process of all of the shots:

@SOBJECT(prod/shot.sthpw/task['@ORDER_BY','process']['@LIMIT','50'])

Aggregate functions

The expression language defines a number of aggregate functions which will operate on the list.

This will give the addition of all the duration attributes of the provided shots.

@SUM(prod/shot.duration)

This will give the average duration attribute of all of the shots.

@AVG(prod/shot.duration)

This will give a count of all of the Search Objects

@COUNT(prod/shot)

All of these aggregates return a single value which can be used to operate on other lists.

Operations

The expression language operates on lists. The operator will operate on each element of the list independently and return a list For example when doing a subtraction operation on items:

@GET(prod/shot.end_frame) - @GET(prod/shot.start_frame)

The first @GET will return a list of start frames and the second @GET will return a list end frames. When two lists are operated on the results are calculated based on items at the same position in each list. So if we had two lists:

[300, 155, 100] - [100, 100, 100] = [200, 55, 0]

Similarly, lists will be multiplied as follows

[5, 4, 3] * [5, 4, 3] = [25, 16, 9]

The expression language supports most operation support by the python language itself.

>>> Search.eval("5 * 25")
125.0

>>> Search.eval("5 + 25")
30.0

>>> Search.eval("(25 - 5) * 5")
100.0

>>> Search.eval("5 / 25") 0.20000000000000001

>>> Search.eval("@COUNT(sthpw/task) * 5")
2310.0

>>> Search.eval("@COUNT(sthpw/task) > 0")
True

>>> Search.eval("@COUNT(sthpw/task) == 462")
True

>>> Search.eval("@COUNT(sthpw/task) != 462")
False

The expression language also supports the regular expression syntax

The following tests whether the name_first column starts with "John"

@GET(.name_first) ~ '^John'

More complex operations

You can do more complex operations by combining the above. The following will return a cost list of all of the shots (assigned user wage * number of hours worked).

@GET(prod/shot.sthpw/task.sthpw/login.wage) * @GET(prod/shot.num_hours)

You could add them all together using @SUM this to get the total

@SUM(
  @GET(prod/shot.sthpw/task.sthpw/login.wage) * @GET(prod/shot.num_hours)
)

There are times the sObjects returned are not unique. The @UNIQUE operator can be used to return a unique list of result. The following returns the unique list of login sObjects related to the task list provided. The @COUNT operator computes the total number of login sObjects.

 # my.tasks is a list of tasks
 expression = "@COUNT(@UNIQUE(@SOBJECT(sthpw/login)))"
 result = my.parser.eval(expression, my.tasks)

Manipulating Strings

Most of the operations in the expression language operate on lists and either return lists or return single values. However, it is often required that expressions be used in string concatenation. A simplified notation is to use curly brackets {} to represent an operation that converts the result of an expression into a string.

For a file to be named chr001_model.png, we could use:

{@GET(prod/asset.code)}_{@GET(sthpw/snapshot.context)}.png
  • The file naming conventions do not current use the expression language. The presently use a simplified expression language. The plan is to merge the two at some point.

String Formatting

For string values, the string operator them can use standard print formatting:

v{@GET(sthpw/snapshot.version),%0.3d}

will return "v012", for example.

The expression language also supports formatting through regular expressions

{ @GET(prod/asset.description),|^(\w{5})| }

This will get the first 5 word characters for the description. Since the full expression language is supported, it is possible to extract a wide variety of parts. Anything matched with () will be returned as the value.

**If there are multiple groupings, the expression language will concatenate the values together.

The following will return the first 3 and last 3 characters of the description.

{ @GET(prod/asset.description),|^(\w{3}).*(\w{3})$| }

The following will return the last 5 characters of the description of the current SObject even if it is written in French or Chinese.

{ @GET(.description),|^(.{5})$| }

Time related formatting

The following formats a timestamp by extracting just the month and date (old way):

{ @GET(.timestamp), %b-%m}

The following formats a timestamp by extracting just the year

{ @GET(.timestamp), %Y}

The following removes the hours, minutes and seconds from the built-in $TODAY variable so only 2011-11-11 is displayed

{ $TODAY,|([^\s]+)| }

The following formats a timestamp by using the new @FORMAT function

@FORMAT( @GET(.timestamp), '31/12/1999')
@FORMAT( @GET(.timestamp), 'Dec 31, 1999')

The following formats it according to a project wide date-time setting or date-only setting. You can define what DATETIME and DATE is in the Project Settings page.

@FORMAT( @GET(.timestamp), 'DATETIME')
@FORMAT( @GET(.timestamp), 'DATE')

Either of the following formats a frame count into timecode

@FORMAT( @GET(.frame_count), 'MM:SS.FF')

The following formats a frame count into hours, minutes and seconds in 30fps, leaving out the frames.

@FORMAT( @GET(.frame_count), 'HH:MM:SS', '30')

The following formats a cost column in currency format

@FORMAT(@GET(.cost), '-$1,234.00')

31/12/99 13:37 can be used to show both date and time

Shorthand (mostly for backwards compatibility)

@GET(sobject.end_frame) - @GET(sobject.start_frame)

or

@GET(.end_frame) - @GET(.start_frame)

Or replicate file naming conventions

{sobject.code}_{snapshot.context}_v{version}.{ext}

In the file naming convention language, the are a number of short hand keywords:

sObject Keywords: sobject, snapshot, file, parent , search_type

Attribute Keywords: context, version, ext, basefile

18.2. Expression Method Reference

Expression Method Reference

GET

@GET( [search]:nav )

v2.5.0+

The GET method will retrieve attributes or columns from a list of SObjects. This method returns a list of the values. The first argument supports the search type navigational syntax to travel through related search types.

Get the bid start date of all of the tasks:

@GET(sthpw/task.bid_start_date)

Get the assigned user of all of the modelling tasks

@GET(sthpw/task['process','model'].assigned)

Get the assigned user of all of the modelling , anim, OR lighting tasks

@GET(sthpw/task['begin']['process','model']['process','anim']['process','lgt']['or'].assigned)

The GET function also supports short hand to get all attributes from the current SObjects. This will get the assigned column for all current SObjects

@GET(.assigned)

GETALL

@GETALL( [search]:nav )

v4.1.0+

The GETALL method works in a similar way to GET; this method also returns a list of the values. Both work identically if the expression given is as simple as (sthpw/login.id). The difference lies in more complex queries:

@GETALL(sthpw/task.sthpw/login.first_name)

The above query returns the name of the user associated with each task. Unlike GET, GETALL will show each user’s name for as many tasks as they are associated with. GET does not display any duplicates.

Example use of GETALL:

@SUM(@GETALL(sthpw/task.sthpw/work_hour.sthpw/login.hourly_wage) *
     @GETALL(sthpw/task.sthpw/work_hour.straight_time))

This example retursns the total cost of a task based on the hours logged and the hourly wage of each employee.

SOBJECT

v2.5.0+

The SOBJECT method is similar to the GET SObject except that the entire search object is retrieved.

The following expression gets all of the completed modelling tasks.

@SOBJECT(sthpw/task['status','complete']['process','model'])

The following expression gets all of the completed tasks OR model tasks.

@SOBJECT(sthpw/task['begin']['status','complete']['process','model']['or'])

The following expression deals with time related attribute. Get the tasks where the bid_end_date is before 2012-02-10 and after 2013-01-08

@SOBJECT(sthpw/task['begin']['bid_end_date','is before','2012-02-10']['bid_end_date','is after','2013-01-08']['or'])

The following expression deals with numbers. You can use >, < , >=,or ⇐.

@SOBJECT(sthpw/task['priority','>=','3'])

The following expression deals with containing a word. You can use EQ, EQI, like. EQ and EQI (case-insensitive) makes use of regular expression engine of the database if available. With "like", you have to make use of the % wildcard

@SOBJECT(sthpw/task['description','like','%rock%'])
@SOBJECT(sthpw/task['description','EQ','rock'])

The following expression deals with NOT containing a word. You can use NEQ, NEQI, not like. NEQ makes use of regular expression engine of the database if available. With "not like", you have to make use of the % wildcard

@SOBJECT(sthpw/task['description','not like','%rock%'])
@SOBJECT(sthpw/task['description','NEQ','rock'])

The SOBJECT method can also traverse thru related sTypes if their relation has been set up in the Project Schema.

The following expression gets all the shots for sequence SE02 and SE03:

@SOBJECT(prod/sequence['code','in','SE02|SE03').prod/shot)

Certain relationships like those between anything to notes or tasks are already pre-established.

The following expression gets all the shots that have a note starting with the word Hello:

@SOBJECT(sthpw/note['note','EQ','^Hello').prod/shot)

COUNT

v2.5.0+

The COUNT method will return the count of the SObject returned by the search specifications.

To get a count of all the tasks:

@COUNT(sthpw/task)

To get a count of all the tasks of all of the shots:

@COUNT(prod/shot.sthpw/task)

To get a count of all the modelling tasks of all of the completed shots

@COUNT(prod/shot['status','complete'].sthpw/task['context','model'])

SUM

v2.5.0+

This method will calculate a sum of all of the values in the first argument. The first argument must conform to the navigational syntax.

AVG

v2.5.0+

Calculates the average of all of the values of the first argument, which must conform to the navigational syntax.

MIN

v2.5.0+

Returns the minimum value in a list

MAX

v2.5.0+

Returns the maximum value in a list

FLOOR

v2.5.0+

Returns the lowest integer value of a passed in value

UNIQUE

@UNIQUE( [expr1]:expr )

v2.5.0+

The UNIQUE method goes through a list returned from an expression and ensures that only unique elements are present. Duplicates are discarded

UNION

@UNION( [expr1]:expr, [expr2]:expr, … )

v2.5.0+

The UNION method combines the union of all of the results from a number of expressions together into a single list.

Combine all the users from accounting and marketing together into one list:

@UNION(
  @SOBJECT(sthpw/login['dept','accounting'],
  @SOBJECT(sthpw/login['dept','marketing']
)

INTERSECT

@INTERSECT( [expr1]:expr, [expr2]:expr )

v2.5.0+

The INTERSECT method takes the intersection of all the results of expressions in the arguments.

@INTERSECT(
  @GET(sthpw/login['dept','supervisor']),
  @GET(sthpw/login['dept','director'])
)

IF

@IF( [condition]:expr, [if_true]:expr, [if_false]:expr )

v2.6.0+

The following example will return red if the number of tasks is greater than 5 and green if less than or equal to 5. These types of expressions are very useful to determine colors of various backgrounds or widgets within TACTIC.

@IF( @COUNT(sthpw/task) > 5, 'red', 'green') )

Not all of the arguments can be expressions, so the values for both is_true and is_false can be expressions that are evaluated:

@IF(
  @COUNT(sthpw/task) > 5, @GET(.color1), @GET(.color2) )
)

CASE

@CASE( [condition1]:expr, [if_true]:expr, [condition2:expr], [if_true]:expr, … )

v2.6.0+

The case statement is an extension of the IF method, but it allows any number of arguments. The odd arguments are conditional tests which must evaluate to True or False. The case method will go through each of the odd arguments until one of the evaluates to True at which point it will evaluate the corresponding even argument and return that value.

@CASE(
  @GET(.age) < 10, 'blue',
  @GET(.age) < 20, 'green',
  @GET(.age) < 30, 'yellow',
  @GET(.age) >= 30, 'red'
)

FOREACH

v2.6.0+

The following expression gets all the first name from the login table as a list. and then loop through and add <li> </li> around each item. This is more suited in situation where you don’t much control over the data returned like in a CustomLayoutWdg:

 @FOREACH( @GET(sthpw/login.first_name), '<li>%s</li>' )

JOIN

@JOIN( [expr]:expression, [delimiter]:literal

v2.6.0+

The join method take the result of an expression and joins all the elements together using a delimiter

UPDATE

@UPDATE( [expr1]:expression, [column]:string, [value]:expression )

v2.6.0+

The UPDATE method provides the ability for an expression to update a value in the database

The following example updates all of the modelling task to approved

@UPDATE( @SOBJECT(sthpw/task['context','model']), 'status', 'Approved' )

You can display a model task status column in the Asset page and any other asset related pages and have them all pointing back to the task search type during an update. It would eliminate any redundant data. The following xml definition can be used to set this up in the asset page for instance:

<element edit='true' name='asset_task_status' title='Task Status'>
  <display widget='expression'>
    <expression>@GET(sthpw/task['context','model'].status)</expression>
  </display>
  <action class='DatabaseAction'>
    <expression>@UPDATE(@SOBJECT(sthpw/task['context','model']), 'status', $VALUE) </expression>
  </action>
</element>

The edit view for the Widget Config of prod/asset needs to contain this snippet to display the selection list of different statuses

  <element name='asset_task_status'>
      <display class='tactic.ui.widget.TaskStatusSelectWdg'/>
  </element>

EVAL

@EVAL( [expr1]:expression )

@( [expr1]:expression )

v2.6.0+

PYTHON

v3.9.0+

It takes one argument, the script path of a script you have defined in the TACTIC Script Editor. For instance, to draw the bid_start_date and bid_end_date of some specific related tasks when the user changes an attribute of a shot, you can define a script called notification/dates and use this expression in the message field of notification.

 @PYTHON(notification/dates)
# notification message displaying shoot schedule
from pyasm.search import Search

expr = "@SOBJECT(sthpw/task['context','minor'])"
tasks = Search.eval(expr, sobjects=[sobject])

dates = []
for task in tasks:
    # assuming they are on the same day
    day_expr = "@FORMAT(@GET(.bid_start_date),'1999-12-31')"
    time_expr1 = "@FORMAT(@GET(.bid_start_date),'01:37 PM')"
    time_expr2 = "@FORMAT(@GET(.bid_end_date),'01:37 PM')"

    day_val= Search.eval(day_expr, sobjects=[task], single=True)
    time_val1= Search.eval(time_expr1, sobjects=[task], single=True)
    time_val2= Search.eval(time_expr2, sobjects=[task], single=True)
    schedule = '%s %s - %s' %(day_val, time_val1, time_val2)

    dates.append(schedule)

return '''
'''.join(dates)

COLOR

@COLOR(attribute[,offset])

v4.1.0+

Returns the current palette’s color for the chosen attribute plus an offset. Offset is a number that can be used to make the color lighter or darker. The attribute can also be a hex value.

Ex: @COLOR(color2, 2) will return the second color for this palette.

GRADIENT

@GRADIENT(attribute[,offset,gradient_range])

v4.1.0+

Returns a CSS gradient value that starts at the attribute + offset and transitions to attribute + offset + gradient_range.

Ex: "@GRADIENT(#777777,3,-4)" will return \[-webkit-gradient(linear, 0% 0%, 0% 100%, from(#7e7e7e), to(#747474))]

PALETTE

@PALETTE([palette_name])

v4.1.0+

Returns a dictionary representing the current palette or a specific palette if the optional argument palette_name is included.

Example return value:

{color: #000, background2: #777777, color3: #333, color2: #333, background: #DDDDDD, shadow: rgba(0,0,0,0.6), border: #888888, table_border: #DDD, background3: #999999, side_bar_title: #3C76C2, theme: default}

18.3. Expression Variable Reference

Expression Variable Reference

There are a number of predefined variables in the expression language. The following list all of the available variables:

  • LOGIN - the login of the current user
  • LOGIN_ID - the login id of the current user
  • LOGINS_IN_GROUP - the group of logins belonging to the group the current user is in
  • PROJECT - code of the current project
  • PROJECT_URL - the URL to the project’s home page (ex: http://10.0.0.65/tactic/media)
  • BASE_URL - The base URL of the TACTIC installaiton (ex: http://10.0.0.65/)

Table 1.

Variable Description Usage

NOW

Current day and time

[multiblock cell omitted]

TODAY

Current day at midnight (12:00 am)

[multiblock cell omitted]

THIS_MINUTE

[multiblock cell omitted]

[multiblock cell omitted]

NEXT_MINUTE

Now + 1 minute

[multiblock cell omitted]

PREV_MINUTE

Now + 1 minute

[multiblock cell omitted]

THIS_HOUR

This hour at 0 minutes

[multiblock cell omitted]

NEXT_HOUR

THIS_HOUR + 1 hour

[multiblock cell omitted]

PREV_HOUR

THIS_HOUR - 1 hour

[multiblock cell omitted]

NEXT_DAY

Today + 1 day

[multiblock cell omitted]

THIS_YEAR

The first day of this year at midnight (12:00am)

[multiblock cell omitted]

NEXT_YEAR

THIS_YEAR + 1 year

[multiblock cell omitted]

PREV_YEAR

THIS_YEAR - year

[multiblock cell omitted]

THIS_MONTH

the first day of this month at midnight (12:00am)

[multiblock cell omitted]

NEXT_MONTH

THIS_MONTH + 1 month

[multiblock cell omitted]

PREV_MONTH

THIS_MONTH - 1 month

[multiblock cell omitted]

NEXT_***DAY

Replace * with a particular day of the week

NEXT_MONDAY: the next day that is a Monday at midnight

PREV_***DAY

Replace * with a particular day of the week

PREV_SATURDAY: the last day that was a Saturday at midnight

**_DAY_AGO

Replace ** with any number between 1 and 12

10_DAY_AGO: today - 10 days

**_DAY_AHEAD

Replace ** with any number between 1 and 12

5_DAY_AHEAD: today + 5 days

**_WEEK_AGO

Replace ** with any number between 1 and 12

same usage as **_DAY_AGO

**_WEEK_AHEAD

Replace ** with any number between 1 and 12

same usage as **_DAY_AHEAD

**_MONTH_AGO

Replace ** with any number between 1 and 12

same usage as **_DAY_AGO

**_MONTH_AHEAD

Replace ** with any number between 1 and 12

same usage as **_DAY_AHEAD

**_YEAR_AGO

Replace ** with any number between 1 and 12

same usage as **_DAY_AGO

**_YEAR_AHEAD

Replace ** with any number between 1 and 12

same usage as **_DAY_AHEAD

These variables can be used for to refer to state information in searches. This expression will retrieve all the login information for the current user.

@GET(sthpw/login['login',$LOGIN])

They can also be used to find items between certain dates. This expression will retrieve all snapshots for this week starting at Sunday.

@GET(sthpw/snapshot['timestamp','>',$LAST_SUNDAY]['timestamp','<',$NEXT_SUNDAY])

The following are shorthands that do not require a starting point or environment sobject. They can be used in an absolute expression:

  • login - the currently logged in user login attribute
  • project - the current project
  • date - a date object with today’s date
  • palette - a palette object used for accessing different attributes of the palette for the current project. e.g. @GET(palette.background) can be used in the css for a Custom Layout Widget

The following are shorthands that require a starting point or environment sobject:

  • parent - the parent of the current related sobject @GET(parent.code)
  • search_type - the sType sobject. e.g. @GET(.search_type.title)
  • connect - the connected sobject registered in the connection sType. Refer to the API methods like connect_sobjects() and get_connected_sobjects()

    To filter down to a particular connected sobject based on the context attribute, which defaults to task, use @CONTEXT.

    e.g. @GET(prod/asset.connect[@CONTEXT,some_task].description)

The following variables are only used in Naming. Refer to the file naming section for details.

  • EXT - file extension
  • BASEFILE - the filename portion of the file without the extension