mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #572 from kalisp/feature/180-Store_task_types
Feature/180 store task types
This commit is contained in:
commit
9eac97efad
9 changed files with 335 additions and 23 deletions
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class ClockifySync(api.Action):
|
|||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from bson.objectid import ObjectId
|
|||
from bson.errors import InvalidId
|
||||
from pymongo import UpdateOne
|
||||
import ftrack_api
|
||||
from pype.api import config
|
||||
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
|
@ -23,9 +24,9 @@ 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
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
@ -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))
|
||||
|
|
@ -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))
|
||||
|
|
@ -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
|
||||
"""
|
||||
|
|
@ -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"
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -378,7 +422,7 @@ class SyncEntitiesFactory:
|
|||
"custom_attributes": {},
|
||||
"hier_attrs": {},
|
||||
"avalon_attrs": {},
|
||||
"tasks": []
|
||||
"tasks": {}
|
||||
})
|
||||
|
||||
for entity in all_project_entities:
|
||||
|
|
@ -389,7 +433,9 @@ class SyncEntitiesFactory:
|
|||
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"]
|
||||
|
|
@ -416,6 +462,13 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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:
|
||||
|
|
@ -425,6 +478,14 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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:
|
||||
|
|
@ -437,6 +498,13 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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:
|
||||
|
|
@ -446,6 +514,15 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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:
|
||||
|
|
@ -458,6 +535,14 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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"})
|
||||
|
|
@ -466,6 +551,14 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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:
|
||||
|
|
@ -474,6 +567,14 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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
|
||||
|
|
@ -482,6 +583,15 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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:
|
||||
|
|
@ -494,6 +604,14 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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"}):
|
||||
|
|
@ -515,6 +633,11 @@ class SyncEntitiesFactory:
|
|||
|
||||
@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")
|
||||
|
|
@ -534,8 +657,9 @@ class SyncEntitiesFactory:
|
|||
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
|
||||
|
|
@ -1014,9 +1138,13 @@ class SyncEntitiesFactory:
|
|||
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"])
|
||||
}
|
||||
self.entities_dict[id]["final_entity"]["config"] = {
|
||||
"tasks": [{"name": tt["name"]} for tt in task_types],
|
||||
"tasks": tasks,
|
||||
"apps": proj_apps
|
||||
}
|
||||
continue
|
||||
|
|
@ -1029,7 +1157,7 @@ class SyncEntitiesFactory:
|
|||
|
||||
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"
|
||||
|
||||
|
|
@ -1904,10 +2032,10 @@ class SyncEntitiesFactory:
|
|||
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):
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin):
|
|||
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 = []
|
||||
|
|
@ -99,11 +99,13 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin):
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ class CollectYetiCache(pyblish.api.InstancePlugin):
|
|||
label = "Collect Yeti Cache"
|
||||
families = ["yetiRig", "yeticache"]
|
||||
hosts = ["maya"]
|
||||
tasks = ["animation", "fx"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
|
|
|
|||
|
|
@ -40,10 +40,10 @@ class CollectShots(api.InstancePlugin):
|
|||
data["name"] = data["subset"] + "_" + data["asset"]
|
||||
|
||||
data["label"] = (
|
||||
"{} - {} - tasks: {} - assetbuilds: {} - comments: {}".format(
|
||||
"{} - {} - tasks:{} - assetbuilds:{} - comments:{}".format(
|
||||
data["asset"],
|
||||
data["subset"],
|
||||
data["tasks"],
|
||||
data["tasks"].keys(),
|
||||
[x["name"] for x in data.get("assetbuilds", [])],
|
||||
len(data.get("comments", []))
|
||||
)
|
||||
|
|
|
|||
87
schema/config-1.1.json
Normal file
87
schema/config-1.1.json
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
|
||||
"title": "pype:config-1.1",
|
||||
"description": "A project configuration.",
|
||||
|
||||
"type": "object",
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"tasks",
|
||||
"apps"
|
||||
],
|
||||
|
||||
"properties": {
|
||||
"schema": {
|
||||
"description": "Schema identifier for payload",
|
||||
"type": "string"
|
||||
},
|
||||
"template": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^.*$": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"type": "object",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"icon": {"type": "string"},
|
||||
"group": {"type": "string"},
|
||||
"label": {"type": "string"}
|
||||
},
|
||||
"required": [
|
||||
"short_name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"icon": {"type": "string"},
|
||||
"group": {"type": "string"},
|
||||
"label": {"type": "string"}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"families": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"icon": {"type": "string"},
|
||||
"label": {"type": "string"},
|
||||
"hideFilter": {"type": "boolean"}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"icon": {"type": "string"},
|
||||
"color": {"type": "string"},
|
||||
"order": {"type": ["integer", "number"]}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"copy": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
schema/inventory-1.1.json
Normal file
10
schema/inventory-1.1.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
|
||||
"title": "pype:config-1.1",
|
||||
"description": "A project configuration.",
|
||||
|
||||
"type": "object",
|
||||
|
||||
"additionalProperties": true
|
||||
}
|
||||
86
schema/project-2.1.json
Normal file
86
schema/project-2.1.json
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
|
||||
"title": "pype:project-2.1",
|
||||
"description": "A unit of data",
|
||||
|
||||
"type": "object",
|
||||
|
||||
"additionalProperties": true,
|
||||
|
||||
"required": [
|
||||
"schema",
|
||||
"type",
|
||||
"name",
|
||||
"data",
|
||||
"config"
|
||||
],
|
||||
|
||||
"properties": {
|
||||
"schema": {
|
||||
"description": "Schema identifier for payload",
|
||||
"type": "string",
|
||||
"enum": ["avalon-core:project-2.1", "pype:project-2.1"],
|
||||
"example": "avalon-core:project-2.1"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of document",
|
||||
"type": "string",
|
||||
"enum": ["project"],
|
||||
"example": "project"
|
||||
},
|
||||
"parent": {
|
||||
"description": "Unique identifier to parent document",
|
||||
"example": "592c33475f8c1b064c4d1696"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of directory",
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9_.]*$",
|
||||
"example": "hulk"
|
||||
},
|
||||
"data": {
|
||||
"description": "Document metadata",
|
||||
"type": "object",
|
||||
"example": {
|
||||
"fps": 24,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"description": "Document metadata",
|
||||
"example": {
|
||||
"schema": "pype:config-1.1",
|
||||
"apps": [
|
||||
{
|
||||
"name": "maya2016",
|
||||
"label": "Autodesk Maya 2016"
|
||||
},
|
||||
{
|
||||
"name": "nuke10",
|
||||
"label": "The Foundry Nuke 10.0"
|
||||
}
|
||||
],
|
||||
"tasks": {
|
||||
"Model": {"short_name": "mdl"},
|
||||
"Render": {"short_name": "rnd"},
|
||||
"Animate": {"short_name": "anim"},
|
||||
"Rig": {"short_name": "rig"},
|
||||
"Lookdev": {"short_name": "look"},
|
||||
"Layout": {"short_name": "lay"}
|
||||
},
|
||||
"template": {
|
||||
"work":
|
||||
"{root}/{project}/{silo}/{asset}/work/{task}/{app}",
|
||||
"publish":
|
||||
"{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}"
|
||||
}
|
||||
},
|
||||
"$ref": "config-1.1.json"
|
||||
}
|
||||
},
|
||||
|
||||
"definitions": {}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue