Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Store task types in the database #572

Merged
merged 12 commits into from
Oct 2, 2020
4 changes: 2 additions & 2 deletions pype/hosts/nukestudio/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ def add_tags_from_presets():
# Get project task types.
tasks = io.find_one({"type": "project"})["config"]["tasks"]
nks_pres_tags["[Tasks]"] = {}
for task in tasks:
nks_pres_tags["[Tasks]"][task["name"]] = {
for task_type in tasks.keys():
nks_pres_tags["[Tasks]"][task_type] = {
"editable": "1",
"note": "",
"icon": {
Expand Down
2 changes: 1 addition & 1 deletion pype/modules/clockify/launcher_actions/ClockifySync.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def process(self, session, **kwargs):

projects_info = {}
for project in projects_to_sync:
task_types = [task['name'] for task in project['config']['tasks']]
task_types = project['config']['tasks'].keys()
projects_info[project['name']] = task_types

clockify_projects = self.clockapi.get_projects()
Expand Down
152 changes: 140 additions & 12 deletions pype/modules/ftrack/lib/avalon_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
from bson.errors import InvalidId
from pymongo import UpdateOne
import ftrack_api
from pype.api import config


log = Logger().get_logger(__name__)


# Current schemas for avalon types
EntitySchemas = {
"project": "avalon-core:project-2.0",
"project": "avalon-core:project-2.1",
"asset": "avalon-core:asset-3.0",
"config": "avalon-core:config-1.0"
"config": "avalon-core:config-1.1"
}

# Group name of custom attributes
Expand All @@ -50,7 +51,7 @@ def check_regex(name, entity_type, in_schema=None, schema_patterns=None):
if in_schema:
schema_name = in_schema
elif entity_type == "project":
schema_name = "project-2.0"
schema_name = "project-2.1"
elif entity_type == "task":
schema_name = "task"

Expand Down Expand Up @@ -103,6 +104,14 @@ def get_pype_attr(session, split_hierarchical=True):


def from_dict_to_set(data):
"""
Converts 'data' into $set part of MongoDB update command.
Args:
data: (dictionary) - up-to-date data from Ftrack

Returns:
(dictionary) - { "$set" : "{..}"}
"""
result = {"$set": {}}
dict_queue = queue.Queue()
dict_queue.put((None, data))
Expand All @@ -114,7 +123,8 @@ def from_dict_to_set(data):
if _key is not None:
new_key = "{}.{}".format(_key, key)

if not isinstance(value, dict):
if not isinstance(value, dict) or \
(isinstance(value, dict) and not bool(value)): # empty dic
result["$set"][new_key] = value
continue
dict_queue.put((new_key, value))
Expand All @@ -123,6 +133,8 @@ def from_dict_to_set(data):

def get_avalon_project_template(project_name):
"""Get avalon template
Args:
project_name: (string)
Returns:
dictionary with templates
"""
Expand All @@ -135,6 +147,16 @@ def get_avalon_project_template(project_name):


def get_project_apps(in_app_list):
"""
Returns metadata information about apps in 'in_app_list' enhanced
from toml files.
Args:
in_app_list: (list) - names of applications

Returns:
tuple (list, dictionary) - list of dictionaries about apps
dictionary of warnings
"""
apps = []
# TODO report
missing_toml_msg = "Missing config file for application"
Expand Down Expand Up @@ -239,6 +261,28 @@ def get_hierarchical_attributes(session, entity, attr_names, attr_defaults={}):
return hier_values


def get_task_short_name(task_type):
"""
Returns short name (code) for 'task_type'. Short name stored in
metadata dictionary in project.config per each 'task_type'.
Could be used in anatomy, paths etc.
If no appropriate short name is found in mapping, 'task_type' is
returned back unchanged.

Currently stores data in:
'pype-config/presets/ftrack/project_defaults.json'
Args:
task_type: (string) - Animation | Modeling ...

Returns:
(string) - anim | model ...
"""
presets = config.get_presets()['ftrack']['project_defaults']\
.get("task_short_names")

return presets.get(task_type, task_type)


class SyncEntitiesFactory:
dbcon = AvalonMongoDB()

Expand Down Expand Up @@ -378,7 +422,7 @@ def launch_setup(self, project_full_name):
"custom_attributes": {},
"hier_attrs": {},
"avalon_attrs": {},
"tasks": []
"tasks": {}
})

for entity in all_project_entities:
Expand All @@ -389,7 +433,9 @@ def launch_setup(self, project_full_name):
continue

elif entity_type_low == "task":
entities_dict[parent_id]["tasks"].append(entity["name"])
# enrich task info with additional metadata
task = {"type": entity["type"]["name"]}
entities_dict[parent_id]["tasks"][entity["name"]] = task
continue

entity_id = entity["id"]
Expand All @@ -416,6 +462,13 @@ def launch_setup(self, project_full_name):

@property
def avalon_ents_by_id(self):
"""
Returns dictionary of avalon tracked entities (assets stored in
MongoDB) accessible by its '_id'
(mongo intenal ID - example ObjectId("5f48de5830a9467b34b69798"))
Returns:
(dictionary) - {"(_id)": whole entity asset}
"""
if self._avalon_ents_by_id is None:
self._avalon_ents_by_id = {}
for entity in self.avalon_entities:
Expand All @@ -425,6 +478,14 @@ def avalon_ents_by_id(self):

@property
def avalon_ents_by_ftrack_id(self):
"""
Returns dictionary of Mongo ids of avalon tracked entities
(assets stored in MongoDB) accessible by its 'ftrackId'
(id from ftrack)
(example '431ee3f2-e91a-11ea-bfa4-92591a5b5e3e')
Returns:
(dictionary) - {"(ftrackId)": "_id"}
"""
if self._avalon_ents_by_ftrack_id is None:
self._avalon_ents_by_ftrack_id = {}
for entity in self.avalon_entities:
Expand All @@ -437,6 +498,13 @@ def avalon_ents_by_ftrack_id(self):

@property
def avalon_ents_by_name(self):
"""
Returns dictionary of Mongo ids of avalon tracked entities
(assets stored in MongoDB) accessible by its 'name'
(example 'Hero')
Returns:
(dictionary) - {"(name)": "_id"}
"""
if self._avalon_ents_by_name is None:
self._avalon_ents_by_name = {}
for entity in self.avalon_entities:
Expand All @@ -446,6 +514,15 @@ def avalon_ents_by_name(self):

@property
def avalon_ents_by_parent_id(self):
"""
Returns dictionary of avalon tracked entities
(assets stored in MongoDB) accessible by its 'visualParent'
(example ObjectId("5f48de5830a9467b34b69798"))

Fills 'self._avalon_archived_ents' for performance
Returns:
(dictionary) - {"(_id)": whole entity}
"""
if self._avalon_ents_by_parent_id is None:
self._avalon_ents_by_parent_id = collections.defaultdict(list)
for entity in self.avalon_entities:
Expand All @@ -458,6 +535,14 @@ def avalon_ents_by_parent_id(self):

@property
def avalon_archived_ents(self):
"""
Returns list of archived assets from DB
(their "type" == 'archived_asset')

Fills 'self._avalon_archived_ents' for performance
Returns:
(list) of assets
"""
if self._avalon_archived_ents is None:
self._avalon_archived_ents = [
ent for ent in self.dbcon.find({"type": "archived_asset"})
Expand All @@ -466,6 +551,14 @@ def avalon_archived_ents(self):

@property
def avalon_archived_by_name(self):
"""
Returns list of archived assets from DB
(their "type" == 'archived_asset')

Fills 'self._avalon_archived_by_name' for performance
Returns:
(dictionary of lists) of assets accessible by asset name
"""
if self._avalon_archived_by_name is None:
self._avalon_archived_by_name = collections.defaultdict(list)
for ent in self.avalon_archived_ents:
Expand All @@ -474,6 +567,14 @@ def avalon_archived_by_name(self):

@property
def avalon_archived_by_id(self):
"""
Returns dictionary of archived assets from DB
(their "type" == 'archived_asset')

Fills 'self._avalon_archived_by_id' for performance
Returns:
(dictionary) of assets accessible by asset mongo _id
"""
if self._avalon_archived_by_id is None:
self._avalon_archived_by_id = {
str(ent["_id"]): ent for ent in self.avalon_archived_ents
Expand All @@ -482,6 +583,15 @@ def avalon_archived_by_id(self):

@property
def avalon_archived_by_parent_id(self):
"""
Returns dictionary of archived assets from DB per their's parent
(their "type" == 'archived_asset')

Fills 'self._avalon_archived_by_parent_id' for performance
Returns:
(dictionary of lists) of assets accessible by asset parent
mongo _id
"""
if self._avalon_archived_by_parent_id is None:
self._avalon_archived_by_parent_id = collections.defaultdict(list)
for entity in self.avalon_archived_ents:
Expand All @@ -494,6 +604,14 @@ def avalon_archived_by_parent_id(self):

@property
def subsets_by_parent_id(self):
"""
Returns dictionary of subsets from Mongo ("type": "subset")
grouped by their parent.

Fills 'self._subsets_by_parent_id' for performance
Returns:
(dictionary of lists)
"""
if self._subsets_by_parent_id is None:
self._subsets_by_parent_id = collections.defaultdict(list)
for subset in self.dbcon.find({"type": "subset"}):
Expand All @@ -515,6 +633,11 @@ def changeability_by_mongo_id(self):

@property
def all_ftrack_names(self):
"""
Returns lists of names of all entities in Ftrack
Returns:
(list)
"""
return [
ent_dict["name"] for ent_dict in self.entities_dict.values() if (
ent_dict.get("name")
Expand All @@ -534,8 +657,9 @@ def duplicity_regex_check(self):
name = entity_dict["name"]
entity_type = entity_dict["entity_type"]
# Tasks must be checked too
for task_name in entity_dict["tasks"]:
passed = task_names.get(task_name)
for task in entity_dict["tasks"].items():
task_name, task = task
passed = task_name
if passed is None:
passed = check_regex(
task_name, "task", schema_patterns=_schema_patterns
Expand Down Expand Up @@ -1014,9 +1138,13 @@ def prepare_ftrack_ent_data(self):
if not msg or not items:
continue
self.report_items["warning"][msg] = items

tasks = {}
for tt in task_types:
tasks[tt["name"]] = {
"short_name": get_task_short_name(tt["name"])
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continuation line over-indented for hanging indent

self.entities_dict[id]["final_entity"]["config"] = {
"tasks": [{"name": tt["name"]} for tt in task_types],
"tasks": tasks,
"apps": proj_apps
}
continue
Expand All @@ -1029,7 +1157,7 @@ def prepare_ftrack_ent_data(self):

data["parents"] = parents
data["hierarchy"] = hierarchy
data["tasks"] = self.entities_dict[id].pop("tasks", [])
data["tasks"] = self.entities_dict[id].pop("tasks", {})
self.entities_dict[id]["final_entity"]["data"] = data
self.entities_dict[id]["final_entity"]["type"] = "asset"

Expand Down Expand Up @@ -1904,10 +2032,10 @@ def update_entities(self):
filter = {"_id": ObjectId(mongo_id)}
change_data = from_dict_to_set(changes)
mongo_changes_bulk.append(UpdateOne(filter, change_data))

if not mongo_changes_bulk:
# TODO LOG
return
log.debug("mongo_changes_bulk:: {}".format(mongo_changes_bulk))
self.dbcon.bulk_write(mongo_changes_bulk)

def reload_parents(self, hierarchy_changing_ids):
Expand Down
12 changes: 7 additions & 5 deletions pype/plugins/global/publish/extract_hierarchy_avalon.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def import_to_avalon(self, input_data, parent=None):
data["inputs"] = entity_data.get("inputs", [])

# Tasks.
tasks = entity_data.get("tasks", [])
tasks = entity_data.get("tasks", {})
if tasks is not None or len(tasks) > 0:
data["tasks"] = tasks
parents = []
Expand Down Expand Up @@ -78,11 +78,13 @@ def import_to_avalon(self, input_data, parent=None):
if entity:
# Do not override data, only update
cur_entity_data = entity.get("data") or {}
new_tasks = data.pop("tasks", [])
new_tasks = data.pop("tasks", {})
if "tasks" in cur_entity_data and new_tasks:
for task_name in new_tasks:
if task_name not in cur_entity_data["tasks"]:
cur_entity_data["tasks"].append(task_name)
for task_name in new_tasks.keys():
if task_name \
not in cur_entity_data["tasks"].keys():
cur_entity_data["tasks"][task_name] = \
new_tasks[task_name]
cur_entity_data.update(data)
data = cur_entity_data
else:
Expand Down
2 changes: 1 addition & 1 deletion pype/plugins/maya/publish/collect_yeti_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CollectYetiCache(pyblish.api.InstancePlugin):
label = "Collect Yeti Cache"
families = ["yetiRig", "yeticache"]
hosts = ["maya"]
tasks = ["animation", "fx"]
tasks = {"animation": {"type": "Animation"}, "fx": {"type": "FX"}}

def process(self, instance):

Expand Down
7 changes: 4 additions & 3 deletions pype/plugins/nukestudio/publish/collect_shots.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ def process(self, instance):
data["name"] = data["subset"] + "_" + data["asset"]

data["label"] = (
"{} - {} - tasks:{} - assetbuilds:{}".format(
"{} - {} - tasks:{} - assetbuilds:{} - comments:{}".format(
data["asset"],
data["subset"],
data["tasks"],
[x["name"] for x in data.get("assetbuilds", [])]
data["tasks"].keys(),
[x["name"] for x in data.get("assetbuilds", [])],
len(data["comments"])
)
)

Expand Down
Loading