Beispiel #1
0
    def launch(self, session, event):
        # load shell scripts presets
        presets = config.get_presets()['ftrack'].get("user_assigment_event")
        if not presets:
            return
        for entity in event.get('data', {}).get('entities', []):
            if entity.get('entity_type') != 'Appointment':
                continue

            task, user = self._get_task_and_user(session,
                                                 entity.get('action'),
                                                 entity.get('changes'))

            if not task or not user:
                self.log.error(
                    'Task or User was not found.')
                continue

            data = self._get_template_data(task)
            # format directories to pass to shell script
            anatomy = Anatomy(data["project"]["name"])
            # formatting work dir is easiest part as we can use whole path
            work_dir = anatomy.format(data)['avalon']['work']
            # we also need publish but not whole
            publish = anatomy.format_all(data)['partial']['avalon']['publish']
            # now find path to {asset}
            m = re.search("(^.+?{})".format(data['asset']),
                          publish)

            if not m:
                msg = 'Cannot get part of publish path {}'.format(publish)
                self.log.error(msg)
                return {
                    'success': False,
                    'message': msg
                }
            publish_dir = m.group(1)

            for script in presets.get(entity.get('action')):
                self.log.info(
                    '[{}] : running script for user {}'.format(
                        entity.get('action'), user["username"]))
                self._run_script(script, [user["username"],
                                          work_dir, publish_dir])

        return True
Beispiel #2
0
def test_anatomy(anatomy_file, monkeypatch):
    # TODO add test for `missing_keys` and `invalid_types`
    anatomy_file = os.path.join(anatomy_file, "repos", "pype-config")
    print(anatomy_file)

    monkeypatch.setitem(os.environ, 'PYPE_CONFIG', anatomy_file)
    anatomy = Anatomy()

    filled_all = anatomy.format_all(valid_data)
    filled = anatomy.format(valid_data)

    assert filled_all['work']['file'] == "PRJ_BOB_MODELING_v001_iAmComment.ABC"
    assert filled_all['work']['file2'] == "PRJ_BOB_MODELING_v001.ABC"
    assert filled_all['work'][
        'noDictKey'] == "PRJ_{asset[name]}_MODELING_v001.ABC"

    assert filled['work']['file'] == "PRJ_BOB_MODELING_v001_iAmComment.ABC"
    assert filled['work']['file2'] == "PRJ_BOB_MODELING_v001.ABC"
    assert filled['work'][
        'multiple_optional'] == "PRJ/BOB/asset/characters_v001.ABC"
Beispiel #3
0
    def launch(self, session, entities, event):
        if "values" not in event["data"]:
            return

        self.report_items = collections.defaultdict(list)

        values = event["data"]["values"]
        skipped = values.pop("__skipped__")
        if skipped:
            return None

        component_names = []
        location_path = values.pop("__location_path__")
        anatomy_name = values.pop("__new_anatomies__")
        project_name = values.pop("__project_name__")

        for key, value in values.items():
            if value is True:
                component_names.append(key)

        if not component_names:
            return {
                "success": True,
                "message": "Not selected components to deliver."
            }

        location_path = location_path.strip()
        if location_path:
            location_path = os.path.normpath(location_path)
            if not os.path.exists(location_path):
                return {
                    "success":
                    False,
                    "message": ("Entered location path does not exists. \"{}\""
                                ).format(location_path)
                }

        self.db_con.install()
        self.db_con.Session["AVALON_PROJECT"] = project_name

        repres_to_deliver = []
        for entity in entities:
            asset = entity["asset"]
            subset_name = asset["name"]
            version = entity["version"]

            parent = asset["parent"]
            parent_mongo_id = parent["custom_attributes"].get(CustAttrIdKey)
            if parent_mongo_id:
                parent_mongo_id = ObjectId(parent_mongo_id)
            else:
                asset_ent = self.db_con.find_one({
                    "type": "asset",
                    "data.ftrackId": parent["id"]
                })
                if not asset_ent:
                    ent_path = "/".join(
                        [ent["name"] for ent in parent["link"]])
                    msg = "Not synchronized entities to avalon"
                    self.report_items[msg].append(ent_path)
                    self.log.warning("{} <{}>".format(msg, ent_path))
                    continue

                parent_mongo_id = asset_ent["_id"]

            subset_ent = self.db_con.find_one({
                "type": "subset",
                "parent": parent_mongo_id,
                "name": subset_name
            })

            version_ent = self.db_con.find_one({
                "type": "version",
                "name": version,
                "parent": subset_ent["_id"]
            })

            repre_ents = self.db_con.find({
                "type": "representation",
                "parent": version_ent["_id"]
            })

            repres_by_name = {}
            for repre in repre_ents:
                repre_name = repre["name"]
                repres_by_name[repre_name] = repre

            for component in entity["components"]:
                comp_name = component["name"]
                if comp_name not in component_names:
                    continue

                repre = repres_by_name.get(comp_name)
                repres_to_deliver.append(repre)

        if not location_path:
            location_path = os.environ.get("AVALON_PROJECTS") or ""

        print(location_path)

        anatomy = Anatomy(project_name)
        for repre in repres_to_deliver:
            # Get destination repre path
            anatomy_data = copy.deepcopy(repre["context"])
            anatomy_data["root"] = location_path

            anatomy_filled = anatomy.format_all(anatomy_data)
            test_path = anatomy_filled["delivery"][anatomy_name]

            if not test_path.solved:
                msg = ("Missing keys in Representation's context"
                       " for anatomy template \"{}\".").format(anatomy_name)

                if test_path.missing_keys:
                    keys = ", ".join(test_path.missing_keys)
                    sub_msg = (
                        "Representation: {}<br>- Missing keys: \"{}\"<br>"
                    ).format(str(repre["_id"]), keys)

                if test_path.invalid_types:
                    items = []
                    for key, value in test_path.invalid_types.items():
                        items.append("\"{}\" {}".format(key, str(value)))

                    keys = ", ".join(items)
                    sub_msg = ("Representation: {}<br>"
                               "- Invalid value DataType: \"{}\"<br>").format(
                                   str(repre["_id"]), keys)

                self.report_items[msg].append(sub_msg)
                self.log.warning(
                    "{} Representation: \"{}\" Filled: <{}>".format(
                        msg, str(repre["_id"]), str(result)))
                continue

            # Get source repre path
            frame = repre['context'].get('frame')

            if frame:
                repre["context"]["frame"] = len(str(frame)) * "#"

            repre_path = self.path_from_represenation(repre)
            # TODO add backup solution where root of path from component
            # is repalced with AVALON_PROJECTS root
            if not frame:
                self.process_single_file(repre_path, anatomy, anatomy_name,
                                         anatomy_data)

            else:
                self.process_sequence(repre_path, anatomy, anatomy_name,
                                      anatomy_data)

        self.db_con.uninstall()

        return self.report()
    def launch(self, session, entities, event):
        # DEBUG LINE
        # root_path = r"C:\Users\jakub.trllo\Desktop\Tests\ftrack_thumbnails"

        user = session.query(
            "User where username is '{0}'".format(session.api_user)
        ).one()
        action_job = session.create("Job", {
            "user": user,
            "status": "running",
            "data": json.dumps({
                "description": "Storing thumbnails to avalon."
            })
        })
        session.commit()

        project = self.get_project_from_entity(entities[0])
        project_name = project["full_name"]
        anatomy = Anatomy(project_name)

        if "publish" not in anatomy.templates:
            msg = "Anatomy does not have set publish key!"

            action_job["status"] = "failed"
            session.commit()

            self.log.warning(msg)

            return {
                "success": False,
                "message": msg
            }

        if "thumbnail" not in anatomy.templates["publish"]:
            msg = (
                "There is not set \"thumbnail\""
                " template in Antomy for project \"{}\""
            ).format(project_name)

            action_job["status"] = "failed"
            session.commit()

            self.log.warning(msg)

            return {
                "success": False,
                "message": msg
            }

        thumbnail_roots = os.environ.get(self.thumbnail_key)
        if (
            "{thumbnail_root}" in anatomy.templates["publish"]["thumbnail"]
            and not thumbnail_roots
        ):
            msg = "`{}` environment is not set".format(self.thumbnail_key)

            action_job["status"] = "failed"
            session.commit()

            self.log.warning(msg)

            return {
                "success": False,
                "message": msg
            }

        existing_thumbnail_root = None
        for path in thumbnail_roots.split(os.pathsep):
            if os.path.exists(path):
                existing_thumbnail_root = path
                break

        if existing_thumbnail_root is None:
            msg = (
                "Can't access paths, set in `{}` ({})"
            ).format(self.thumbnail_key, thumbnail_roots)

            action_job["status"] = "failed"
            session.commit()

            self.log.warning(msg)

            return {
                "success": False,
                "message": msg
            }

        example_template_data = {
            "_id": "ID",
            "thumbnail_root": "THUBMNAIL_ROOT",
            "thumbnail_type": "THUMBNAIL_TYPE",
            "ext": ".EXT",
            "project": {
                "name": "PROJECT_NAME",
                "code": "PROJECT_CODE"
            },
            "asset": "ASSET_NAME",
            "subset": "SUBSET_NAME",
            "version": "VERSION_NAME",
            "hierarchy": "HIERARCHY"
        }
        tmp_filled = anatomy.format_all(example_template_data)
        thumbnail_result = tmp_filled["publish"]["thumbnail"]
        if not thumbnail_result.solved:
            missing_keys = thumbnail_result.missing_keys
            invalid_types = thumbnail_result.invalid_types
            submsg = ""
            if missing_keys:
                submsg += "Missing keys: {}".format(", ".join(
                    ["\"{}\"".format(key) for key in missing_keys]
                ))

            if invalid_types:
                items = []
                for key, value in invalid_types.items():
                    items.append("{}{}".format(str(key), str(value)))
                submsg += "Invalid types: {}".format(", ".join(items))

            msg = (
                "Thumbnail Anatomy template expects more keys than action"
                " can offer. {}"
            ).format(submsg)

            action_job["status"] = "failed"
            session.commit()

            self.log.warning(msg)

            return {
                "success": False,
                "message": msg
            }

        thumbnail_template = anatomy.templates["publish"]["thumbnail"]

        self.db_con.install()

        for entity in entities:
            # Skip if entity is not AssetVersion (never should happend, but..)
            if entity.entity_type.lower() != "assetversion":
                continue

            # Skip if AssetVersion don't have thumbnail
            thumbnail_ent = entity["thumbnail"]
            if thumbnail_ent is None:
                self.log.debug((
                    "Skipping. AssetVersion don't "
                    "have set thumbnail. {}"
                ).format(entity["id"]))
                continue

            avalon_ents_result = self.get_avalon_entities_for_assetversion(
                entity, self.db_con
            )
            version_full_path = (
                "Asset: \"{project_name}/{asset_path}\""
                " | Subset: \"{subset_name}\""
                " | Version: \"{version_name}\""
            ).format(**avalon_ents_result)

            version = avalon_ents_result["version"]
            if not version:
                self.log.warning((
                    "AssetVersion does not have version in avalon. {}"
                ).format(version_full_path))
                continue

            thumbnail_id = version["data"].get("thumbnail_id")
            if thumbnail_id:
                self.log.info((
                    "AssetVersion skipped, already has thubmanil set. {}"
                ).format(version_full_path))
                continue

            # Get thumbnail extension
            file_ext = thumbnail_ent["file_type"]
            if not file_ext.startswith("."):
                file_ext = ".{}".format(file_ext)

            avalon_project = avalon_ents_result["project"]
            avalon_asset = avalon_ents_result["asset"]
            hierarchy = ""
            parents = avalon_asset["data"].get("parents") or []
            if parents:
                hierarchy = "/".join(parents)

            # Prepare anatomy template fill data
            # 1. Create new id for thumbnail entity
            thumbnail_id = ObjectId()

            template_data = {
                "_id": str(thumbnail_id),
                "thumbnail_root": existing_thumbnail_root,
                "thumbnail_type": "thumbnail",
                "ext": file_ext,
                "project": {
                    "name": avalon_project["name"],
                    "code": avalon_project["data"].get("code")
                },
                "asset": avalon_ents_result["asset_name"],
                "subset": avalon_ents_result["subset_name"],
                "version": avalon_ents_result["version_name"],
                "hierarchy": hierarchy
            }

            anatomy_filled = anatomy.format(template_data)
            thumbnail_path = anatomy_filled["publish"]["thumbnail"]
            thumbnail_path = thumbnail_path.replace("..", ".")
            thumbnail_path = os.path.normpath(thumbnail_path)

            downloaded = False
            for loc in (thumbnail_ent.get("component_locations") or []):
                res_id = loc.get("resource_identifier")
                if not res_id:
                    continue

                thubmnail_url = self.get_thumbnail_url(res_id)
                if self.download_file(thubmnail_url, thumbnail_path):
                    downloaded = True
                    break

            if not downloaded:
                self.log.warning(
                    "Could not download thumbnail for {}".format(
                        version_full_path
                    )
                )
                continue

            # Clean template data from keys that are dynamic
            template_data.pop("_id")
            template_data.pop("thumbnail_root")

            thumbnail_entity = {
                "_id": thumbnail_id,
                "type": "thumbnail",
                "schema": "pype:thumbnail-1.0",
                "data": {
                    "template": thumbnail_template,
                    "template_data": template_data
                }
            }

            # Create thumbnail entity
            self.db_con.insert_one(thumbnail_entity)
            self.log.debug(
                "Creating entity in database {}".format(str(thumbnail_entity))
            )

            # Set thumbnail id for version
            self.db_con.update_one(
                {"_id": version["_id"]},
                {"$set": {"data.thumbnail_id": thumbnail_id}}
            )

            self.db_con.update_one(
                {"_id": avalon_asset["_id"]},
                {"$set": {"data.thumbnail_id": thumbnail_id}}
            )

        action_job["status"] = "done"
        session.commit()

        return True