def test_create_shot(bootstrapped_project, cwd_finding_dory): shot_code = "shot050" # ACT and create the asset ktrack_command.create("shot", shot_code) # now check everything is correct # check asset was created in database assert project_has_shot_with_code(bootstrapped_project["id"], shot_code) # check asset folder exists on disk shots_folder = os.path.join(FINDING_DORY_PATH, "Shots", shot_code) assert os.path.exists(shots_folder) # make sure we can get context from this path correctly context = path_cache_manager.context_from_path(shots_folder) assert context # make sure context project is correct assert context.project["id"] == bootstrapped_project["id"] assert context.entity # make sure context entity is correct kt = ktrack_api.get_ktrack() assert kt.find_one("shot", context.entity["id"])["code"] == "shot050"
def task_preset(): # get context path = os.getcwd() context = kttk.path_cache_manager.context_from_path(path) # make sure path was registered and we have a context if not context: print_result("No Context registered for path {}".format(path)) return # make sure context has project and entity assert context.project and context.entity # get task presets for entity presets = kttk.get_task_presets(context.entity["type"]) # now create presets kt = ktrack_api.get_ktrack() for preset in presets: logger.info( "Creating task {} of step {}".format(preset["name"], preset["step"]) ) task = kt.create( "task", { "project": context.project, "entity": context.entity, "name": preset["name"], "step": preset["step"], }, ) kttk.init_entity(task["type"], task["id"])
def test_create_asset(bootstrapped_project, cwd_finding_dory): asset_name = "Nemo" # ACT and create the asset ktrack_command.create("asset", asset_name, asset_type="character") # now check everything is correct # check asset was created in database assert project_has_asset_with_code(bootstrapped_project["id"], asset_name) # check asset folder exists on disk asset_folder = os.path.join(FINDING_DORY_PATH, "Assets", "character", "Nemo") assert os.path.exists(asset_folder) # make sure we can get context from this path correctly context = path_cache_manager.context_from_path(asset_folder) assert context # make sure context project is correct assert context.project["id"] == bootstrapped_project["id"] assert context.entity # make sure context entity is correct kt = ktrack_api.get_ktrack() assert kt.find_one("asset", context.entity["id"])["code"] == "Nemo"
def change_file(self, new_file): # type: (dict) -> None """ Changes context workfile to new file. Called when open or save_as change the DCC workfile. Will NOT load the file into the DCC!!! :param new_file: workfile which will be opened in the DCC :return: """ # extract contest from the workfile # load full workfile kt = ktrack_api.get_ktrack() new_file = kt.find_one("workfile", new_file["id"]) project = new_file["project"] task = kt.find_one("task", new_file["entity"]["id"]) step = task["step"] entity = task["entity"] # entity here means shot / asset... # todo what to do with user? usermanager.restore_user() can lead to issues in travis ci self.context = Context( project=project, entity=entity, step=step, task=task, workfile=new_file, user=None, ) # todo add context changed signal / callback
def show(entity_type, link_entity_type=None, link_entity_id=None): # type: (str, str, KtrackIdType) -> None # make sure entity type is lowercase entity_type = entity_type.lower() entities = None kt = ktrack_api.get_ktrack() try: # if we have a link_entity_type and link_entity_id, we use a filter if link_entity_type and link_entity_id: # make sure link_entity_type is also lowercase link_entity_type = link_entity_type.lower() entities = kt.find( entity_type, [["link", "is", {"type": link_entity_type, "id": link_entity_id}]], ) # otherwise we get all entities of this type else: entities = kt.find(entity_type, []) except EntityMissing: print_result("Entity type {} does not exist".format(entity_type)) return # make sure we got at least one entits got_entities = entities is not None and len(entities) > 0 if got_entities: # generate table entities = sorted(entities, key=get_name_or_code) # get all dict keys from first entity and sort them first_entity = entities[0] headers = ["id", get_name_or_code(first_entity), "created_at", "created_by"] table = [] for entity in entities: entry = ( entity["id"], get_name_or_code(entity), entity["created_at"], entity["created_by"], ) table.append(entry) print_result(tabulate(table, headers=headers)) else: print_result("No entities of type {} found..".format(entity_type))
def _create_workfile_from(self, context, workfile, comment=""): """ Creates a new workfile based on given workfile. Will increase version number. Workfile and context have to match for project etc. :param context: :return: the new workfile """ # initial version number is 1 version_number = (workfile["version_number"] + 1 if workfile["version_number"] else 1) # get template for file name workfile_file_name_template = template_manager.get_route_template( "workfile_file_name") tokens = context.get_avaible_tokens() tokens["dcc_extension"] = self._engine.file_extension tokens["dcc_name"] = self._engine.name tokens["version"] = version_number # format template for file name workfile_file_name = template_manager.format_template( workfile_file_name_template, tokens) # get and format template for workfile folder workfile_location_template = template_manager.get_route_template( "dcc_scenes_location_{}_{}".format(context.entity["type"], self._engine.name.lower())) workfile_location = template_manager.format_template( workfile_location_template, tokens) # combine location and name to workfile path path = os.path.join(workfile_location, workfile_file_name) workfile_data = {} workfile_data["project"] = context.project workfile_data["entity"] = context.task workfile_data["version_number"] = version_number workfile_data["comment"] = comment if len(workfile.keys()) > 1: workfile_data["created_from"] = workfile workfile_data["name"] = workfile_file_name workfile_data["path"] = path # create new workfile kt = ktrack_api.get_ktrack() new_workfile = kt.create( "workfile", workfile_data) # todo register path for workfile # return newly created workfile return new_workfile
def project_has_task_with_name(project_id, task_name): kt = ktrack_api.get_ktrack() tasks = kt.find("task", [["project", "is", { "type": "project", "id": project_id }]]) for task in tasks: if task["name"] == task_name: return True return False
def test_task_preset_asset(bootstrapped_project, mock_print_result, cwd_hank): # old task count kt = ktrack_api.get_ktrack() old_task_count = len( kt.find("task", [["project", "is", bootstrapped_project]])) # apply task preset ktrack_command.task_preset() # validate new_task_count = len( kt.find("task", [["project", "is", bootstrapped_project]])) assert new_task_count == (old_task_count + 2)
def project_has_shot_with_code(project_id, shot_code): kt = ktrack_api.get_ktrack() assets = kt.find("shot", [["project", "is", { "type": "project", "id": project_id }]]) for asset in assets: if asset["code"] == shot_code: return True return False
def __init__(self, project=None, entity=None, step=None, task=None, workfile=None, user=None): kt = ktrack_api.get_ktrack() # project self._validate_entity_dict(project) if project: self._project = kt.find_one("project", project["id"]) else: self._project = None # entity self._validate_entity_dict(entity) if entity: self._entity = kt.find_one(entity["type"], entity["id"]) else: self._entity = None # step self._validate_step(step) self._step = step # task self._validate_entity_dict(task) if task: self._task = kt.find_one("task", task["id"]) else: self._task = None # workfile self._validate_entity_dict(workfile) if workfile: self._workfile = kt.find_one("workfile", workfile["id"]) else: self._workfile = None # user self._validate_entity_dict(user) if user: self._user = kt.find_one("user", user["id"]) else: self._user = None
def _get_highest_workfile(self, context): """ Checks for exisitng workfiles for context.task. If there is one, will return the one with highest version_number. If not, will return None :param context: :return: workfile with highest version number if exists, else None """ kt = ktrack_api.get_ktrack() # get all workfiles for task workfiles = kt.find("workfile", [["entity", "is", context.task]]) # no tasks exist, so return None if len(workfiles) == 0: return None # else search for highest version number, return this one return max(workfiles, key=lambda workfile: workfile["version_number"])
def get_avaible_tokens(self): # type: () -> dict avaible_tokens = {} kt = ktrack_api.get_ktrack() if self.project: project = kt.find_one("project", self.project["id"]) avaible_tokens["project_name"] = project["name"] avaible_tokens["project_year"] = project["created_at"].year # make sure to query all fields from ktrack, because we might only have id and type if self.entity: entity = kt.find_one(self.entity["type"], self.entity["id"]) avaible_tokens["code"] = entity["code"] if entity["type"] == "asset": avaible_tokens["asset_type"] = entity["asset_type"] if self.step: avaible_tokens["step"] = self.step if self.task: task = kt.find_one("task", self.task["id"]) avaible_tokens["task_name"] = task["name"] if self.workfile: workfile = kt.find_one("workfile", self.workfile["id"]) avaible_tokens["work_file_name"] = workfile["name"] avaible_tokens["work_file_path"] = workfile["path"] avaible_tokens["work_file_comment"] = workfile["comment"] avaible_tokens["version"] = "v{}".format("{}".format( workfile["version_number"]).zfill(3)) if self.user: user = kt.find_one("user", self.user["id"]) avaible_tokens["user_name"] = user["name"] avaible_tokens["project_root"] = template_manager.get_route_template( "project_root") return avaible_tokens
def context_from_path(path): # type: (str) -> kttk.context.Context """ Extracts context by path from database :param path: path for context :return: stored context if exists, else None """ # make path beautifull path = __good_path(path) kt = ktrack_api.get_ktrack() context_dicts = kt.find("path_entry", [["path", "is", path]]) context_found = len(context_dicts) > 0 if context_found: context = Context.from_dict(context_dicts[0]["context"]) return context else: return None
def unregister_path(path): # type: (str) -> None """ Unregisters a path from database :param path: :return: True if path was registered in database and deleted, False otherwise """ # make path beautifull path = __good_path(path) kt = ktrack_api.get_ktrack() path_entries = kt.find("path_entry", [["path", "is", path]]) entry_found = len(path_entries) > 0 if entry_found: for path_entry in path_entries: kt.delete("path_entry", path_entry["id"]) return True else: return False
def register_path(path, context): # type: (str, Context) -> dict """ registeres given context for given path in database, so we can later get the context back from the path :param path: path to register, None or "" not allowed!!! :param context: context to register :return: newly created path entry from database """ # check if path is valid if not is_valid_path(path): raise ValueError(path) # make path beautifull path = __good_path(path) kt = ktrack_api.get_ktrack() path_entry_data = {} path_entry_data["path"] = path path_entry_data["context"] = context.as_dict( ) # todo remove user information return kt.create("path_entry", path_entry_data)
def find_one(entity_type, entity_id): # type: (str, KtrackIdType) -> None """ Finds the given entity in the database and pretty prints it :param entity_type: type of the entity :param entity_id: id of the entity :return: None """ kt = ktrack_api.get_ktrack() entity = None try: entity = kt.find_one(entity_type, entity_id) except EntityMissing: print_result("Entity type '{}' does not exist".format(entity_type)) return if entity: pp = pprint.PrettyPrinter(indent=4) pp.pprint(entity) else: print_result( 'Entity of type "{}" and id "{}" not found..'.format(entity_type, entity_id) )
def create_user(): # type: () -> dict """ Queries first and second name from user, creates a new user. Does NOT save it to disk, as we may create a user for another PC :return: user_information of newly created user """ # todo do command line version and PySide Gui Version # ask for first name first_name_text = "Enter first name: " if six.PY3: first_name = input(first_name_text) else: first_name = raw_input(first_name_text) # ask for second name second_name_text = "Enter second name: " if six.PY3: second_name = input(second_name_text) else: second_name = raw_input(second_name_text) # genereate user name username = generate_user_name(first_name, second_name) # todo check if user name if unique # create user in db kt = ktrack_api.get_ktrack() user = kt.create( "user", {"name": username, "first_name": first_name, "second_name": second_name} ) user_information = {"type": "user", "id": user["id"]} # return user information return user_information
def bootstrap_project(): # type: () -> Tuple[Dict, Dict] """ Bootstraps a project. Project can be used for testing or demonstration purposes :return: the bootstrapped project and a dict with information about the created entites, example: { 'project': {project_dict [...]}, 'Hank': {asset_dict [...]}, 'Hank_modelling': {task_dict [...]} [...] } ['shot040_lsr', 'shot020_anim', 'shot040_anim', 'shot030_comp', 'Fluke_and_Rudder_modelling', 'Dory_lookdev', 'Dory_modelling', 'shot010_comp', 'shot030', 'Hank', 'project', 'shot010_anim', 'shot030_anim', 'Dory', 'Hank_lookdev', 'Hank_modelling', 'shot010_lsr', 'shot020', 'shot040', 'Fluke_and_Rudder_lookdev', 'shot030_lsr', 'Fluke_and_Rudder', 'shot020_comp', 'shot010', 'shot040_comp', 'shot020_lsr'] """ project_data = {} kt = ktrack_api.get_ktrack() # sanitize project name project_name = name_sanitizer.sanitize_name(data["project_name"]) # create project project = kt.create("project", {"name": project_name}) project_data["project"] = project # init project kttk.init_entity(project["type"], project["id"]) entity_types = ["asset", "shot"] for entity_type in entity_types: entity_presets = task_presets_manager.get_task_presets(entity_type) for entity in data["{}_names".format(entity_type)]: # sanitize name entity_name = name_sanitizer.sanitize_name(entity) # create the entity entity_data = {"code": entity_name, "project": project} # make sure each asset has an entity type if entity_type == "asset": entity_data["asset_type"] = "character" entity = kt.create(entity_type, entity_data) project_data[entity_name] = entity # init asset kttk.init_entity(entity["type"], entity["id"]) # apply task preset for preset in entity_presets: logger.info("Creating task {} of step {}".format( preset["name"], preset["step"])) task = kt.create( "task", { "project": project, "entity": entity, "name": preset["name"], "step": preset["step"], }, ) kttk.init_entity(task["type"], task["id"]) project_data["{}_{}".format(entity_name, preset["name"])] = task return project, project_data
def remove_bootstrapped_project(project_id): # type: (KtrackIdType) -> None """ Removes the bootstrapped project. Removes entites from database, deletes folders and unregisters folders in database :return: """ # get all entities kt = ktrack_api.get_ktrack() entities = [] # get project project = kt.find_one("project", project_id) entities.append(project) # get shots shots = kt.find("shot", [["project", "is", project]]) entities.extend(shots) # get assets assets = kt.find("asset", [["project", "is", project]]) entities.extend(assets) # get asset tasks asset_tasks = [] for asset in assets: asset_tasks.extend(kt.find("task", [["entity", "is", asset]])) entities.extend(asset_tasks) # get shot tasks shot_tasks = [] for shot in shots: shot_tasks.extend(kt.find("task", [["entity", "is", shot]])) entities.extend(shot_tasks) # workfiles workfiles = kt.find( "workfile", [["project", "is", { "type": "project", "id": project_id }]]) entities.extend(workfiles) # walk project folder and unregister each path project_folder_template = template_manager.get_route_template( "project_folder") project_root_template = template_manager.get_route_template("project_root") project_folder = template_manager.format_template( project_folder_template, { "project_name": project["name"], "project_root": project_root_template, "project_year": project["created_at"].year, }, ) logger.info("Unregister paths...") for root, dirs, files in os.walk(project_folder): path = root if kttk.path_cache_manager.unregister_path(path): logger.info("Unregistered path {}".format(path)) # delete all entities logger.info("Deleting entities...") for entity in entities: kt.delete(entity["type"], entity["id"]) logger.info("Deleted {} with id {}".format(entity["type"], entity["id"])) # delete project folder and subfolders logger.info("Remove project folder {}".format(project_folder)) shutil.rmtree(project_folder)
def create(entity_type, entity_name, project_id=None, asset_type=None, task_step=None): """ Creates a new entity if given type in database and initialises it in disk kttk.init_entity :param entity_type: type of the new entity to create :param entity_name: name the new entity will get, will be used for code if entity has no name :param project_id: optional, will link project entity to project with this id :param asset_type: type a newly created asset will get :param task_step: step a newly created task will get :return: None """ # init: # if project: # checken ob es namen gibt, dann project in db erstellen und dann initialisieren # else: # checken ob aktueller ordner einen context hat, wenn ja, dann context holen, wenn nicht, abbruch # neuen entity erstellen und mit context richtig verlinken, dann initialisieren # todo add some more documentation logger.info( "creation entity of type {} with name {} for project {}".format( entity_type, entity_name, project_id ) ) logger.info("init cmd") logger.info("Connecting to database..") kt = ktrack_api.get_ktrack() if entity_type == "project": logger.info("initialise project") logger.info("create project in database..") project_data = {} project_data["name"] = entity_name project = kt.create("project", project_data) logger.info( "Created project {} with id {}".format(project["name"], project["id"]) ) logger.info("initialise project on disk..") kttk.init_entity("project", project["id"]) logger.info( "{entity_type} with id {entity_id} initialised. Done.".format( entity_type="project", entity_id=project["id"] ) ) else: is_asset = entity_type == "asset" is_task = entity_type == "task" if is_asset: if not asset_type: print_result("no asset type") # todo hints for asset_type return if is_task: if not task_step: # todo hints for task_step print_result("no task step") return logger.info("initialise {}".format(entity_type)) current_location = os.getcwd() # current_location = r"M:\Projekte\2018\my_awesome_project" logger.info("Restore context from location {}..".format(current_location)) context = kttk.context_from_path(current_location) if not context: print_result("No context provided for path") return logger.info("Context is {context}".format(context=context)) logger.info("create {} in database..".format(entity_type)) entity_data = {} entity_data["code"] = entity_name entity_data["project"] = context.project if is_asset: entity_data["asset_type"] = asset_type if is_task: entity_data["name"] = entity_name entity_data["step"] = task_step if not context.entity: print_result("No entity provided for task") return entity_data["entity"] = context.entity entity_data["assigned"] = { "type": "user", "id": "5af33abd6e87ff056014967a", } # todo dont hardcode user id, use kttk.restore_user() entity = kt.create(entity_type, entity_data) logger.info( "created {entity_type} {entity_name} with id {entity_id}.".format( entity_type=entity["type"], entity_name=entity.get("code") if entity.get("code") else entity["name"], entity_id=entity["id"], ) ) logger.info("initialise entity on disk..") kttk.init_entity(entity_type, entity["id"]) logger.info( "{entity_type} with id {entity_id} initialised. Done.".format( entity_type=entity["type"], entity_id=entity["id"] ) )
import ktrack_api # todo remove kt = ktrack_api.get_ktrack() def main(): paths = kt.find("path_entry", [["path", "is", ""]]) for path in paths: kt.delete("path_entry", path["id"]) if __name__ == "__main__": main()
def __init__(self): self._kt = ktrack_api.get_ktrack()