Merged in feature/PYPE-123_sync_and_name_change (pull request #41)

Feature/PYPE-123 sync and name change

Approved-by: Milan Kolar <milan@orbi.tools>
This commit is contained in:
Jakub Trllo 2019-01-08 15:32:12 +00:00 committed by Milan Kolar
commit 5cb9e80440
3 changed files with 199 additions and 60 deletions

View file

@ -9,10 +9,15 @@ from avalon.vendor import toml
from bson.objectid import ObjectId
from pype.ftrack import ftrack_utils
class ExpectedError(Exception):
def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)
class Sync_to_Avalon(BaseEvent):
def launch(self, session, entities, event):
self.ca_mongoid = 'avalon_mongo_id'
# If mongo_id textfield has changed: RETURN!
# - infinite loop
@ -21,7 +26,7 @@ class Sync_to_Avalon(BaseEvent):
if self.ca_mongoid in ent['keys']:
return
self.proj = None
self.errors = []
# get project
for entity in entities:
try:
@ -32,10 +37,12 @@ class Sync_to_Avalon(BaseEvent):
break
# check if project is set to auto-sync
if (self.proj is None or
if (
self.proj is None or
'avalon_auto_sync' not in self.proj['custom_attributes'] or
self.proj['custom_attributes']['avalon_auto_sync'] is False):
return
self.proj['custom_attributes']['avalon_auto_sync'] is False
):
return
# check if project have Custom Attribute 'avalon_mongo_id'
if self.ca_mongoid not in self.proj['custom_attributes']:
@ -51,13 +58,18 @@ class Sync_to_Avalon(BaseEvent):
# get avalon project if possible
io.install()
try:
self.avalon_project = io.find_one({"_id": ObjectId(self.projectId)})
self.avalon_project = io.find_one({
"_id": ObjectId(self.projectId)
})
except:
self.avalon_project = None
importEntities = []
if self.avalon_project is None:
self.avalon_project = io.find_one({"type": "project", "name": self.proj["full_name"]})
self.avalon_project = io.find_one({
"type": "project",
"name": self.proj["full_name"]
})
if self.avalon_project is None:
importEntities.append(self.proj)
else:
@ -69,9 +81,10 @@ class Sync_to_Avalon(BaseEvent):
if entity.entity_type.lower() in ['task']:
entity = entity['parent']
try:
mongo_id = entity['custom_attributes'][self.ca_mongoid]
except:
if (
'custom_attributes' not in entity or
self.ca_mongoid not in entity['custom_attributes']
):
message = "Custom attribute '{}' for '{}' is not created or don't have set permissions for API".format(self.ca_mongoid, entity.entity_type)
self.log.warning(message)
self.show_message(event, message, False)
@ -88,25 +101,39 @@ class Sync_to_Avalon(BaseEvent):
io.install()
try:
for entity in importEntities:
self.importToAvalon(session, entity)
self.importToAvalon(session, event, entity)
session.commit()
except ValueError as ve:
message = str(ve)
self.show_message(event, message, False)
self.log.warning(message)
except ExpectedError as ee:
items = []
for error in self.errors:
info = {
'label': 'Error',
'type': 'textarea',
'name': 'error',
'value': error
}
items.append(info)
self.log.warning(error)
self.show_interface(event, items)
except Exception as e:
message = str(e)
ftrack_message = "SyncToAvalon event ended with unexpected error please check log file for more information."
self.show_message(event, ftrack_message, False)
items = [{
'label': 'Error',
'type': 'textarea',
'name': 'error',
'value': ftrack_message
}]
self.show_interface(event, items)
self.log.error(message)
io.uninstall()
return
def importToAvalon(self, session, entity):
def importToAvalon(self, session, event, entity):
if self.ca_mongoid not in entity['custom_attributes']:
raise ValueError("Custom attribute '{}' for '{}' is not created or don't have set permissions for API".format(self.ca_mongoid, entity['name']))
@ -122,21 +149,26 @@ class Sync_to_Avalon(BaseEvent):
if self.avalon_project is None:
inventory.save(name, config, template)
self.avalon_project = io.find_one({'type': 'project', 'name': name})
self.avalon_project = io.find_one({'type': type, 'name': name})
elif self.avalon_project['name'] != name:
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly!'.format(self.avalon_project['name'], name))
entity['name'] = self.avalon_project['name']
session.commit()
msg = 'You can\'t change name {} to {}, avalon wouldn\'t work properly!\nName was changed back!'.format(self.avalon_project['name'], name)
self.errors.append(msg)
return
self.projectId = self.avalon_project['_id']
data = ftrack_utils.get_data(self, entity, session,self.custom_attributes)
data = ftrack_utils.get_data(self, entity, session, self.custom_attributes)
io.update_many(
{"_id": ObjectId(self.projectId)},
{'$set':{
'name':name,
'config':config,
'data':data,
{'$set': {
'name': name,
'config': config,
'data': data,
}})
entity['custom_attributes'][self.ca_mongoid] = str(self.projectId)
@ -144,12 +176,14 @@ class Sync_to_Avalon(BaseEvent):
return
if self.avalon_project is None:
self.importToAvalon(session, self.proj)
self.importToAvalon(session, event, self.proj)
data = ftrack_utils.get_data(self, entity, session,self.custom_attributes)
data = ftrack_utils.get_data(self, entity, session, self.custom_attributes)
# return if entity is silo
# only check name if entity is silo
if len(data['parents']) == 0:
if self.checkSilo(entity, event, session) is False:
raise ExpectedError
return
else:
silo = data['parents'][0]
@ -171,30 +205,104 @@ class Sync_to_Avalon(BaseEvent):
if avalon_asset is None:
mongo_id = inventory.create_asset(name, silo, data, ObjectId(self.projectId))
# Raise error if it seems to be different ent. with same name
elif (avalon_asset['data']['parents'] != data['parents'] or
avalon_asset['silo'] != silo):
raise ValueError('In Avalon DB already exists entity with name "{0}"'.format(name))
elif avalon_asset['name'] != entity['name']:
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please set name back'.format(avalon_asset['name'], name))
elif avalon_asset['silo'] != silo or avalon_asset['data']['parents'] != data['parents']:
old_path = "/".join(avalon_asset['data']['parents'])
new_path = "/".join(data['parents'])
raise ValueError('You can\'t move with entities. Entity "{}" was moved from "{}" to "{}" , avalon DB won\'t work properly'.format(avalon_asset['name'], old_path, new_path))
elif (
avalon_asset['data']['parents'] != data['parents'] or
avalon_asset['silo'] != silo
):
msg = 'In Avalon DB already exists entity with name "{0}"'.format(name)
self.errors.append(msg)
return
else:
if avalon_asset['name'] != entity['name']:
if self.checkChilds(entity) is False:
msg = 'You can\'t change name {} to {}, avalon wouldn\'t work properly!\n\nName was changed back!\n\nCreate new entity if you want to change name.'.format(avalon_asset['name'], entity['name'])
entity['name'] = avalon_asset['name']
session.commit()
self.errors.append(msg)
if avalon_asset['silo'] != silo or avalon_asset['data']['parents'] != data['parents']:
old_path = "/".join(avalon_asset['data']['parents'])
new_path = "/".join(data['parents'])
msg = 'You can\'t move with entities.\nEntity "{}" was moved from "{}" to "{}"\n\nAvalon won\'t work properly, please move them back!'.format(avalon_asset['name'], old_path, new_path)
self.errors.append(msg)
if len(self.errors) > 0:
raise ExpectedError
io.update_many(
{"_id": ObjectId(mongo_id)},
{'$set':{
'name':name,
'silo':silo,
'data':data,
{'$set': {
'name': name,
'silo': silo,
'data': data,
'parent': ObjectId(self.projectId)}})
entity['custom_attributes'][self.ca_mongoid] = str(mongo_id)
def checkChilds(self, entity):
if (entity.entity_type.lower() != 'task' and 'children' not in entity):
return True
childs = entity['children']
for child in childs:
if child.entity_type.lower() == 'task':
config = ftrack_utils.get_config_data()
if 'sync_to_avalon' in config:
config = config['sync_to_avalon']
if 'statuses_name_change' in config:
available_statuses = config['statuses_name_change']
else:
available_statuses = []
ent_status = child['status']['name'].lower()
if ent_status not in available_statuses:
return False
# If not task go deeper
elif self.checkChilds(child) is False:
return False
# If everything is allright
return True
def checkSilo(self, entity, event, session):
changes = event['data']['entities'][0]['changes']
if 'name' not in changes:
return True
new_name = changes['name']['new']
old_name = changes['name']['old']
if 'children' not in entity or len(entity['children']) < 1:
return True
if self.checkChilds(entity) is True:
self.updateSilo(old_name, new_name)
return True
new_found = 0
old_found = 0
for asset in io.find({'silo': new_name}):
new_found += 1
for asset in io.find({'silo': old_name}):
old_found += 1
if new_found > 0 or old_found == 0:
return True
# If any condition is possible, show error to user and change name back
msg = 'You can\'t change name {} to {}, avalon wouldn\'t work properly!\n\nName was changed back!\n\nCreate new entity if you want to change name.'.format(old_name, new_name)
self.errors.append(msg)
entity['name'] = old_name
session.commit()
return False
def updateSilo(self, old, new):
io.update_many(
{'silo': old},
{'$set': {'silo': new}}
)
def setAvalonAttributes(self):
self.custom_attributes = []
all_avalon_attr = self.session.query('CustomAttributeGroup where name is "avalon"').one()
query = 'CustomAttributeGroup where name is "avalon"'
all_avalon_attr = self.session.query(query).one()
for cust_attr in all_avalon_attr['custom_attribute_configurations']:
if 'avalon_' not in cust_attr['key']:
self.custom_attributes.append(cust_attr)
@ -210,10 +318,13 @@ class Sync_to_Avalon(BaseEvent):
self.session, *args
)
return
def _translate_event(self, session, event):
exceptions = ['assetversion', 'job', 'user', 'reviewsessionobject', 'timer', 'socialfeed', 'timelog']
_selection = event['data'].get('entities',[])
exceptions = [
'assetversion', 'job', 'user', 'reviewsessionobject', 'timer',
'socialfeed', 'timelog'
]
_selection = event['data'].get('entities', [])
_entities = list()
for entity in _selection:
@ -227,6 +338,7 @@ class Sync_to_Avalon(BaseEvent):
return [_entities, event]
def register(session, **kw):
'''Register plugin. Called when used as an plugin.'''

View file

@ -1,19 +1,7 @@
# :coding: utf-8
# :copyright: Copyright (c) 2017 ftrack
import os
import logging
import getpass
# import platform
import ftrack_api
import toml
from avalon import io, lib, pipeline
from avalon import session as sess
import acre
from app.api import (
Templates,
Logger
)
from app.api import Logger
class BaseEvent(object):
@ -47,7 +35,7 @@ class BaseEvent(object):
def _translate_event(self, session, event):
'''Return *event* translated structure to be used with the API.'''
_selection = event['data'].get('entities',[])
_selection = event['data'].get('entities', [])
_entities = list()
for entity in _selection:
@ -119,7 +107,7 @@ class BaseEvent(object):
'''
raise NotImplementedError()
def show_message(self, event, input_message, result = False):
def show_message(self, event, input_message, result=False):
"""
Shows message to user who triggered event
- event - just source of user id
@ -137,6 +125,8 @@ class BaseEvent(object):
return
user_id = event['source']['user']['id']
target = 'applicationId=ftrack.client.web and user.id="{0}"'.format(user_id)
self.session.event_hub.publish(
ftrack_api.event.base.Event(
topic='ftrack.action.trigger-user-interface',
@ -145,7 +135,27 @@ class BaseEvent(object):
success=result,
message=message
),
target='applicationId=ftrack.client.web and user.id="{0}"'.format(user_id)
target=target
),
on_error='ignore'
)
def show_interface(self, event, items):
"""
Shows interface to user who triggered event
- 'items' must be list containing Ftrack interface items
"""
user_id = event['source']['user']['id']
target = 'applicationId=ftrack.client.web and user.id="{0}"'.format(user_id)
self.session.event_hub.publish(
ftrack_api.event.base.Event(
topic='ftrack.action.trigger-user-interface',
data=dict(
type='widget',
items=items
),
target=target
),
on_error='ignore'
)

View file

@ -1,6 +1,7 @@
import os
import sys
import re
import json
from pprint import *
import ftrack_api
@ -13,6 +14,22 @@ from app.api import Logger
log = Logger.getLogger(__name__)
def get_config_data():
templates = os.environ['PYPE_STUDIO_TEMPLATES']
path_items = [templates, 'presets', 'ftrack', 'ftrack_config.json']
filepath = os.path.sep.join(path_items)
data = dict()
try:
with open(filepath) as data_file:
data = json.load(data_file)
except Exception as e:
msg = 'Loading "Ftrack Config file" Failed. Please check log for more information. Times are set to default.'
log.warning("{} - {}".format(msg, str(e)))
return data
def get_data(parent, entity, session, custom_attributes):
entity_type = entity.entity_type