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"
Exemple #4
0
    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))
Exemple #6
0
    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
Exemple #10
0
    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
Exemple #11
0
    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"])
Exemple #12
0
    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
Exemple #13
0
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
Exemple #14
0
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
Exemple #15
0
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()