Create first Maya Workfile

I am looking to procedurally populate a series of shots (an entire sequence) with workfiles for artists. For each shot I would like there to be a v001 workfile in the directory which already has the assets loaded and mocap data applied (I have the code to create them). I just need to save a workfile for them. What is the best way to do that?

For example, this code (in maya) will iterate over each of the shots for the sequence, and create a sphere. How do I save that sphere to being the first workfile for each shot (animation/body task)?

import sgtk

def all_shots(sequence_id=1234):
    e = sgtk.platform.current_engine()
    p = e.context.project
    sequence = { 'type': 'Sequence', 'id': sequence_id }
    return e.shotgun.find("Shot", [('sg_sequence', 'is', sequence), ('project', 'is', p)], ['code'])

def make_shot():
    cmds.file(f=True, new=True)
    cmds.polySphere()

for shot in all_shots():
    make_shot()
    save_maya_shot_workfile(shot)  ?????

Thanks!

Al.

6 Likes

Hi

This is a great question. You would use the sgtk API to resolve the Maya work template into a path for each given context.

I am working on a guide that will explain how to do this, but it’s not ready to be properly shared yet. That said I do have a build of the docs which I will message to you directly.

For the benefit of everyone else I’ll post a snippet of code here.
This code will generate a path for a template given a context.

# Initialization
# ==============

import sgtk
import os

# Get the engine instance that is currently running.
current_engine = sgtk.platform.current_engine()

# Grab the pre created Sgtk instance from the current engine.
tk = current_engine.sgtk

# Get a context object from a Task, this Task must belong to a Shot for the future steps to work. 
context = tk.context_from_entity("Task", 13155)

# Create the required folders based upon the task
tk.create_filesystem_structure("Task", context.task["id"])

# Generating a Path
# =================

# Get a template instance by providing a name of a valid template in your config's templates.yml
template = tk.templates["maya_shot_publish"]

# Use the context to resolve as many of the template fields as possible.
fields = context.as_template_fields(template)

# Manually resolve the remaining fields that can't be figured out automatically from context.
fields["name"] = "myscene"

# Get an authenticated Shotgun API instance from the engine
sg = current_engine.shotgun

# Run a Shotgun API query to summarize the maximum version number on PublishedFiles that
# are linked to the task and match the provided name.
# Since PublishedFiles generated by the Publish app have the extension on the end of the name we need to add the
# extension in our filter.
r = sg.summarize(entity_type="PublishedFile",
                 filters = [["task", "is", {"type":"Task", "id": context.task["id"]}],
                            ["name","is", fields["name"] + ".ma"]],
                 summary_fields=[{"field":"version_number", "type":"maximum"}])

# Extract the version number and add 1 to it.
# In scenarios where there are no files already this summary will return 0.
fields["version"] = r["summaries"]["version_number"] + 1

# Use the fields to resolve the template path into an absolute path.
publish_path = template.apply_fields(fields)

# Make sure we create any missing folders
current_engine.ensure_folder_exists(os.path.dirname(publish_path))
7 Likes

Thanks that is super helpful.

One question… The sg.summarize call will return the highest published version, but I am looking to save a workfile. There may be workfile versions already saved (but not yet published). So presumably I would want the next workfile number, not the next publish number?

Something like this perhaps…?

def workfile(task_id, name=None, version=1):
    if name is None: name = "scene"
    tk = sgtk.platform.current_engine().sgtk
    context = tk.context_from_entity("Task", task_id)
    template = tk.templates['maya_shot_work']
    fields = context.as_template_fields(template)
    fields["name"] = name
    fields['version'] = version
    return template.apply_fields(fields)


def next_workfile(task_id, name=None):
    i = 1
    while os.path.isfile(workfile(task_id, name, version=i)):
        i+=1
        continue

    return workfile(task_id, name, version=i)
2 Likes

Thats also covered in the guide as well, although I chose not to make it part of the final example.
Here is a method that should do what you’re after.

def get_next_version_number(tk, template_name, fields):
    template_work = tk.templates[template_name]

    # Get a list of existing file paths on disk that match the template and provided fields
    # Skip the version field as we want to find all versions not a specific version.
    skip_fields = ["version"]
    work_file_paths = tk.paths_from_template(
                 template_work,
                 fields,
                 skip_fields,
                 skip_missing_optional_keys=True
             )

    versions = []
    for work_file in work_file_paths:
        # extract the values from the path so we can read the version.
        path_fields = template_work.get_fields(work_file)
        versions.append(path_fields["version"])
    
    # find the highest version in the list and add one.
    return max(versions) + 1

get_next_version_number(tk, "maya_shot_work", fields)
1 Like

Sorry yes I was just reading that bit now. This is what I’m using (based on your doc)

def tk():
    return sgtk.platform.current_engine().sgtk

def next_workfile(task_id, name=None, template_name='maya_shot_work'):
    if name is None: name = "scene"
    template = tk().templates[template_name]
    context = tk().context_from_entity("Task", task_id)
    fields = context.as_template_fields(template)
    fields["name"] = name
    fields['version'] = 0

    # Get a list of existing file paths on disk that match the template and provided fields
    # Skip the version field as we want to find all versions not a specific version.
    skip_fields = ["version"]
    work_file_paths = tk().paths_from_template(template, fields, skip_fields, skip_missing_optional_keys=True)

    versions = []
    for work_file in work_file_paths:
        # extract the values from the path so we can read the version.
        path_fields = template.get_fields(work_file)
        versions.append(path_fields["version"])

    # find the highest version in the list and add one.
    fields['version'] = max(versions) + 1
    return template.apply_fields(fields)
2 Likes

Just following up, the guide I was referring to as being worked on, was published here:

1 Like