mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-02 00:44:52 +01:00
Merge branch 'refs/heads/develop' into feature/PYPE-463_sync_to_avalon_event
# Conflicts: # pype/ftrack/actions/action_create_cust_attrs.py # pype/ftrack/actions/action_sync_to_avalon.py # pype/ftrack/events/action_sync_to_avalon.py # pype/ftrack/events/event_del_avalon_id_from_new.py # pype/ftrack/events/event_sync_hier_attr.py # pype/ftrack/events/event_sync_to_avalon.py # pype/ftrack/events/event_user_assigment.py
This commit is contained in:
commit
42c815a57e
722 changed files with 2659 additions and 167002 deletions
|
|
@ -1,2 +1,2 @@
|
|||
from .lib import *
|
||||
from .ftrack_server import FtrackServer
|
||||
from .ftrack_server import FtrackServer, check_ftrack_url
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import sys
|
|||
import argparse
|
||||
import logging
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import sys
|
|||
import argparse
|
||||
import logging
|
||||
import subprocess
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import argparse
|
|||
import json
|
||||
import arrow
|
||||
import logging
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.ftrack.lib.avalon_sync import cust_attr_id_key
|
||||
from pypeapp import config
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import logging
|
|||
import argparse
|
||||
import re
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from avalon import lib as avalonlib
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import re
|
|||
import argparse
|
||||
import logging
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pypeapp import config
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import json
|
|||
import argparse
|
||||
import logging
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import sys
|
|||
import logging
|
||||
from bson.objectid import ObjectId
|
||||
import argparse
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
||||
|
|
@ -277,10 +277,7 @@ class DeleteAsset(BaseAction):
|
|||
'message': 'No entities to delete in avalon'
|
||||
}
|
||||
|
||||
or_subquery = []
|
||||
for id in all_ids:
|
||||
or_subquery.append({'_id': id})
|
||||
delete_query = {'$or': or_subquery}
|
||||
delete_query = {'_id': {'$in': all_ids}}
|
||||
self.db.delete_many(delete_query)
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
||||
|
|
@ -97,10 +97,7 @@ class AssetsRemover(BaseAction):
|
|||
'message': 'None of assets'
|
||||
}
|
||||
|
||||
or_subquery = []
|
||||
for id in all_ids:
|
||||
or_subquery.append({'_id': id})
|
||||
delete_query = {'$or': or_subquery}
|
||||
delete_query = {'_id': {'$in': all_ids}}
|
||||
self.db.delete_many(delete_query)
|
||||
|
||||
self.db.uninstall()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import json
|
|||
import logging
|
||||
import subprocess
|
||||
from operator import itemgetter
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pypeapp import Logger, config
|
||||
|
||||
|
|
@ -36,12 +36,13 @@ class DJVViewAction(BaseAction):
|
|||
'file_ext', ["img", "mov", "exr"]
|
||||
)
|
||||
|
||||
def register(self):
|
||||
assert (self.djv_path is not None), (
|
||||
'DJV View is not installed'
|
||||
' or paths in presets are not set correctly'
|
||||
)
|
||||
super().register()
|
||||
def preregister(self):
|
||||
if self.djv_path is None:
|
||||
return (
|
||||
'DJV View is not installed'
|
||||
' or paths in presets are not set correctly'
|
||||
)
|
||||
return True
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
"""Return available actions based on *event*. """
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import argparse
|
|||
import logging
|
||||
import json
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -108,6 +108,7 @@ class JobKiller(BaseAction):
|
|||
'Changing Job ({}) status: {} -> failed'
|
||||
).format(job['id'], origin_status))
|
||||
except Exception:
|
||||
session.rollback()
|
||||
self.log.warning((
|
||||
'Changing Job ({}) has failed'
|
||||
).format(job['id']))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import os
|
|||
import json
|
||||
|
||||
from ruamel import yaml
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pypeapp import config
|
||||
from pype.ftrack.lib.avalon_sync import get_avalon_attr
|
||||
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
from ftrack_api import session as fa_session
|
||||
|
||||
|
||||
class PrepareProject(BaseAction):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import json
|
|||
|
||||
from pypeapp import Logger, config
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from avalon import io, api
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
|
@ -61,12 +61,12 @@ class RVAction(BaseAction):
|
|||
def set_rv_path(self):
|
||||
self.rv_path = self.config_data.get("rv_path")
|
||||
|
||||
def register(self):
|
||||
assert (self.rv_path is not None), (
|
||||
'RV is not installed'
|
||||
' or paths in presets are not set correctly'
|
||||
)
|
||||
super().register()
|
||||
def preregister(self):
|
||||
if self.rv_path is None:
|
||||
return (
|
||||
'RV is not installed or paths in presets are not set correctly'
|
||||
)
|
||||
return True
|
||||
|
||||
def get_components_from_entity(self, session, entity, components):
|
||||
"""Get components from various entity types.
|
||||
|
|
|
|||
347
pype/ftrack/actions/action_seed.py
Normal file
347
pype/ftrack/actions/action_seed.py
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
import os
|
||||
from operator import itemgetter
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
class SeedDebugProject(BaseAction):
|
||||
'''Edit meta data action.'''
|
||||
|
||||
#: Action identifier.
|
||||
identifier = "seed.debug.project"
|
||||
#: Action label.
|
||||
label = "SeedDebugProject"
|
||||
#: Action description.
|
||||
description = "Description"
|
||||
#: priority
|
||||
priority = 100
|
||||
#: roles that are allowed to register this action
|
||||
role_list = ["Pypeclub"]
|
||||
icon = "{}/ftrack/action_icons/SeedProject.svg".format(
|
||||
os.environ.get("PYPE_STATICS_SERVER", "")
|
||||
)
|
||||
|
||||
# Asset names which will be created in `Assets` entity
|
||||
assets = [
|
||||
"Addax", "Alpaca", "Ant", "Antelope", "Aye", "Badger", "Bear", "Bee",
|
||||
"Beetle", "Bluebird", "Bongo", "Bontebok", "Butterflie", "Caiman",
|
||||
"Capuchin", "Capybara", "Cat", "Caterpillar", "Coyote", "Crocodile",
|
||||
"Cuckoo", "Deer", "Dragonfly", "Duck", "Eagle", "Egret", "Elephant",
|
||||
"Falcon", "Fossa", "Fox", "Gazelle", "Gecko", "Gerbil",
|
||||
"GiantArmadillo", "Gibbon", "Giraffe", "Goose", "Gorilla",
|
||||
"Grasshoper", "Hare", "Hawk", "Hedgehog", "Heron", "Hog",
|
||||
"Hummingbird", "Hyena", "Chameleon", "Cheetah", "Iguana", "Jackal",
|
||||
"Jaguar", "Kingfisher", "Kinglet", "Kite", "Komodo", "Lemur",
|
||||
"Leopard", "Lion", "Lizard", "Macaw", "Malachite", "Mandrill",
|
||||
"Mantis", "Marmoset", "Meadowlark", "Meerkat", "Mockingbird",
|
||||
"Mongoose", "Monkey", "Nyal", "Ocelot", "Okapi", "Oribi", "Oriole",
|
||||
"Otter", "Owl", "Panda", "Parrot", "Pelican", "Pig", "Porcupine",
|
||||
"Reedbuck", "Rhinocero", "Sandpiper", "Servil", "Skink", "Sloth",
|
||||
"Snake", "Spider", "Squirrel", "Sunbird", "Swallow", "Swift", "Tiger",
|
||||
"Sylph", "Tanager", "Vulture", "Warthog", "Waterbuck", "Woodpecker",
|
||||
"Zebra"
|
||||
]
|
||||
|
||||
# Tasks which will be created for Assets
|
||||
asset_tasks = [
|
||||
"Modeling", "Lookdev", "Rigging"
|
||||
]
|
||||
# Tasks which will be created for Shots
|
||||
shot_tasks = [
|
||||
"Animation", "Lighting", "Compositing", "FX"
|
||||
]
|
||||
|
||||
# Define how much sequences will be created
|
||||
default_seq_count = 5
|
||||
# Define how much shots will be created for each sequence
|
||||
default_shots_count = 10
|
||||
|
||||
existing_projects = None
|
||||
new_project_item = "< New Project >"
|
||||
current_project_item = "< Current Project >"
|
||||
|
||||
def discover(self, session, entities, event):
|
||||
''' Validation '''
|
||||
return True
|
||||
|
||||
def interface(self, session, entities, event):
|
||||
if event["data"].get("values", {}):
|
||||
return
|
||||
|
||||
title = "Select Project where you want to create seed data"
|
||||
|
||||
items = []
|
||||
item_splitter = {"type": "label", "value": "---"}
|
||||
|
||||
description_label = {
|
||||
"type": "label",
|
||||
"value": (
|
||||
"WARNING: Action does NOT check if entities already exist !!!"
|
||||
)
|
||||
}
|
||||
items.append(description_label)
|
||||
|
||||
all_projects = session.query("select full_name from Project").all()
|
||||
self.existing_projects = [proj["full_name"] for proj in all_projects]
|
||||
projects_items = [
|
||||
{"label": proj, "value": proj} for proj in self.existing_projects
|
||||
]
|
||||
|
||||
data_items = []
|
||||
|
||||
data_items.append({
|
||||
"label": self.new_project_item,
|
||||
"value": self.new_project_item
|
||||
})
|
||||
|
||||
data_items.append({
|
||||
"label": self.current_project_item,
|
||||
"value": self.current_project_item
|
||||
})
|
||||
|
||||
data_items.extend(sorted(
|
||||
projects_items,
|
||||
key=itemgetter("label"),
|
||||
reverse=False
|
||||
))
|
||||
projects_item = {
|
||||
"label": "Choose Project",
|
||||
"type": "enumerator",
|
||||
"name": "project_name",
|
||||
"data": data_items,
|
||||
"value": self.current_project_item
|
||||
}
|
||||
items.append(projects_item)
|
||||
items.append(item_splitter)
|
||||
|
||||
items.append({
|
||||
"label": "Number of assets",
|
||||
"type": "number",
|
||||
"name": "asset_count",
|
||||
"value": len(self.assets)
|
||||
})
|
||||
items.append({
|
||||
"label": "Number of sequences",
|
||||
"type": "number",
|
||||
"name": "seq_count",
|
||||
"value": self.default_seq_count
|
||||
})
|
||||
items.append({
|
||||
"label": "Number of shots",
|
||||
"type": "number",
|
||||
"name": "shots_count",
|
||||
"value": self.default_shots_count
|
||||
})
|
||||
items.append(item_splitter)
|
||||
|
||||
note_label = {
|
||||
"type": "label",
|
||||
"value": (
|
||||
"<p><i>NOTE: Enter project name and choose schema if you "
|
||||
"chose `\"< New Project >\"`(code is optional)</i><p>"
|
||||
)
|
||||
}
|
||||
items.append(note_label)
|
||||
items.append({
|
||||
"label": "Project name",
|
||||
"name": "new_project_name",
|
||||
"type": "text",
|
||||
"value": ""
|
||||
})
|
||||
|
||||
project_schemas = [
|
||||
sch["name"] for sch in self.session.query("ProjectSchema").all()
|
||||
]
|
||||
schemas_item = {
|
||||
"label": "Choose Schema",
|
||||
"type": "enumerator",
|
||||
"name": "new_schema_name",
|
||||
"data": [
|
||||
{"label": sch, "value": sch} for sch in project_schemas
|
||||
],
|
||||
"value": project_schemas[0]
|
||||
}
|
||||
items.append(schemas_item)
|
||||
|
||||
items.append({
|
||||
"label": "*Project code",
|
||||
"name": "new_project_code",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"empty_text": "Optional..."
|
||||
})
|
||||
|
||||
return {
|
||||
"items": items,
|
||||
"title": title
|
||||
}
|
||||
|
||||
def launch(self, session, in_entities, event):
|
||||
if "values" not in event["data"]:
|
||||
return
|
||||
|
||||
# THIS IS THE PROJECT PART
|
||||
values = event["data"]["values"]
|
||||
selected_project = values["project_name"]
|
||||
if selected_project == self.new_project_item:
|
||||
project_name = values["new_project_name"]
|
||||
if project_name in self.existing_projects:
|
||||
msg = "Project \"{}\" already exist".format(project_name)
|
||||
self.log.error(msg)
|
||||
return {"success": False, "message": msg}
|
||||
|
||||
project_code = values["new_project_code"]
|
||||
project_schema_name = values["new_schema_name"]
|
||||
if not project_code:
|
||||
project_code = project_name
|
||||
project_code = project_code.lower().replace(" ", "_").strip()
|
||||
_project = session.query(
|
||||
"Project where name is \"{}\"".format(project_code)
|
||||
).first()
|
||||
if _project:
|
||||
msg = "Project with code \"{}\" already exist".format(
|
||||
project_code
|
||||
)
|
||||
self.log.error(msg)
|
||||
return {"success": False, "message": msg}
|
||||
|
||||
project_schema = session.query(
|
||||
"ProjectSchema where name is \"{}\"".format(
|
||||
project_schema_name
|
||||
)
|
||||
).one()
|
||||
# Create the project with the chosen schema.
|
||||
self.log.debug((
|
||||
"*** Creating Project: name <{}>, code <{}>, schema <{}>"
|
||||
).format(project_name, project_code, project_schema_name))
|
||||
project = session.create("Project", {
|
||||
"name": project_code,
|
||||
"full_name": project_name,
|
||||
"project_schema": project_schema
|
||||
})
|
||||
session.commit()
|
||||
|
||||
elif selected_project == self.current_project_item:
|
||||
entity = in_entities[0]
|
||||
if entity.entity_type.lower() == "project":
|
||||
project = entity
|
||||
else:
|
||||
if "project" in entity:
|
||||
project = entity["project"]
|
||||
else:
|
||||
project = entity["parent"]["project"]
|
||||
project_schema = project["project_schema"]
|
||||
self.log.debug((
|
||||
"*** Using Project: name <{}>, code <{}>, schema <{}>"
|
||||
).format(
|
||||
project["full_name"], project["name"], project_schema["name"]
|
||||
))
|
||||
else:
|
||||
project = session.query("Project where full_name is \"{}\"".format(
|
||||
selected_project
|
||||
)).one()
|
||||
project_schema = project["project_schema"]
|
||||
self.log.debug((
|
||||
"*** Using Project: name <{}>, code <{}>, schema <{}>"
|
||||
).format(
|
||||
project["full_name"], project["name"], project_schema["name"]
|
||||
))
|
||||
|
||||
# THIS IS THE MAGIC PART
|
||||
task_types = {}
|
||||
for _type in project_schema["_task_type_schema"]["types"]:
|
||||
if _type["name"] not in task_types:
|
||||
task_types[_type["name"]] = _type
|
||||
self.task_types = task_types
|
||||
|
||||
asset_count = values.get("asset_count") or len(self.assets)
|
||||
seq_count = values.get("seq_count") or self.default_seq_count
|
||||
shots_count = values.get("shots_count") or self.default_shots_count
|
||||
|
||||
self.create_assets(project, asset_count)
|
||||
self.create_shots(project, seq_count, shots_count)
|
||||
|
||||
return True
|
||||
|
||||
def create_assets(self, project, asset_count):
|
||||
self.log.debug("*** Creating assets:")
|
||||
|
||||
main_entity = self.session.create("Folder", {
|
||||
"name": "Assets",
|
||||
"parent": project
|
||||
})
|
||||
self.log.debug("- Assets")
|
||||
available_assets = len(self.assets)
|
||||
repetitive_times = (
|
||||
int(asset_count / available_assets) +
|
||||
(asset_count % available_assets > 0)
|
||||
)
|
||||
created_assets = 0
|
||||
for _asset_name in self.assets:
|
||||
if created_assets >= asset_count:
|
||||
break
|
||||
for asset_num in range(1, repetitive_times + 1):
|
||||
if created_assets >= asset_count:
|
||||
break
|
||||
asset_name = "%s_%02d" % (_asset_name, asset_num)
|
||||
asset = self.session.create("AssetBuild", {
|
||||
"name": asset_name,
|
||||
"parent": main_entity
|
||||
})
|
||||
created_assets += 1
|
||||
self.log.debug("- Assets/{}".format(asset_name))
|
||||
|
||||
for task_name in self.asset_tasks:
|
||||
self.session.create("Task", {
|
||||
"name": task_name,
|
||||
"parent": asset,
|
||||
"type": self.task_types[task_name]
|
||||
})
|
||||
self.log.debug("- Assets/{}/{}".format(
|
||||
asset_name, task_name
|
||||
))
|
||||
|
||||
self.log.debug("*** Commiting Assets")
|
||||
self.session.commit()
|
||||
|
||||
def create_shots(self, project, seq_count, shots_count):
|
||||
self.log.debug("*** Creating shots:")
|
||||
main_entity = self.session.create("Folder", {
|
||||
"name": "Shots",
|
||||
"parent": project
|
||||
})
|
||||
self.log.debug("- Shots")
|
||||
|
||||
for seq_num in range(1, seq_count+1):
|
||||
seq_name = "sq%03d" % seq_num
|
||||
seq = self.session.create("Sequence", {
|
||||
"name": seq_name,
|
||||
"parent": main_entity
|
||||
})
|
||||
self.log.debug("- Shots/{}".format(seq_name))
|
||||
|
||||
for shot_num in range(1, shots_count+1):
|
||||
shot_name = "%ssh%04d" % (seq_name, (shot_num*10))
|
||||
shot = self.session.create("Shot", {
|
||||
"name": shot_name,
|
||||
"parent": seq
|
||||
})
|
||||
self.log.debug("- Shots/{}/{}".format(seq_name, shot_name))
|
||||
|
||||
for task_name in self.shot_tasks:
|
||||
self.session.create("Task", {
|
||||
"name": task_name,
|
||||
"parent": shot,
|
||||
"type": self.task_types[task_name]
|
||||
})
|
||||
self.log.debug("- Shots/{}/{}/{}".format(
|
||||
seq_name, shot_name, task_name
|
||||
))
|
||||
|
||||
self.log.debug("*** Commiting Shots")
|
||||
self.session.commit()
|
||||
|
||||
|
||||
def register(session, plugins_presets={}):
|
||||
'''Register plugin. Called when used as an plugin.'''
|
||||
|
||||
SeedDebugProject(session, plugins_presets).register()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ from pymongo import UpdateOne
|
|||
from pype.ftrack import BaseAction
|
||||
from pype.ftrack.lib import avalon_sync
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
import ftrack_api
|
||||
from ftrack_api import session as fa_session
|
||||
|
||||
|
||||
class SyncEntitiesFactory:
|
||||
|
|
@ -318,8 +318,8 @@ class SyncEntitiesFactory:
|
|||
|
||||
def filter_by_duplicate_regex(self):
|
||||
filter_queue = queue.Queue()
|
||||
failed_regex_msg = "{} - Entity has invalid symbol/s in name"
|
||||
duplicate_msg = "Multiple entities have name \"{}\":"
|
||||
failed_regex_msg = "{} - Entity has invalid symbols in the name"
|
||||
duplicate_msg = "There are multiple entities with the name: \"{}\":"
|
||||
|
||||
for ids in self.failed_regex.values():
|
||||
for id in ids:
|
||||
|
|
@ -547,15 +547,19 @@ class SyncEntitiesFactory:
|
|||
])
|
||||
|
||||
cust_attr_query = (
|
||||
"select value, entity_id from CustomAttributeValue "
|
||||
"select value, entity_id from ContextCustomAttributeValue "
|
||||
"where entity_id in ({}) and configuration.key in ({})"
|
||||
)
|
||||
[values] = self.session._call([{
|
||||
call_expr = [{
|
||||
"action": "query",
|
||||
"expression": cust_attr_query.format(
|
||||
entity_ids_joined, attributes_joined
|
||||
)
|
||||
}])
|
||||
}]
|
||||
if hasattr(self.session, "_call"):
|
||||
[values] = self.session._call(call_expr)
|
||||
else:
|
||||
[values] = self.session.call(call_expr)
|
||||
|
||||
for value in values["data"]:
|
||||
entity_id = value["entity_id"]
|
||||
|
|
@ -608,13 +612,17 @@ class SyncEntitiesFactory:
|
|||
attributes_joined = ", ".join([
|
||||
"\"{}\"".format(name) for name in attribute_names
|
||||
])
|
||||
[values] = self.session._call([{
|
||||
call_expr = [{
|
||||
"action": "query",
|
||||
"expression": (
|
||||
"select value, entity_id from CustomAttributeValue "
|
||||
"select value, entity_id from ContextCustomAttributeValue "
|
||||
"where entity_id in ({}) and configuration.key in ({})"
|
||||
).format(entity_ids_joined, attributes_joined)
|
||||
}])
|
||||
}]
|
||||
if hasattr(self.session, "_call"):
|
||||
[values] = self.session._call(call_expr)
|
||||
else:
|
||||
[values] = self.session.call(call_expr)
|
||||
|
||||
avalon_hier = []
|
||||
for value in values["data"]:
|
||||
|
|
@ -749,8 +757,8 @@ class SyncEntitiesFactory:
|
|||
|
||||
if not_set_ids:
|
||||
self.log.debug((
|
||||
"- Debug information: Filtering bug, in entities dict are "
|
||||
"empty dicts (function should not affect) <{}>"
|
||||
"- Debug information: Filtering bug, there are empty dicts"
|
||||
"in entities dict (functionality should not be affected) <{}>"
|
||||
).format("| ".join(not_set_ids)))
|
||||
for id in not_set_ids:
|
||||
self.entities_dict.pop(id)
|
||||
|
|
@ -913,7 +921,7 @@ class SyncEntitiesFactory:
|
|||
self.deleted_entities = deleted_entities
|
||||
|
||||
self.log.debug((
|
||||
"Ftrack -> Avalon comparation: New <{}> "
|
||||
"Ftrack -> Avalon comparison: New <{}> "
|
||||
"| Existing <{}> | Deleted <{}>"
|
||||
).format(
|
||||
len(create_ftrack_ids),
|
||||
|
|
@ -1046,9 +1054,9 @@ class SyncEntitiesFactory:
|
|||
))
|
||||
self._ent_paths_by_ftrack_id.pop(ftrack_id, None)
|
||||
msg = (
|
||||
"<Entity renamed back> It is not allowed to change"
|
||||
" name of entity or it's parents"
|
||||
" that already has published context"
|
||||
"<Entity renamed back> It is not possible to change"
|
||||
" the name of an entity or it's parents, "
|
||||
" if it already contained published data."
|
||||
)
|
||||
self.report_items["warning"][msg].append(ent_path)
|
||||
|
||||
|
|
@ -1068,14 +1076,14 @@ class SyncEntitiesFactory:
|
|||
# TODO logging
|
||||
ent_path = self.get_ent_path(ftrack_id)
|
||||
msg = (
|
||||
"<Entity moved back in hierachy> It is not allowed"
|
||||
" to change hierarchy of entity or it's parents"
|
||||
" that already has published context"
|
||||
"<Entity moved back in hierachy> It is not possible"
|
||||
" to change the hierarchy of an entity or it's parents,"
|
||||
" if it already contained published data."
|
||||
)
|
||||
self.report_items["warning"][msg].append(ent_path)
|
||||
self.log.warning((
|
||||
"Entity has published context so was moved"
|
||||
" back in hierarchy <{}>"
|
||||
" Entity contains published data so it was moved"
|
||||
" back to it's original hierarchy <{}>"
|
||||
).format(ent_path))
|
||||
self.entities_dict[ftrack_id]["entity"]["parent_id"] = (
|
||||
old_ftrack_parent_id
|
||||
|
|
@ -1116,14 +1124,14 @@ class SyncEntitiesFactory:
|
|||
# TODO logging
|
||||
# TODO report (turn off auto-sync?)
|
||||
self.log.error((
|
||||
"Entity has published context but was moved in"
|
||||
" hierarchy and previous parent was not found so it is"
|
||||
" not possible to solve this programmatically <{}>"
|
||||
"The entity contains published data but it was moved to"
|
||||
" a different place in the hierarchy and it's previous"
|
||||
" parent cannot be found."
|
||||
" It's impossible to solve this programmatically <{}>"
|
||||
).format(ent_path))
|
||||
msg = (
|
||||
"<Entity not synchronizable> Parent of entity can't be"
|
||||
" changed due to published context and previous parent"
|
||||
" was not found"
|
||||
"<Entity can't be synchronised> Hierarchy of an entity" " can't be changed due to published data and missing"
|
||||
" previous parent"
|
||||
)
|
||||
self.report_items["error"][msg].append(ent_path)
|
||||
self.filter_with_children(ftrack_id)
|
||||
|
|
@ -1143,14 +1151,15 @@ class SyncEntitiesFactory:
|
|||
):
|
||||
ent_path = self.get_ent_path(ftrack_id)
|
||||
self.log.error((
|
||||
"Entity has published context but was moved in"
|
||||
" hierarchy and previous parents were moved too it is"
|
||||
" not possible to solve this programmatically <{}>"
|
||||
"The entity contains published data but it was moved to"
|
||||
" a different place in the hierarchy and it's previous"
|
||||
" parents were moved too."
|
||||
" It's impossible to solve this programmatically <{}>"
|
||||
).format(ent_path))
|
||||
msg = (
|
||||
"<Entity not synchronizable> Parent of entity can't be"
|
||||
" changed due to published context but whole hierarchy"
|
||||
" was scrambled"
|
||||
"<Entity not synchronizable> Hierarchy of an entity"
|
||||
" can't be changed due to published data and scrambled"
|
||||
"hierarchy"
|
||||
)
|
||||
continue
|
||||
|
||||
|
|
@ -1160,8 +1169,8 @@ class SyncEntitiesFactory:
|
|||
entities_to_create = []
|
||||
# TODO logging
|
||||
self.log.warning(
|
||||
"Ftrack entities must be recreated because have"
|
||||
" published context but were removed"
|
||||
"Ftrack entities must be recreated because they were deleted,"
|
||||
" but they contain published data."
|
||||
)
|
||||
|
||||
_avalon_ent = old_parent_ent
|
||||
|
|
@ -1178,13 +1187,13 @@ class SyncEntitiesFactory:
|
|||
# TODO report
|
||||
# TODO logging
|
||||
self.log.error((
|
||||
"Can't recreate entity in Ftrack because entity with"
|
||||
" same name already exists in different hierarchy <{}>"
|
||||
"Can't recreate the entity in Ftrack because an entity" " with the same name already exists in a different"
|
||||
" place in the hierarchy <{}>"
|
||||
).format(av_ent_path))
|
||||
msg = (
|
||||
"<Entity not synchronizable> Parent of entity can't be"
|
||||
" changed due to published context but previous parent"
|
||||
" had name that exist in different hierarchy level"
|
||||
"<Entity not synchronizable> Hierarchy of an entity"
|
||||
" can't be changed. I contains published data and it's" " previous parent had a name, that is duplicated at a "
|
||||
" different hierarchy level"
|
||||
)
|
||||
self.report_items["error"][msg].append(av_ent_path)
|
||||
self.filter_with_children(ftrack_id)
|
||||
|
|
@ -1526,7 +1535,7 @@ class SyncEntitiesFactory:
|
|||
else:
|
||||
# TODO logging - What is happening here?
|
||||
self.log.warning((
|
||||
"In avalon are entities without valid parents that"
|
||||
"Avalon contains entities without valid parents that"
|
||||
" lead to Project (should not cause errors)"
|
||||
" - MongoId <{}>"
|
||||
).format(str(entity_id)))
|
||||
|
|
@ -1670,7 +1679,7 @@ class SyncEntitiesFactory:
|
|||
"Project code was changed back to \"{}\"".format(avalon_code)
|
||||
)
|
||||
msg = (
|
||||
"It is not allowed to change"
|
||||
"It is not possible to change"
|
||||
" project code after synchronization"
|
||||
)
|
||||
self.report_items["warning"][msg] = sub_msg
|
||||
|
|
@ -1895,8 +1904,8 @@ class SyncEntitiesFactory:
|
|||
|
||||
ent_path = self.get_ent_path(new_entity_id)
|
||||
msg = (
|
||||
"Deleted entity was recreated because had (or his children)"
|
||||
" published context"
|
||||
"Deleted entity was recreated because it or its children"
|
||||
" contain published data"
|
||||
)
|
||||
|
||||
self.report_items["info"][msg].append(ent_path)
|
||||
|
|
@ -1906,7 +1915,7 @@ class SyncEntitiesFactory:
|
|||
def regex_duplicate_interface(self):
|
||||
items = []
|
||||
if self.failed_regex or self.tasks_failed_regex:
|
||||
subtitle = "Not allowed symbols in entity names:"
|
||||
subtitle = "Entity names contain prohibited symbols:"
|
||||
items.append({
|
||||
"type": "label",
|
||||
"value": "# {}".format(subtitle)
|
||||
|
|
@ -1914,7 +1923,7 @@ class SyncEntitiesFactory:
|
|||
items.append({
|
||||
"type": "label",
|
||||
"value": (
|
||||
"<p><i>NOTE: Allowed symbols are Letters( a-Z ),"
|
||||
"<p><i>NOTE: You can use Letters( a-Z ),"
|
||||
" Numbers( 0-9 ) and Underscore( _ )</i></p>"
|
||||
)
|
||||
})
|
||||
|
|
@ -1967,8 +1976,8 @@ class SyncEntitiesFactory:
|
|||
items.append({
|
||||
"type": "label",
|
||||
"value": (
|
||||
"<p><i>NOTE: It is not allowed to have same name"
|
||||
" for multiple entities in one project</i></p>"
|
||||
"<p><i>NOTE: It is not allowed to use the same name"
|
||||
" for multiple entities in the same project</i></p>"
|
||||
)
|
||||
})
|
||||
log_msgs = []
|
||||
|
|
@ -2165,7 +2174,7 @@ class SyncToAvalonLocal(BaseAction):
|
|||
self.log.error(
|
||||
"Synchronization failed due to code error", exc_info=True
|
||||
)
|
||||
msg = "An error has happened during synchronization"
|
||||
msg = "An error occurred during synchronization"
|
||||
title = "Synchronization report ({}):".format(ft_project_name)
|
||||
items = []
|
||||
items.append({
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import collections
|
|||
import json
|
||||
import re
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from avalon import io, inventory, schema
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import argparse
|
|||
import logging
|
||||
import json
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ class ThumbToChildren(BaseAction):
|
|||
'description': 'Push thumbnails to Childrens'
|
||||
})
|
||||
})
|
||||
|
||||
session.commit()
|
||||
try:
|
||||
for entity in entities:
|
||||
thumbid = entity['thumbnail_id']
|
||||
|
|
@ -53,10 +53,11 @@ class ThumbToChildren(BaseAction):
|
|||
|
||||
# inform the user that the job is done
|
||||
job['status'] = 'done'
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
session.rollback()
|
||||
# fail the job if something goes wrong
|
||||
job['status'] = 'failed'
|
||||
raise
|
||||
raise exc
|
||||
finally:
|
||||
session.commit()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import sys
|
|||
import argparse
|
||||
import logging
|
||||
import json
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
@ -40,9 +40,9 @@ class ThumbToParent(BaseAction):
|
|||
'status': 'running',
|
||||
'data': json.dumps({
|
||||
'description': 'Push thumbnails to parents'
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
session.commit()
|
||||
try:
|
||||
for entity in entities:
|
||||
parent = None
|
||||
|
|
@ -74,10 +74,11 @@ class ThumbToParent(BaseAction):
|
|||
# inform the user that the job is done
|
||||
job['status'] = status or 'done'
|
||||
|
||||
except Exception as e:
|
||||
except Exception as exc:
|
||||
session.rollback()
|
||||
# fail the job if something goes wrong
|
||||
job['status'] = 'failed'
|
||||
raise e
|
||||
raise exc
|
||||
|
||||
finally:
|
||||
session.commit()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import collections
|
|||
import json
|
||||
import re
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from avalon import io, inventory, schema
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
from ftrack_api import session as fa_session
|
||||
|
||||
|
||||
class ActionAskWhereIRun(BaseAction):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import platform
|
||||
import socket
|
||||
import getpass
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseAction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ from pymongo import UpdateOne
|
|||
from pype.ftrack import BaseAction
|
||||
from pype.ftrack.lib import avalon_sync
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
import ftrack_api
|
||||
from ftrack_api import session as fa_session
|
||||
from pypeapp import config
|
||||
|
||||
|
||||
|
|
@ -548,7 +548,7 @@ class SyncEntitiesFactory:
|
|||
])
|
||||
|
||||
cust_attr_query = (
|
||||
"select value, entity_id from CustomAttributeValue "
|
||||
"select value, entity_id from ContextCustomAttributeValue "
|
||||
"where entity_id in ({}) and configuration.key in ({})"
|
||||
)
|
||||
[values] = self.session._call([{
|
||||
|
|
@ -612,7 +612,7 @@ class SyncEntitiesFactory:
|
|||
[values] = self.session._call([{
|
||||
"action": "query",
|
||||
"expression": (
|
||||
"select value, entity_id from CustomAttributeValue "
|
||||
"select value, entity_id from ContextCustomAttributeValue "
|
||||
"where entity_id in ({}) and configuration.key in ({})"
|
||||
).format(entity_ids_joined, attributes_joined)
|
||||
}])
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.ftrack import BaseEvent
|
||||
import BaseEvent
|
||||
from pype.ftrack.lib import avalon_sync
|
||||
from pype.ftrack.events.event_sync_to_avalon import SyncToAvalonEvent
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
import operator
|
||||
|
||||
|
|
@ -80,10 +80,10 @@ class NextTaskUpdate(BaseEvent):
|
|||
'>>> [ {} ] updated to [ Ready ]'
|
||||
).format(path))
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
self.log.warning((
|
||||
'!!! [ {} ] status couldnt be set: [ {} ]'
|
||||
).format(path, e))
|
||||
session.rollback()
|
||||
).format(path, str(e)), exc_info=True)
|
||||
|
||||
|
||||
def register(session, plugins_presets):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from pype.vendor import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
import ftrack_api
|
||||
from pype.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class Radio_buttons(BaseEvent):
|
||||
class RadioButtons(BaseEvent):
|
||||
|
||||
ignore_me = True
|
||||
|
||||
|
|
@ -37,4 +37,4 @@ class Radio_buttons(BaseEvent):
|
|||
def register(session, plugins_presets):
|
||||
'''Register plugin. Called when used as an plugin.'''
|
||||
|
||||
Radio_buttons(session, plugins_presets).register()
|
||||
RadioButtons(session, plugins_presets).register()
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ from pype.ftrack.lib import avalon_sync
|
|||
from pype.ftrack.lib.avalon_sync import (
|
||||
cust_attr_id_key, cust_attr_auto_sync, entity_schemas
|
||||
)
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
import ftrack_api
|
||||
from ftrack_api import session as fa_session
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
|
||||
class Test_Event(BaseEvent):
|
||||
class TestEvent(BaseEvent):
|
||||
|
||||
ignore_me = True
|
||||
|
||||
|
|
@ -23,4 +23,4 @@ class Test_Event(BaseEvent):
|
|||
def register(session, plugins_presets):
|
||||
'''Register plugin. Called when used as an plugin.'''
|
||||
|
||||
Test_Event(session, plugins_presets).register()
|
||||
TestEvent(session, plugins_presets).register()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -49,7 +49,10 @@ class ThumbnailEvents(BaseEvent):
|
|||
|
||||
self.log.info(msg)
|
||||
|
||||
session.commit()
|
||||
try:
|
||||
session.commit()
|
||||
except Exception:
|
||||
session.rollback()
|
||||
|
||||
|
||||
def register(session, plugins_presets):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
from pype.ftrack.lib import avalon_sync
|
||||
from pype.ftrack.lib.io_nonsingleton import DbConnector
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -6,7 +6,6 @@ class VersionToTaskStatus(BaseEvent):
|
|||
|
||||
def launch(self, session, event):
|
||||
'''Propagates status from version to task when changed'''
|
||||
session.commit()
|
||||
|
||||
# start of event procedure ----------------------------------
|
||||
for entity in event['data'].get('entities', []):
|
||||
|
|
@ -62,8 +61,10 @@ class VersionToTaskStatus(BaseEvent):
|
|||
task['status'] = task_status
|
||||
session.commit()
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
self.log.warning('!!! [ {} ] status couldnt be set:\
|
||||
[ {} ]'.format(path, e))
|
||||
session.rollback()
|
||||
else:
|
||||
self.log.info('>>> [ {} ] updated to [ {} ]'.format(
|
||||
path, task_status['name']))
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
from .ftrack_server import FtrackServer
|
||||
from .lib import check_ftrack_url
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ import atexit
|
|||
import time
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pype.ftrack.lib import credentials
|
||||
from pype.ftrack.ftrack_server import FtrackServer
|
||||
from pype.ftrack.ftrack_server.lib import ftrack_events_mongo_settings
|
||||
from pype.ftrack.ftrack_server.lib import (
|
||||
ftrack_events_mongo_settings, check_ftrack_url
|
||||
)
|
||||
import socket_thread
|
||||
|
||||
|
||||
|
|
@ -25,36 +26,6 @@ class MongoPermissionsError(Exception):
|
|||
super().__init__(message)
|
||||
|
||||
|
||||
def check_ftrack_url(url, log_errors=True):
|
||||
"""Checks if Ftrack server is responding"""
|
||||
if not url:
|
||||
print('ERROR: Ftrack URL is not set!')
|
||||
return None
|
||||
|
||||
url = url.strip('/ ')
|
||||
|
||||
if 'http' not in url:
|
||||
if url.endswith('ftrackapp.com'):
|
||||
url = 'https://' + url
|
||||
else:
|
||||
url = 'https://{0}.ftrackapp.com'.format(url)
|
||||
try:
|
||||
result = requests.get(url, allow_redirects=False)
|
||||
except requests.exceptions.RequestException:
|
||||
if log_errors:
|
||||
print('ERROR: Entered Ftrack URL is not accesible!')
|
||||
return False
|
||||
|
||||
if (result.status_code != 200 or 'FTRACK_VERSION' not in result.headers):
|
||||
if log_errors:
|
||||
print('ERROR: Entered Ftrack URL is not accesible!')
|
||||
return False
|
||||
|
||||
print('DEBUG: Ftrack server {} is accessible.'.format(url))
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def check_mongo_url(host, port, log_error=False):
|
||||
"""Checks if mongo server is responding"""
|
||||
sock = None
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import sys
|
||||
import types
|
||||
import importlib
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
import time
|
||||
import logging
|
||||
import inspect
|
||||
|
|
@ -100,7 +100,10 @@ class FtrackServer:
|
|||
log.warning(msg, exc_info=e)
|
||||
|
||||
if len(register_functions_dict) < 1:
|
||||
raise Exception
|
||||
raise Exception((
|
||||
"There are no events with register function."
|
||||
" Registered paths: \"{}\""
|
||||
).format("| ".join(paths)))
|
||||
|
||||
# Load presets for setting plugins
|
||||
key = "user"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import requests
|
||||
try:
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
except ImportError:
|
||||
|
|
@ -66,3 +67,33 @@ def get_ftrack_event_mongo_info():
|
|||
url = "mongodb://{}{}{}{}".format(user_pass, socket_path, dab, auth)
|
||||
|
||||
return url, database, collection
|
||||
|
||||
|
||||
def check_ftrack_url(url, log_errors=True):
|
||||
"""Checks if Ftrack server is responding"""
|
||||
if not url:
|
||||
print('ERROR: Ftrack URL is not set!')
|
||||
return None
|
||||
|
||||
url = url.strip('/ ')
|
||||
|
||||
if 'http' not in url:
|
||||
if url.endswith('ftrackapp.com'):
|
||||
url = 'https://' + url
|
||||
else:
|
||||
url = 'https://{0}.ftrackapp.com'.format(url)
|
||||
try:
|
||||
result = requests.get(url, allow_redirects=False)
|
||||
except requests.exceptions.RequestException:
|
||||
if log_errors:
|
||||
print('ERROR: Entered Ftrack URL is not accesible!')
|
||||
return False
|
||||
|
||||
if (result.status_code != 200 or 'FTRACK_VERSION' not in result.headers):
|
||||
if log_errors:
|
||||
print('ERROR: Entered Ftrack URL is not accesible!')
|
||||
return False
|
||||
|
||||
print('DEBUG: Ftrack server {} is accessible.'.format(url))
|
||||
|
||||
return url
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ def main(args):
|
|||
server.run_server(session)
|
||||
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
traceback.print_tb(exc.__traceback__)
|
||||
log.error("Event server crashed. See traceback below", exc_info=True)
|
||||
|
||||
finally:
|
||||
log.debug("First closing socket")
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import signal
|
|||
import threading
|
||||
|
||||
from ftrack_server import FtrackServer
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.vendor.ftrack_api.event.hub import EventHub
|
||||
import ftrack_api
|
||||
from ftrack_api.event.hub import EventHub
|
||||
from pypeapp import Logger
|
||||
|
||||
log = Logger().get_logger("Event Server Legacy")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
import json
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
import appdirs
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ import pymongo
|
|||
from pymongo.client_session import ClientSession
|
||||
|
||||
class NotActiveTable(Exception):
|
||||
pass
|
||||
def __init__(self, *args, **kwargs):
|
||||
msg = "Active table is not set. (This is bug)"
|
||||
if not (args or kwargs):
|
||||
args = (default_message,)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def auto_reconnect(func):
|
||||
"""Handling auto reconnect in 3 retry times"""
|
||||
|
|
@ -37,7 +42,16 @@ def auto_reconnect(func):
|
|||
time.sleep(0.1)
|
||||
else:
|
||||
raise
|
||||
return decorated
|
||||
|
||||
|
||||
def check_active_table(func):
|
||||
"""Check if DbConnector has active table before db method is called"""
|
||||
@functools.wraps(func)
|
||||
def decorated(obj, *args, **kwargs):
|
||||
if not obj.active_table:
|
||||
raise NotActiveTable()
|
||||
return func(obj, *args, **kwargs)
|
||||
return decorated
|
||||
|
||||
|
||||
|
|
@ -53,7 +67,6 @@ def check_active_table(func):
|
|||
|
||||
|
||||
class DbConnector:
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
timeout = 1000
|
||||
|
||||
|
|
@ -68,10 +81,18 @@ class DbConnector:
|
|||
|
||||
self.active_table = table_name
|
||||
|
||||
def __getitem__(self, key):
|
||||
# gives direct access to collection withou setting `active_table`
|
||||
return self._database[key]
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
# not all methods of PyMongo database are implemented with this it is
|
||||
# possible to use them too
|
||||
try:
|
||||
return super().__getattribute__(attr)
|
||||
return super(DbConnector, self).__getattribute__(attr)
|
||||
except AttributeError:
|
||||
if self.active_table is None:
|
||||
raise NotActiveTable()
|
||||
return self._database[self.active_table].__getattribute__(attr)
|
||||
|
||||
def install(self):
|
||||
|
|
@ -131,6 +152,15 @@ class DbConnector:
|
|||
def exist_table(self, table_name):
|
||||
return table_name in self.tables()
|
||||
|
||||
def create_table(self, name, **options):
|
||||
if self.exist_table(name):
|
||||
return
|
||||
|
||||
return self._database.create_collection(name, **options)
|
||||
|
||||
def exist_table(self, table_name):
|
||||
return table_name in self.tables()
|
||||
|
||||
def tables(self):
|
||||
"""List available tables
|
||||
Returns:
|
||||
|
|
@ -166,18 +196,21 @@ class DbConnector:
|
|||
@check_active_table
|
||||
@auto_reconnect
|
||||
def find(self, filter, projection=None, sort=None, **options):
|
||||
options["projection"] = projection
|
||||
options["sort"] = sort
|
||||
return self._database[self.active_table].find(filter, **options)
|
||||
return self._database[self.active_table].find(
|
||||
filter, projection, **options
|
||||
)
|
||||
|
||||
@check_active_table
|
||||
@auto_reconnect
|
||||
def find_one(self, filter, projection=None, sort=None, **options):
|
||||
assert isinstance(filter, dict), "filter must be <dict>"
|
||||
|
||||
options["projection"] = projection
|
||||
options["sort"] = sort
|
||||
return self._database[self.active_table].find_one(filter, **options)
|
||||
return self._database[self.active_table].find_one(
|
||||
filter,
|
||||
projection,
|
||||
**options
|
||||
)
|
||||
|
||||
@check_active_table
|
||||
@auto_reconnect
|
||||
|
|
@ -202,8 +235,8 @@ class DbConnector:
|
|||
|
||||
@check_active_table
|
||||
@auto_reconnect
|
||||
def distinct(self, *args, **kwargs):
|
||||
return self._database[self.active_table].distinct(*args, **kwargs)
|
||||
def distinct(self, **options):
|
||||
return self._database[self.active_table].distinct(**options)
|
||||
|
||||
@check_active_table
|
||||
@auto_reconnect
|
||||
|
|
@ -216,10 +249,14 @@ class DbConnector:
|
|||
@auto_reconnect
|
||||
def delete_one(self, filter, collation=None, **options):
|
||||
options["collation"] = collation
|
||||
return self._database[self.active_table].delete_one(filter, **options)
|
||||
return self._database[self.active_table].delete_one(
|
||||
filter, **options
|
||||
)
|
||||
|
||||
@check_active_table
|
||||
@auto_reconnect
|
||||
def delete_many(self, filter, collation=None, **options):
|
||||
options["collation"] = collation
|
||||
return self._database[self.active_table].delete_many(filter, **options)
|
||||
return self._database[self.active_table].delete_many(
|
||||
filter, **options
|
||||
)
|
||||
|
|
|
|||
|
|
@ -345,25 +345,44 @@ class AppAction(BaseHandler):
|
|||
statuses = presets['status_update']
|
||||
|
||||
actual_status = entity['status']['name'].lower()
|
||||
next_status_name = None
|
||||
for key, value in statuses.items():
|
||||
if actual_status in value or '_any_' in value:
|
||||
if key != '_ignore_':
|
||||
next_status_name = key
|
||||
already_tested = []
|
||||
ent_path = "/".join(
|
||||
[ent["name"] for ent in entity['link']]
|
||||
)
|
||||
while True:
|
||||
next_status_name = None
|
||||
for key, value in statuses.items():
|
||||
if key in already_tested:
|
||||
continue
|
||||
if actual_status in value or '_any_' in value:
|
||||
if key != '_ignore_':
|
||||
next_status_name = key
|
||||
already_tested.append(key)
|
||||
break
|
||||
already_tested.append(key)
|
||||
|
||||
if next_status_name is None:
|
||||
break
|
||||
|
||||
if next_status_name is not None:
|
||||
try:
|
||||
query = 'Status where name is "{}"'.format(
|
||||
next_status_name
|
||||
)
|
||||
status = session.query(query).one()
|
||||
|
||||
entity['status'] = status
|
||||
session.commit()
|
||||
self.log.debug("Changing status to \"{}\" <{}>".format(
|
||||
next_status_name, ent_path
|
||||
))
|
||||
break
|
||||
|
||||
except Exception:
|
||||
session.rollback()
|
||||
msg = (
|
||||
'Status "{}" in presets wasn\'t found on Ftrack'
|
||||
).format(next_status_name)
|
||||
'Status "{}" in presets wasn\'t found'
|
||||
' on Ftrack entity type "{}"'
|
||||
).format(next_status_name, entity.entity_type)
|
||||
self.log.warning(msg)
|
||||
|
||||
# Set origin avalon environments
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import functools
|
||||
import time
|
||||
from pypeapp import Logger
|
||||
from pype.vendor import ftrack_api
|
||||
from pype.vendor.ftrack_api import session as fa_session
|
||||
import ftrack_api
|
||||
from ftrack_api import session as fa_session
|
||||
from pype.ftrack.ftrack_server import session_processor
|
||||
|
||||
|
||||
|
|
@ -13,6 +13,13 @@ class MissingPermision(Exception):
|
|||
super().__init__(message)
|
||||
|
||||
|
||||
class PreregisterException(Exception):
|
||||
def __init__(self, message=None):
|
||||
if not message:
|
||||
message = "Pre-registration conditions were not met"
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class BaseHandler(object):
|
||||
'''Custom Action base class
|
||||
|
||||
|
|
@ -89,15 +96,17 @@ class BaseHandler(object):
|
|||
'!{} "{}" - You\'re missing required {} permissions'
|
||||
).format(self.type, label, str(MPE)))
|
||||
except AssertionError as ae:
|
||||
self.log.info((
|
||||
self.log.warning((
|
||||
'!{} "{}" - {}'
|
||||
).format(self.type, label, str(ae)))
|
||||
except NotImplementedError:
|
||||
self.log.error((
|
||||
'{} "{}" - Register method is not implemented'
|
||||
).format(
|
||||
self.type, label)
|
||||
)
|
||||
).format(self.type, label))
|
||||
except PreregisterException as exc:
|
||||
self.log.warning((
|
||||
'{} "{}" - {}'
|
||||
).format(self.type, label, str(exc)))
|
||||
except Exception as e:
|
||||
self.log.error('{} "{}" - Registration failed ({})'.format(
|
||||
self.type, label, str(e))
|
||||
|
|
@ -119,6 +128,7 @@ class BaseHandler(object):
|
|||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as exc:
|
||||
self.session.rollback()
|
||||
msg = '{} "{}": Failed ({})'.format(self.type, label, str(exc))
|
||||
self.log.error(msg, exc_info=True)
|
||||
return {
|
||||
|
|
@ -163,10 +173,10 @@ class BaseHandler(object):
|
|||
|
||||
if result is True:
|
||||
return
|
||||
msg = "Pre-register conditions were not met"
|
||||
msg = None
|
||||
if isinstance(result, str):
|
||||
msg = result
|
||||
raise Exception(msg)
|
||||
raise PreregisterException(msg)
|
||||
|
||||
def preregister(self):
|
||||
'''
|
||||
|
|
@ -593,3 +603,24 @@ class BaseHandler(object):
|
|||
self.log.debug(
|
||||
"Action \"{}\" Triggered successfully".format(action_name)
|
||||
)
|
||||
|
||||
def trigger_event(
|
||||
self, topic, event_data={}, session=None, source=None,
|
||||
event=None, on_error="ignore"
|
||||
):
|
||||
if session is None:
|
||||
session = self.session
|
||||
|
||||
if not source and event:
|
||||
source = event.get("source")
|
||||
# Create and trigger event
|
||||
event = fa_session.ftrack_api.event.base.Event(
|
||||
topic=topic,
|
||||
data=event_data,
|
||||
source=source
|
||||
)
|
||||
session.event_hub.publish(event, on_error=on_error)
|
||||
|
||||
self.log.debug((
|
||||
"Publishing event: {}"
|
||||
).format(str(event.__dict__)))
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class BaseEvent(BaseHandler):
|
|||
try:
|
||||
func(*args, **kwargs)
|
||||
except Exception as exc:
|
||||
self.session.rollback()
|
||||
self.log.error(
|
||||
'Event "{}" Failed: {}'.format(
|
||||
self.__class__.__name__, str(exc)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import threading
|
|||
import time
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
|
||||
from pype.vendor import ftrack_api
|
||||
import ftrack_api
|
||||
from pypeapp import style
|
||||
from pype.ftrack import FtrackServer, credentials
|
||||
from pype.ftrack import FtrackServer, check_ftrack_url, credentials
|
||||
from . import login_dialog
|
||||
|
||||
from pype import api as pype
|
||||
|
|
@ -24,7 +24,8 @@ class FtrackModule:
|
|||
self.thread_timer = None
|
||||
|
||||
self.bool_logged = False
|
||||
self.bool_action_server = False
|
||||
self.bool_action_server_running = False
|
||||
self.bool_action_thread_running = False
|
||||
self.bool_timer_event = False
|
||||
|
||||
def show_login_widget(self):
|
||||
|
|
@ -74,28 +75,50 @@ class FtrackModule:
|
|||
|
||||
# Actions part
|
||||
def start_action_server(self):
|
||||
self.bool_action_thread_running = True
|
||||
self.set_menu_visibility()
|
||||
if (
|
||||
self.thread_action_server is not None and
|
||||
self.bool_action_thread_running is False
|
||||
):
|
||||
self.stop_action_server()
|
||||
|
||||
if self.thread_action_server is None:
|
||||
self.thread_action_server = threading.Thread(
|
||||
target=self.set_action_server
|
||||
)
|
||||
self.thread_action_server.daemon = True
|
||||
self.thread_action_server.start()
|
||||
|
||||
log.info("Ftrack action server launched")
|
||||
self.bool_action_server = True
|
||||
self.set_menu_visibility()
|
||||
|
||||
def set_action_server(self):
|
||||
try:
|
||||
self.action_server.run_server()
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
"Ftrack Action server crashed! Please try to start again.",
|
||||
exc_info=True
|
||||
first_check = True
|
||||
while self.bool_action_thread_running is True:
|
||||
if not check_ftrack_url(os.environ['FTRACK_SERVER']):
|
||||
if first_check:
|
||||
log.warning(
|
||||
"Could not connect to Ftrack server"
|
||||
)
|
||||
first_check = False
|
||||
time.sleep(1)
|
||||
continue
|
||||
log.info(
|
||||
"Connected to Ftrack server. Running actions session"
|
||||
)
|
||||
# TODO show message to user
|
||||
self.bool_action_server = False
|
||||
try:
|
||||
self.bool_action_server_running = True
|
||||
self.set_menu_visibility()
|
||||
self.action_server.run_server()
|
||||
if self.bool_action_thread_running:
|
||||
log.debug("Ftrack action server has stopped")
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Ftrack Action server crashed. Trying to connect again",
|
||||
exc_info=True
|
||||
)
|
||||
self.bool_action_server_running = False
|
||||
self.set_menu_visibility()
|
||||
first_check = True
|
||||
|
||||
self.bool_action_thread_running = False
|
||||
|
||||
def reset_action_server(self):
|
||||
self.stop_action_server()
|
||||
|
|
@ -103,16 +126,21 @@ class FtrackModule:
|
|||
|
||||
def stop_action_server(self):
|
||||
try:
|
||||
self.bool_action_thread_running = False
|
||||
self.action_server.stop_session()
|
||||
if self.thread_action_server is not None:
|
||||
self.thread_action_server.join()
|
||||
self.thread_action_server = None
|
||||
|
||||
log.info("Ftrack action server stopped")
|
||||
self.bool_action_server = False
|
||||
log.info("Ftrack action server was forced to stop")
|
||||
|
||||
self.bool_action_server_running = False
|
||||
self.set_menu_visibility()
|
||||
except Exception as e:
|
||||
log.error("During Killing action server: {0}".format(e))
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Error has happened during Killing action server",
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
# Definition of Tray menu
|
||||
def tray_menu(self, parent_menu):
|
||||
|
|
@ -158,6 +186,9 @@ class FtrackModule:
|
|||
def tray_start(self):
|
||||
self.validate()
|
||||
|
||||
def tray_exit(self):
|
||||
self.stop_action_server()
|
||||
|
||||
# Definition of visibility of each menu actions
|
||||
def set_menu_visibility(self):
|
||||
|
||||
|
|
@ -170,9 +201,9 @@ class FtrackModule:
|
|||
self.stop_timer_thread()
|
||||
return
|
||||
|
||||
self.aRunActionS.setVisible(not self.bool_action_server)
|
||||
self.aResetActionS.setVisible(self.bool_action_server)
|
||||
self.aStopActionS.setVisible(self.bool_action_server)
|
||||
self.aRunActionS.setVisible(not self.bool_action_thread_running)
|
||||
self.aResetActionS.setVisible(self.bool_action_thread_running)
|
||||
self.aStopActionS.setVisible(self.bool_action_thread_running)
|
||||
|
||||
if self.bool_timer_event is False:
|
||||
self.start_timer_thread()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue