diff --git a/pype/ftrack/actions/action_prepare_project.py b/pype/ftrack/actions/action_prepare_project.py
index 4cc6cfd8df..9b21febb81 100644
--- a/pype/ftrack/actions/action_prepare_project.py
+++ b/pype/ftrack/actions/action_prepare_project.py
@@ -1,9 +1,8 @@
import os
import json
-from ruamel import yaml
from pype.ftrack import BaseAction
-from pypeapp import config
+from pypeapp import config, Anatomy, project_overrides_dir_path
from pype.ftrack.lib.avalon_sync import get_avalon_attr
@@ -24,6 +23,7 @@ class PrepareProject(BaseAction):
# Key to store info about trigerring create folder structure
create_project_structure_key = "create_folder_structure"
+ item_splitter = {'type': 'label', 'value': '---'}
def discover(self, session, entities, event):
''' Validation '''
@@ -41,15 +41,190 @@ class PrepareProject(BaseAction):
# Inform user that this may take a while
self.show_message(event, "Preparing data... Please wait", True)
+ self.log.debug("Preparing data which will be shown")
self.log.debug("Loading custom attributes")
- cust_attrs, hier_cust_attrs = get_avalon_attr(session, True)
- project_defaults = config.get_presets(
- entities[0]["full_name"]
- ).get("ftrack", {}).get("project_defaults", {})
- self.log.debug("Preparing data which will be shown")
+ project_name = entities[0]["full_name"]
+
+ project_defaults = (
+ config.get_presets(project_name)
+ .get("ftrack", {})
+ .get("project_defaults", {})
+ )
+
+ anatomy = Anatomy(project_name)
+ if not anatomy.roots:
+ return {
+ "success": False,
+ "message": (
+ "Have issues with loading Roots for project \"{}\"."
+ ).format(anatomy.project_name)
+ }
+
+ root_items = self.prepare_root_items(anatomy)
+
+ ca_items, multiselect_enumerators = (
+ self.prepare_custom_attribute_items(project_defaults)
+ )
+
+ self.log.debug("Heavy items are ready. Preparing last items group.")
+
+ title = "Prepare Project"
+ items = []
+
+ # Add root items
+ items.extend(root_items)
+ items.append(self.item_splitter)
+
+ # Ask if want to trigger Action Create Folder Structure
+ items.append({
+ "type": "label",
+ "value": "
Want to create basic Folder Structure?
"
+ })
+ items.append({
+ "name": self.create_project_structure_key,
+ "type": "boolean",
+ "value": False,
+ "label": "Check if Yes"
+ })
+
+ items.append(self.item_splitter)
+ items.append({
+ "type": "label",
+ "value": "Set basic Attributes:
"
+ })
+
+ items.extend(ca_items)
+
+ # This item will be last (before enumerators)
+ # - sets value of auto synchronization
+ auto_sync_name = "avalon_auto_sync"
+ auto_sync_item = {
+ "name": auto_sync_name,
+ "type": "boolean",
+ "value": project_defaults.get(auto_sync_name, False),
+ "label": "AutoSync to Avalon"
+ }
+ # Add autosync attribute
+ items.append(auto_sync_item)
+
+ # Add enumerator items at the end
+ for item in multiselect_enumerators:
+ items.append(item)
+
+ return {
+ "items": items,
+ "title": title
+ }
+
+ def prepare_root_items(self, anatomy):
+ root_items = []
+ self.log.debug("Root items preparation begins.")
+
+ root_names = anatomy.root_names()
+ roots = anatomy.roots
+
+ root_items.append({
+ "type": "label",
+ "value": "Check your Project root settings
"
+ })
+ root_items.append({
+ "type": "label",
+ "value": (
+ "NOTE: Roots are crutial for path filling"
+ " (and creating folder structure).
"
+ )
+ })
+ root_items.append({
+ "type": "label",
+ "value": (
+ "WARNING: Do not change roots on running project,"
+ " that will cause workflow issues.
"
+ )
+ })
+
+ default_roots = anatomy.roots
+ while isinstance(default_roots, dict):
+ key = tuple(default_roots.keys())[0]
+ default_roots = default_roots[key]
+
+ empty_text = "Enter root path here..."
+
+ # Root names is None when anatomy templates contain "{root}"
+ all_platforms = ["windows", "linux", "darwin"]
+ if root_names is None:
+ root_items.append(self.item_splitter)
+ # find first possible key
+ for platform in all_platforms:
+ value = default_roots.raw_data.get(platform) or ""
+ root_items.append({
+ "label": platform,
+ "name": "__root__{}".format(platform),
+ "type": "text",
+ "value": value,
+ "empty_text": empty_text
+ })
+ return root_items
+
+ root_name_data = {}
+ missing_roots = []
+ for root_name in root_names:
+ root_name_data[root_name] = {}
+ if not isinstance(roots, dict):
+ missing_roots.append(root_name)
+ continue
+
+ root_item = roots.get(root_name)
+ if not root_item:
+ missing_roots.append(root_name)
+ continue
+
+ for platform in all_platforms:
+ root_name_data[root_name][platform] = (
+ root_item.raw_data.get(platform) or ""
+ )
+
+ if missing_roots:
+ default_values = {}
+ for platform in all_platforms:
+ default_values[platform] = (
+ default_roots.raw_data.get(platform) or ""
+ )
+
+ for root_name in missing_roots:
+ root_name_data[root_name] = default_values
+
+ root_names = list(root_name_data.keys())
+ root_items.append({
+ "type": "hidden",
+ "name": "__rootnames__",
+ "value": json.dumps(root_names)
+ })
+
+ for root_name, values in root_name_data.items():
+ root_items.append(self.item_splitter)
+ root_items.append({
+ "type": "label",
+ "value": "Root: \"{}\"".format(root_name)
+ })
+ for platform, value in values.items():
+ root_items.append({
+ "label": platform,
+ "name": "__root__{}{}".format(root_name, platform),
+ "type": "text",
+ "value": value,
+ "empty_text": empty_text
+ })
+
+ self.log.debug("Root items preparation ended.")
+ return root_items
+
+ def _attributes_to_set(self, project_defaults):
attributes_to_set = {}
+
+ cust_attrs, hier_cust_attrs = get_avalon_attr(self.session, True)
+
for attr in hier_cust_attrs:
key = attr["key"]
if key.startswith("avalon_"):
@@ -77,45 +252,17 @@ class PrepareProject(BaseAction):
attributes_to_set.items(),
key=lambda x: x[1]["label"]
))
+ return attributes_to_set
+
+ def prepare_custom_attribute_items(self, project_defaults):
+ items = []
+ multiselect_enumerators = []
+ attributes_to_set = self._attributes_to_set(project_defaults)
+
self.log.debug("Preparing interface for keys: \"{}\"".format(
str([key for key in attributes_to_set])
))
- item_splitter = {'type': 'label', 'value': '---'}
- title = "Prepare Project"
- items = []
-
- # Ask if want to trigger Action Create Folder Structure
- items.append({
- "type": "label",
- "value": "Want to create basic Folder Structure?
"
- })
-
- items.append({
- "name": self.create_project_structure_key,
- "type": "boolean",
- "value": False,
- "label": "Check if Yes"
- })
-
- items.append(item_splitter)
- items.append({
- "type": "label",
- "value": "Set basic Attributes:
"
- })
-
- multiselect_enumerators = []
-
- # This item will be last (before enumerators)
- # - sets value of auto synchronization
- auto_sync_name = "avalon_auto_sync"
- auto_sync_item = {
- "name": auto_sync_name,
- "type": "boolean",
- "value": project_defaults.get(auto_sync_name, False),
- "label": "AutoSync to Avalon"
- }
-
for key, in_data in attributes_to_set.items():
attr = in_data["object"]
@@ -139,8 +286,7 @@ class PrepareProject(BaseAction):
attr_config_data = json.loads(attr_config["data"])
if attr_config["multiSelect"] is True:
- multiselect_enumerators.append(item_splitter)
-
+ multiselect_enumerators.append(self.item_splitter)
multiselect_enumerators.append({
"type": "label",
"value": in_data["label"]
@@ -160,10 +306,7 @@ class PrepareProject(BaseAction):
"label": "- {}".format(option["menu"])
}
if default:
- if (
- isinstance(default, list) or
- isinstance(default, tuple)
- ):
+ if isinstance(default, (list, tuple)):
if name in default:
item["value"] = True
else:
@@ -204,17 +347,7 @@ class PrepareProject(BaseAction):
items.append(item)
- # Add autosync attribute
- items.append(auto_sync_item)
-
- # Add enumerator items at the end
- for item in multiselect_enumerators:
- items.append(item)
-
- return {
- 'items': items,
- 'title': title
- }
+ return items, multiselect_enumerators
def launch(self, session, entities, event):
if not event['data'].get('values', {}):
@@ -222,6 +355,35 @@ class PrepareProject(BaseAction):
in_data = event['data']['values']
+ root_values = {}
+ root_key = "__root__"
+ for key, value in tuple(in_data.items()):
+ if key.startswith(root_key):
+ _key = key[len(root_key):]
+ root_values[_key] = in_data.pop(key)
+
+ root_names = in_data.pop("__rootnames__", None)
+ root_data = {}
+ if root_names:
+ for root_name in json.loads(root_names):
+ root_data[root_name] = {}
+ for key, value in tuple(root_values.items()):
+ if key.startswith(root_name):
+ _key = key[len(root_name):]
+ root_data[root_name][_key] = value
+
+ else:
+ for key, value in root_values.items():
+ root_data[key] = value
+
+ project_name = entities[0]["full_name"]
+ anatomy = Anatomy(project_name)
+ anatomy.templates_obj.save_project_overrides(project_name)
+ anatomy.roots_obj.save_project_overrides(
+ project_name, root_data, override=True
+ )
+ anatomy.reset()
+
# pop out info about creating project structure
create_proj_struct = in_data.pop(self.create_project_structure_key)
@@ -269,94 +431,22 @@ class PrepareProject(BaseAction):
def create_project_specific_config(self, project_name, json_data):
self.log.debug("*** Creating project specifig configs ***")
-
- path_proj_configs = os.environ.get('PYPE_PROJECT_CONFIGS', "")
-
- # Skip if PYPE_PROJECT_CONFIGS is not set
- # TODO show user OS message
- if not path_proj_configs:
- self.log.warning((
- "Environment variable \"PYPE_PROJECT_CONFIGS\" is not set."
- " Project specific config can't be set."
- ))
- return
-
- path_proj_configs = os.path.normpath(path_proj_configs)
- # Skip if path does not exist
- # TODO create if not exist?!!!
- if not os.path.exists(path_proj_configs):
- self.log.warning((
- "Path set in Environment variable \"PYPE_PROJECT_CONFIGS\""
- " Does not exist."
- ))
- return
-
- project_specific_path = os.path.normpath(
- os.path.join(path_proj_configs, project_name)
- )
+ project_specific_path = project_overrides_dir_path(project_name)
if not os.path.exists(project_specific_path):
os.makedirs(project_specific_path)
self.log.debug((
"Project specific config folder for project \"{}\" created."
).format(project_name))
- # Anatomy ####################################
- self.log.debug("--- Processing Anatomy Begins: ---")
-
- anatomy_dir = os.path.normpath(os.path.join(
- project_specific_path, "anatomy"
- ))
- anatomy_path = os.path.normpath(os.path.join(
- anatomy_dir, "default.yaml"
- ))
-
- anatomy = None
- if os.path.exists(anatomy_path):
- self.log.debug(
- "Anatomy file already exist. Trying to read: \"{}\"".format(
- anatomy_path
- )
- )
- # Try to load data
- with open(anatomy_path, 'r') as file_stream:
- try:
- anatomy = yaml.load(file_stream, Loader=yaml.loader.Loader)
- self.log.debug("Reading Anatomy file was successful")
- except yaml.YAMLError as exc:
- self.log.warning(
- "Reading Yaml file failed: \"{}\"".format(anatomy_path),
- exc_info=True
- )
-
- if not anatomy:
- self.log.debug("Anatomy is not set. Duplicating default.")
- # Create Anatomy folder
- if not os.path.exists(anatomy_dir):
- self.log.debug(
- "Creating Anatomy folder: \"{}\"".format(anatomy_dir)
- )
- os.makedirs(anatomy_dir)
-
- source_items = [
- os.environ["PYPE_CONFIG"], "anatomy", "default.yaml"
- ]
-
- source_path = os.path.normpath(os.path.join(*source_items))
- with open(source_path, 'r') as file_stream:
- source_data = file_stream.read()
-
- with open(anatomy_path, 'w') as file_stream:
- file_stream.write(source_data)
-
# Presets ####################################
self.log.debug("--- Processing Presets Begins: ---")
- project_defaults_dir = os.path.normpath(os.path.join(*[
+ project_defaults_dir = os.path.normpath(os.path.join(
project_specific_path, "presets", "ftrack"
- ]))
- project_defaults_path = os.path.normpath(os.path.join(*[
+ ))
+ project_defaults_path = os.path.normpath(os.path.join(
project_defaults_dir, "project_defaults.json"
- ]))
+ ))
# Create folder if not exist
if not os.path.exists(project_defaults_dir):
self.log.debug("Creating Ftrack Presets folder: \"{}\"".format(
@@ -372,5 +462,4 @@ class PrepareProject(BaseAction):
def register(session, plugins_presets={}):
'''Register plugin. Called when used as an plugin.'''
-
PrepareProject(session, plugins_presets).register()