more io usage replaced, added ignore_me variable to actions/events for ftrack server, added 'get avalon projects' function, fixed few bugs, added titles to show_interface in action handlers

This commit is contained in:
Jakub Trllo 2019-01-17 16:06:34 +01:00
parent ae68a41400
commit 9d50a5b8b7
10 changed files with 153 additions and 93 deletions

View file

@ -1,8 +1,9 @@
import os
import toml
import time
from ftrack_action_handler import AppAction
from avalon import io, lib
from avalon import lib
from app.api import Logger
from pype import lib as pypelib
log = Logger.getLogger(__name__)
@ -28,8 +29,11 @@ def registerApp(app, session):
apptoml = toml.load(abspath)
''' REQUIRED '''
executable = apptoml['executable']
label = apptoml.get('ftrack_label', app['label'])
''' OPTIONAL '''
label = apptoml.get('ftrack_label', app.get('label', name))
icon = apptoml.get('ftrack_icon', None)
description = apptoml.get('description', None)
@ -40,17 +44,7 @@ def registerApp(app, session):
def register(session):
# set avalon environ - they just must exist
os.environ['AVALON_PROJECT'] = ''
os.environ['AVALON_ASSET'] = ''
os.environ['AVALON_SILO'] = ''
# Get all projects from Avalon DB
try:
io.install()
projects = sorted(io.projects(), key=lambda x: x['name'])
io.uninstall()
except Exception as e:
log.error(e)
projects = pypelib.get_all_avalon_projects()
apps = []
appNames = []
@ -65,5 +59,6 @@ def register(session):
for app in apps:
try:
registerApp(app, session)
time.sleep(0.05)
except Exception as e:
log.warning("'{0}' - not proper App ({1})".format(app['name'], e))

View file

@ -147,7 +147,6 @@ class SyncToAvalon(BaseAction):
)
if 'errors' in result and len(result['errors']) > 0:
print('error')
items = []
for error in result['errors']:
for key, message in error.items():
@ -162,10 +161,12 @@ class SyncToAvalon(BaseAction):
self.log.error(
'{}: {}'.format(key, message)
)
title = 'Hey You! Few Errors were raised! (*look below*)'
job['status'] = 'failed'
session.commit()
self.show_interface(event, items)
self.show_interface(event, items, title)
return {
'success': False,
'message': "Sync to avalon FAILED"

View file

@ -6,6 +6,8 @@ import sys
import json
import base64
ignore_me = True
# sys.path.append(os.path.dirname(os.path.dirname(__file__)))
# from ftrack_kredenc.lucidity.vendor import yaml
# from ftrack_kredenc import lucidity

View file

@ -4,12 +4,16 @@ import os
import sys
import platform
import ftrack_api
from avalon import io, lib
from avalon import lib
import acre
from pype.ftrack import ftrack_utils
from pype import api as pype
ignore_me = True
class AppAction(object):
'''Custom Action base class
@ -122,33 +126,20 @@ class AppAction(object):
entity = session.get(entity_type, entity_id)
# TODO Should return False if not TASK ?!!!
if entity.entity_type != 'Task':
return False
# TODO Should return False if more than one entity is selected ?!!!
if len(entities) > 1:
if (
len(entities) > 1 or
entity.entity_type.lower() != 'task'
):
return False
ft_project = entity
if (entity.entity_type != 'Project'):
ft_project = entity['project']
ft_project = entity['project']
silo = ""
if 'ancestors' in entity:
for ancestor in entity['ancestors']:
silo = ancestor['name']
break
os.environ['AVALON_PROJECT'] = ft_project['full_name']
os.environ['AVALON_ASSET'] = entity['name']
os.environ['AVALON_SILO'] = silo
io.install()
avalon_project = io.find_one({
"type": "project",
"name": ft_project['full_name']
database = ftrack_utils.get_avalon_database()
project_name = ft_project['full_name']
avalon_project = database[project_name].find_one({
"type": "project"
})
io.uninstall()
if avalon_project is None:
return False
@ -249,24 +240,37 @@ class AppAction(object):
entity, id = entities[0]
entity = session.get(entity, id)
project_name = entity['project']['full_name']
database = ftrack_utils.get_avalon_database()
# Get current environments
env_list = [
'AVALON_PROJECT',
'AVALON_SILO',
'AVALON_ASSET',
'AVALON_TASK',
'AVALON_APP',
'AVALON_APP_NAME'
]
env_origin = {}
for env in env_list:
env_origin[env] = os.environ.get(env, None)
# set environments for Avalon
os.environ["AVALON_PROJECT"] = entity['project']['full_name']
os.environ["AVALON_PROJECT"] = project_name
os.environ["AVALON_SILO"] = entity['ancestors'][0]['name']
os.environ["AVALON_ASSET"] = entity['parent']['name']
os.environ["AVALON_TASK"] = entity['name']
os.environ["AVALON_APP"] = self.identifier.split("_")[0]
os.environ["AVALON_APP_NAME"] = self.identifier
os.environ["FTRACK_TASKID"] = id
anatomy = pype.Anatomy
io.install()
hierarchy = io.find_one({
hierarchy = database[project_name].find_one({
"type": 'asset',
"name": entity['parent']['name']
})['data']['parents']
io.uninstall()
if hierarchy:
hierarchy = os.path.join(*hierarchy)
@ -384,6 +388,10 @@ class AppAction(object):
self.log.info('Starting timer for task: ' + task['name'])
user.start_timer(task, force=True)
# Set origin avalon environments
for key, value in env_origin.items():
os.environ[key] = value
return {
'success': True,
'message': "Launching {0}".format(self.label)
@ -729,7 +737,7 @@ class BaseAction(object):
return result
def show_interface(self, event, items):
def show_interface(self, event, items, title=''):
"""
Shows interface to user who triggered event
- 'items' must be list containing Ftrack interface items
@ -744,7 +752,8 @@ class BaseAction(object):
topic='ftrack.action.trigger-user-interface',
data=dict(
type='widget',
items=items
items=items,
title=title
),
target=target
),

View file

@ -158,7 +158,6 @@ class Sync_To_Avalon(BaseAction):
)
if 'errors' in result and len(result['errors']) > 0:
print('error')
items = []
for error in result['errors']:
for key, message in error.items():
@ -173,10 +172,12 @@ class Sync_To_Avalon(BaseAction):
self.log.error(
'{}: {}'.format(key, message)
)
title = 'Hey You! Few Errors were raised! (*look below*)'
job['status'] = 'failed'
session.commit()
self.show_interface(event, items)
self.show_interface(event, items, title)
return {
'success': False,
'message': "Sync to avalon FAILED"

View file

@ -86,7 +86,6 @@ class Sync_to_Avalon(BaseEvent):
custom_attributes=custom_attributes
)
if 'errors' in result and len(result['errors']) > 0:
print('error')
items = []
for error in result['errors']:
for key, message in error.items():
@ -101,9 +100,9 @@ class Sync_to_Avalon(BaseEvent):
self.log.error(
'{}: {}'.format(key, message)
)
session.commit()
self.show_interface(event, items)
title = 'Hey You! You raised few Errors! (*look below*)'
self.show_interface(event, items, title)
return
if avalon_project is None:
@ -122,7 +121,8 @@ class Sync_to_Avalon(BaseEvent):
'name': 'error',
'value': ftrack_message
}]
self.show_interface(event, items)
title = 'Hey You! Unknown Error has been raised! (*look below*)'
self.show_interface(event, items, title)
self.log.error(message)
return

View file

@ -140,7 +140,7 @@ class BaseEvent(object):
on_error='ignore'
)
def show_interface(self, event, items):
def show_interface(self, event, items, title=''):
"""
Shows interface to user who triggered event
- 'items' must be list containing Ftrack interface items
@ -153,7 +153,8 @@ class BaseEvent(object):
topic='ftrack.action.trigger-user-interface',
data=dict(
type='widget',
items=items
items=items,
title=title
),
target=target
),

View file

@ -1,4 +1,3 @@
import sys
import os
import json
import threading
@ -6,6 +5,7 @@ import time
import ftrack_api
from app import style
from app.vendor.Qt import QtCore, QtGui, QtWidgets
from pype.ftrack import credentials, login_dialog as login_dialog
from pype.vendor.pynput import mouse, keyboard
@ -21,7 +21,6 @@ log = pype.Logger.getLogger(__name__, "ftrack")
class FtrackRunner:
def __init__(self, main_parent=None, parent=None):
self.parent = parent
@ -86,7 +85,9 @@ class FtrackRunner:
# Actions part
def start_action_server(self):
if self.thread_action_server is None:
self.thread_action_server = threading.Thread(target=self.set_action_server)
self.thread_action_server = threading.Thread(
target=self.set_action_server
)
self.thread_action_server.daemon = True
self.thread_action_server.start()
@ -95,7 +96,14 @@ class FtrackRunner:
self.set_menu_visibility()
def set_action_server(self):
self.action_server.run_server()
try:
self.action_server.run_server()
except Exception:
msg = 'Ftrack Action server crashed! Please try to start again.'
log.error(msg)
# TODO show message to user
self.bool_action_server = False
self.set_menu_visibility()
def reset_action_server(self):
self.stop_action_server()
@ -123,11 +131,19 @@ class FtrackRunner:
# Actions - server
self.smActionS = self.menu.addMenu("Action server")
self.aRunActionS = QtWidgets.QAction("Run action server", self.smActionS)
self.aRunActionS = QtWidgets.QAction(
"Run action server", self.smActionS
)
self.aResetActionS = QtWidgets.QAction(
"Reset action server", self.smActionS
)
self.aStopActionS = QtWidgets.QAction(
"Stop action server", self.smActionS
)
self.aRunActionS.triggered.connect(self.start_action_server)
self.aResetActionS = QtWidgets.QAction("Reset action server", self.smActionS)
self.aResetActionS.triggered.connect(self.reset_action_server)
self.aStopActionS = QtWidgets.QAction("Stop action server", self.smActionS)
self.aStopActionS.triggered.connect(self.stop_action_server)
self.smActionS.addAction(self.aRunActionS)
@ -168,12 +184,19 @@ class FtrackRunner:
self.start_timer_thread()
def start_timer_thread(self):
if self.thread_timer is None:
self.thread_timer = FtrackEventsThread(self)
self.bool_timer_event = True
self.thread_timer.signal_timer_started.connect(self.timer_started)
self.thread_timer.signal_timer_stopped.connect(self.timer_stopped)
self.thread_timer.start()
try:
if self.thread_timer is None:
self.thread_timer = FtrackEventsThread(self)
self.bool_timer_event = True
self.thread_timer.signal_timer_started.connect(
self.timer_started
)
self.thread_timer.signal_timer_stopped.connect(
self.timer_stopped
)
self.thread_timer.start()
except Exception:
pass
def stop_timer_thread(self):
try:
@ -188,9 +211,15 @@ class FtrackRunner:
def start_countdown_thread(self):
if self.thread_timer_coundown is None:
self.thread_timer_coundown = CountdownThread(self)
self.thread_timer_coundown.signal_show_question.connect(self.show_widget_timer)
self.thread_timer_coundown.signal_send_time.connect(self.change_count_widget)
self.thread_timer_coundown.signal_stop_timer.connect(self.timer_stop)
self.thread_timer_coundown.signal_show_question.connect(
self.show_widget_timer
)
self.thread_timer_coundown.signal_send_time.connect(
self.change_count_widget
)
self.thread_timer_coundown.signal_stop_timer.connect(
self.timer_stop
)
self.thread_timer_coundown.start()
def stop_countdown_thread(self):
@ -255,7 +284,9 @@ class FtrackEventsThread(QtCore.QThread):
def run(self):
self.timer_session = ftrack_api.Session(auto_connect_event_hub=True)
self.timer_session.event_hub.subscribe(
'topic=ftrack.update and source.user.username={}'.format(self.username),
'topic=ftrack.update and source.user.username={}'.format(
self.username
),
self.event_handler)
user_query = 'User where username is "{}"'.format(self.username)
@ -273,7 +304,7 @@ class FtrackEventsThread(QtCore.QThread):
try:
if event['data']['entities'][0]['objectTypeId'] != 'timer':
return
except:
except Exception:
return
new = event['data']['entities'][0]['changes']['start']['new']
@ -301,12 +332,6 @@ class FtrackEventsThread(QtCore.QThread):
def ftrack_restart_timer(self):
try:
last_task = None
if "FTRACK_LAST_TASK_ID" in os.environ:
task_id = os.environ["FTRACK_LAST_TASK_ID"]
query = 'Task where id is {}'.format(task_id)
last_task = self.timer_session.query(query).one()
if (self.last_task is not None) and (self.user is not None):
self.user.start_timer(self.last_task)
self.timer_session.commit()
@ -386,7 +411,11 @@ class CountdownThread(QtCore.QThread):
json_dict = json.load(data_file)
data = json_dict['timer']
except Exception as e:
msg = 'Loading "Ftrack Config file" Failed. Please check log for more information. Times are set to default.'
msg = (
'Loading "Ftrack Config file" Failed.'
' Please check log for more information.'
' Times are set to default.'
)
log.warning("{} - {}".format(msg, str(e)))
data = self.validate_timer_values(data)
@ -485,15 +514,17 @@ class StopTimer(QtWidgets.QWidget):
def _main(self):
self.main = QtWidgets.QVBoxLayout()
self.main.setObjectName("main")
self.main.setObjectName('main')
self.form = QtWidgets.QFormLayout()
self.form.setContentsMargins(10, 15, 10, 5)
self.form.setObjectName("form")
self.form.setObjectName('form')
msg_info = "You didn't work for a long time."
msg_question = "Would you like to stop Ftrack timer?"
msg_stopped = "Your Ftrack timer was stopped. Do you want to start again?"
msg_info = 'You didn\'t work for a long time.'
msg_question = 'Would you like to stop Ftrack timer?'
msg_stopped = (
'Your Ftrack timer was stopped. Do you want to start again?'
)
self.lbl_info = QtWidgets.QLabel(msg_info)
self.lbl_info.setFont(self.font)

View file

@ -1,7 +1,8 @@
import os
import re
from pype import lib
from avalon import io, schema
from pype.lib import get_avalon_database
from avalon import schema
from bson.objectid import ObjectId
from pype.ftrack.ftrack_utils import ftrack_utils
from avalon.vendor import jsonschema
@ -11,12 +12,6 @@ ValidationError = jsonschema.ValidationError
log = Logger.getLogger(__name__)
def get_avalon_database():
if io._database is None:
io.install()
return io._database
def get_ca_mongoid():
# returns name of Custom attribute that stores mongo_id
return 'avalon_mongo_id'
@ -213,6 +208,10 @@ def import_to_avalon(
errors.append({'Entity name duplication': msg})
output['errors'] = errors
return output
# Store new ID (in case that asset was removed from DB)
else:
mongo_id = avalon_asset['_id']
else:
if avalon_asset['name'] != entity['name']:
if silo is None or changeability_check_childs(entity) is False:
@ -388,7 +387,7 @@ def get_data(entity, session, custom_attributes):
parentId = None
for parent in parents:
parentId = database[project_name](
parentId = database[project_name].find_one(
{'type': 'asset', 'name': parName}
)['_id']
if parent['parent'].entity_type != 'project' and parentId is None:

View file

@ -372,3 +372,24 @@ def get_avalon_project_template():
def get_avalon_asset_template_schema():
schema = "avalon-core:asset-2.0"
return schema
def get_avalon_database():
if io._database is None:
project = os.environ.get('AVALON_PROJECT', '')
asset = os.environ.get('AVALON_ASSET', '')
silo = os.environ.get('AVALON_SILO', '')
os.environ['AVALON_PROJECT'] = project
os.environ['AVALON_ASSET'] = asset
os.environ['AVALON_SILO'] = silo
io.install()
return io._database
def get_all_avalon_projects():
db = get_avalon_database()
project_names = db.collection_names()
projects = []
for name in project_names:
projects.append(db[name].find_one({'type': 'project'}))
return projects