def launch(self, session, event):
        ca_mongoid = lib.get_ca_mongoid()
        # If mongo_id textfield has changed: RETURN!
        # - infinite loop
        for ent in event['data']['entities']:
            if ent.get('keys') is not None:
                if ca_mongoid in ent['keys']:
                    return

        entities = self._get_entities(session, event, self.ignore_entityType)
        ft_project = None
        # get project
        for entity in entities:
            try:
                base_proj = entity['link'][0]
            except Exception:
                continue
            ft_project = session.get(base_proj['type'], base_proj['id'])
            break

        # check if project is set to auto-sync
        if (ft_project is None
                or 'avalon_auto_sync' not in ft_project['custom_attributes'] or
                ft_project['custom_attributes']['avalon_auto_sync'] is False):
            return

        # check if project have Custom Attribute 'avalon_mongo_id'
        if ca_mongoid not in ft_project['custom_attributes']:
            message = (
                "Custom attribute '{}' for 'Project' is not created"
                " or don't have set permissions for API").format(ca_mongoid)
            self.log.warning(message)
            self.show_message(event, message, False)
            return

        # get avalon project if possible
        import_entities = []

        custom_attributes = lib.get_avalon_attr(session)

        avalon_project = lib.get_avalon_project(ft_project)
        if avalon_project is None:
            import_entities.append(ft_project)

        for entity in entities:
            if entity.entity_type.lower() in ['task']:
                entity = entity['parent']

            if 'custom_attributes' not in entity:
                continue
            if ca_mongoid not in entity['custom_attributes']:

                message = ("Custom attribute '{}' for '{}' is not created"
                           " or don't have set permissions for API").format(
                               ca_mongoid, entity.entity_type)

                self.log.warning(message)
                self.show_message(event, message, False)
                return

            if entity not in import_entities:
                import_entities.append(entity)

        if len(import_entities) < 1:
            return

        try:
            for entity in import_entities:
                result = lib.import_to_avalon(
                    session=session,
                    entity=entity,
                    ft_project=ft_project,
                    av_project=avalon_project,
                    custom_attributes=custom_attributes)
                if 'errors' in result and len(result['errors']) > 0:
                    session.commit()
                    lib.show_errors(self, event, result['errors'])

                    return

                if avalon_project is None:
                    if 'project' in result:
                        avalon_project = result['project']

        except Exception as e:
            session.reset()  # reset session to clear it

            message = str(e)
            title = 'Hey You! Unknown Error has been raised! (*look below*)'
            ftrack_message = ('SyncToAvalon event ended with unexpected error'
                              ' please check log file or contact Administrator'
                              ' for more information.')
            items = [{
                'type': 'label',
                'value': '# Fatal Error'
            }, {
                'type': 'label',
                'value': '<p>{}</p>'.format(ftrack_message)
            }]
            self.show_interface(items, title, event=event)
            self.log.error('Fatal error during sync: {}'.format(message),
                           exc_info=True)

        return
示例#2
0
    def interface(self, session, entities, event):
        if event['data'].get('values', {}):
            return

        # Inform user that this may take a while
        self.show_message(event, "Preparing data... Please wait", True)

        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")
        attributes_to_set = {}
        for attr in hier_cust_attrs:
            key = attr["key"]
            attributes_to_set[key] = {
                "label": attr["label"],
                "object": attr,
                "default": project_defaults.get(key)
            }

        for attr in cust_attrs:
            if attr["entity_type"].lower() != "show":
                continue
            key = attr["key"]
            attributes_to_set[key] = {
                "label": attr["label"],
                "object": attr,
                "default": project_defaults.get(key)
            }

        # Sort by label
        attributes_to_set = dict(
            sorted(attributes_to_set.items(), key=lambda x: x[1]["label"]))
        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": "<h3>Want to create basic Folder Structure?</h3>"
        })

        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": "<h3>Set basic Attributes:</h3>"
        })

        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"]

            # initial item definition
            item = {"name": key, "label": in_data["label"]}

            # cust attr type - may have different visualization
            type_name = attr["type"]["name"].lower()
            easy_types = ["text", "boolean", "date", "number"]

            easy_type = False
            if type_name in easy_types:
                easy_type = True

            elif type_name == "enumerator":

                attr_config = json.loads(attr["config"])
                attr_config_data = json.loads(attr_config["data"])

                if attr_config["multiSelect"] is True:
                    multiselect_enumerators.append(item_splitter)

                    multiselect_enumerators.append({
                        "type": "label",
                        "value": in_data["label"]
                    })

                    default = in_data["default"]
                    names = []
                    for option in sorted(attr_config_data,
                                         key=lambda x: x["menu"]):
                        name = option["value"]
                        new_name = "__{}__{}".format(key, name)
                        names.append(new_name)
                        item = {
                            "name": new_name,
                            "type": "boolean",
                            "label": "- {}".format(option["menu"])
                        }
                        if default:
                            if (isinstance(default, list)
                                    or isinstance(default, tuple)):
                                if name in default:
                                    item["value"] = True
                            else:
                                if name == default:
                                    item["value"] = True

                        multiselect_enumerators.append(item)

                    multiselect_enumerators.append({
                        "type":
                        "hidden",
                        "name":
                        "__hidden__{}".format(key),
                        "value":
                        json.dumps(names)
                    })
                else:
                    easy_type = True
                    item["data"] = attr_config_data

            else:
                self.log.warning(
                    ("Custom attribute \"{}\" has type \"{}\"."
                     " I don't know how to handle").format(key, type_name))
                items.append({
                    "type":
                    "label",
                    "value": ("!!! Can't handle Custom attritubte type \"{}\""
                              " (key: \"{}\")").format(type_name, key)
                })

            if easy_type:
                item["type"] = type_name

                # default value in interface
                default = in_data["default"]
                if default is not None:
                    item["value"] = default

                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}
示例#3
0
    def launch(self, session, entities, event):
        time_start = time.time()
        message = ""

        # JOB SETTINGS
        userId = event['source']['user']['id']
        user = session.query('User where id is ' + userId).one()

        job = session.create(
            'Job', {
                'user': user,
                'status': 'running',
                'data': json.dumps({'description': 'Sync Ftrack to Avalon.'})
            })
        session.commit()
        try:
            self.log.debug("Preparing entities for synchronization")

            if entities[0].entity_type.lower() == "project":
                ft_project_name = entities[0]["full_name"]
            else:
                ft_project_name = entities[0]["project"]["full_name"]

            project_entities = session.query(
                self.entities_query.format(ft_project_name)).all()

            ft_project = session.query(
                self.project_query.format(ft_project_name)).one()

            entities_by_id = {}
            entities_by_parent = collections.defaultdict(list)

            entities_by_id[ft_project["id"]] = ft_project
            for ent in project_entities:
                entities_by_id[ent["id"]] = ent
                entities_by_parent[ent["parent_id"]].append(ent)

            importable = []
            for ent_info in event["data"]["selection"]:
                ent = entities_by_id[ent_info["entityId"]]
                for link_ent_info in ent["link"]:
                    link_ent = entities_by_id[link_ent_info["id"]]
                    if (ent.entity_type.lower() in self.ignore_entity_types
                            or link_ent in importable):
                        continue

                    importable.append(link_ent)

            def add_children(parent_id):
                ents = entities_by_parent[parent_id]
                for ent in ents:
                    if ent.entity_type.lower() in self.ignore_entity_types:
                        continue

                    if ent not in importable:
                        importable.append(ent)

                    add_children(ent["id"])

            # add children of selection to importable
            for ent_info in event["data"]["selection"]:
                add_children(ent_info["entityId"])

            # Check names: REGEX in schema/duplicates - raise error if found
            all_names = []
            duplicates = []

            for entity in importable:
                lib.avalon_check_name(entity)
                if entity.entity_type.lower() == "project":
                    continue

                if entity['name'] in all_names:
                    duplicates.append("'{}'".format(entity['name']))
                else:
                    all_names.append(entity['name'])

            if len(duplicates) > 0:
                # TODO Show information to user and return False
                raise ValueError("Entity name duplication: {}".format(
                    ", ".join(duplicates)))

            # ----- PROJECT ------
            avalon_project = lib.get_avalon_project(ft_project)
            custom_attributes = lib.get_avalon_attr(session)

            # Import all entities to Avalon DB
            for entity in importable:
                result = lib.import_to_avalon(
                    session=session,
                    entity=entity,
                    ft_project=ft_project,
                    av_project=avalon_project,
                    custom_attributes=custom_attributes)
                # TODO better error handling
                # maybe split into critical, warnings and messages?
                if 'errors' in result and len(result['errors']) > 0:
                    job['status'] = 'failed'
                    session.commit()

                    lib.show_errors(self, event, result['errors'])

                    return {
                        'success': False,
                        'message': "Sync to avalon FAILED"
                    }

                if avalon_project is None:
                    if 'project' in result:
                        avalon_project = result['project']

            job['status'] = 'done'
            session.commit()

        except ValueError as ve:
            # TODO remove this part!!!!
            job['status'] = 'failed'
            session.commit()
            message = str(ve)
            self.log.error('Error during syncToAvalon: {}'.format(message),
                           exc_info=True)

        except Exception as e:
            job['status'] = 'failed'
            session.commit()
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            log_message = "{}/{}/Line: {}".format(exc_type, fname,
                                                  exc_tb.tb_lineno)
            self.log.error('Error during syncToAvalon: {}'.format(log_message),
                           exc_info=True)
            # TODO add traceback to message and show to user
            message = ('Unexpected Error'
                       ' - Please check Log for more information')

        finally:
            if job['status'] in ['queued', 'running']:
                job['status'] = 'failed'

            session.commit()

            time_end = time.time()
            self.log.debug("Synchronization took \"{}\"".format(
                str(time_end - time_start)))

            if job["status"] != "failed":
                self.log.debug("Triggering Sync hierarchical attributes")
                self.trigger_action("sync.hierarchical.attrs", event)

        if len(message) > 0:
            message = "Unable to sync: {}".format(message)
            return {'success': False, 'message': message}

        return {'success': True, 'message': "Synchronization was successfull"}
示例#4
0
    def launch(self, session, entities, event):
        message = ""

        # JOB SETTINGS
        userId = event['source']['user']['id']
        user = session.query('User where id is ' + userId).one()

        job = session.create(
            'Job', {
                'user': user,
                'status': 'running',
                'data': json.dumps({'description': 'Sync Ftrack to Avalon.'})
            })
        session.commit()
        try:
            self.importable = []

            # get from top entity in hierarchy all parent entities
            top_entity = entities[0]['link']
            if len(top_entity) > 1:
                for e in top_entity:
                    parent_entity = session.get(e['type'], e['id'])
                    self.importable.append(parent_entity)

            # get all child entities separately/unique
            for entity in entities:
                self.add_childs_to_importable(entity)

            # Check names: REGEX in schema/duplicates - raise error if found
            all_names = []
            duplicates = []

            for entity in self.importable:
                ftracklib.avalon_check_name(entity)
                if entity['name'] in all_names:
                    duplicates.append("'{}'".format(e['name']))
                else:
                    all_names.append(entity['name'])

            if len(duplicates) > 0:
                raise ValueError("Entity name duplication: {}".format(
                    ", ".join(duplicates)))

            # ----- PROJECT ------
            # store Ftrack project- self.importable[0] must be project entity!!
            ft_project = self.importable[0]
            avalon_project = ftracklib.get_avalon_project(ft_project)
            custom_attributes = ftracklib.get_avalon_attr(session)

            # Import all entities to Avalon DB
            for entity in self.importable:
                result = ftracklib.import_to_avalon(
                    session=session,
                    entity=entity,
                    ft_project=ft_project,
                    av_project=avalon_project,
                    custom_attributes=custom_attributes)

                if 'errors' in result and len(result['errors']) > 0:
                    job['status'] = 'failed'
                    session.commit()

                    ftracklib.show_errors(self, event, result['errors'])

                    return {
                        'success': False,
                        'message': "Sync to avalon FAILED"
                    }

                if avalon_project is None:
                    if 'project' in result:
                        avalon_project = result['project']

            job['status'] = 'done'

        except ValueError as ve:
            job['status'] = 'failed'
            message = str(ve)
            self.log.error('Error during syncToAvalon: {}'.format(message))

        except Exception as e:
            job['status'] = 'failed'
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            log_message = "{}/{}/Line: {}".format(exc_type, fname,
                                                  exc_tb.tb_lineno)
            self.log.error('Error during syncToAvalon: {}'.format(log_message))
            message = ('Unexpected Error'
                       ' - Please check Log for more information')
        finally:
            if job['status'] in ['queued', 'running']:
                job['status'] = 'failed'
            session.commit()

        if len(message) > 0:
            message = "Unable to sync: {}".format(message)
            return {'success': False, 'message': message}

        return {'success': True, 'message': "Synchronization was successfull"}