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