def asset_update_callback(data):
    """Update an asset name when receiving an asset:update event"""
    # Log in to API
    gazu.client.set_host("{0}/api".format(os.environ["GAZU_URL"]))
    gazu.log_in(os.environ["GAZU_USER"], os.environ["GAZU_PASSWD"])

    asset = gazu.asset.get_asset(data["asset_id"])
    project = gazu.project.get_project(asset["project_id"])
    project_name = lib.get_consistent_name(project["name"])

    os.environ["AVALON_PROJECT"] = project_name

    db.uninstall()
    db.install()

    # Get Avalon Asset Id.
    asset_id = lib.get_asset_data(project["id"], data["asset_id"])
    # Get asset Type
    entity_type = gazu.entity.get_entity_type(asset["entity_type_id"])

    # Find the asset in Avalon
    avalon_asset = db.find_one({"_id": db.ObjectId(asset_id), "type": "asset"})

    # Set keep asset name for use in filesystem path renaming.
    old_asset_name = lib.get_consistent_name(avalon_asset["name"])

    # Ensure asset["name"] consistency.
    asset_name = lib.get_consistent_name(asset["name"])

    avalon_asset["name"] = asset_name
    avalon_asset["data"]["label"] = asset["name"]
    avalon_asset["data"]["group"] = entity_type["name"]

    db.replace_one({
        "_id": db.ObjectId(asset_id),
        "type": "asset"
    }, avalon_asset)

    db.uninstall()

    logger.info("Updated Asset \"{0}\" in Project \"{1}\"".format(
        old_asset_name, project["name"]))

    if asset_name != old_asset_name:
        logger.info("Asset renamed from \"{0}\" to \"{1}\"".format(
            old_asset_name, asset_name))

    # If file system path renaming is enabled, rename asset disk
    # filepaths to match.
    if (os.environ["FILESYS_RENAME"]):
        lib.rename_filepath(old_asset_name, asset_name, project_name, "assets")
def task_new_callback(data):
    """
    On receiving a task:new event, add a task to an asset in the
    Avalon mongodb.
    """

    # Log in to API
    gazu.client.set_host("{0}/api".format(os.environ["GAZU_URL"]))
    gazu.log_in(os.environ["GAZU_USER"], os.environ["GAZU_PASSWD"])

    task = gazu.task.get_task(data["task_id"])
    entity = task["entity"]
    project = task["project"]
    task_type = task["task_type"]

    project_name = lib.get_consistent_name(project["name"])
    task_name = lib.get_consistent_name(task_type["name"])

    # Get Avalon Asset Id.
    entity_id = lib.get_asset_data(project["id"], entity["id"])

    os.environ["AVALON_PROJECT"] = project_name

    db.uninstall()
    db.install()

    # Find the asset in Avalon
    avalon_entity = db.find_one({
        "_id": db.ObjectId(entity_id),
        "type": "asset"
    })

    if avalon_entity["data"] is not None:
        if "tasks" in avalon_entity["data"]:
            avalon_entity["data"]["tasks"].append(task_name)
        else:
            avalon_entity["data"]["tasks"] = [task_name]
    else:
        avalon_entity["data"]["tasks"] = [task_name]

    db.replace_one({
        "_id": db.ObjectId(entity_id),
        "type": "asset"
    }, avalon_entity)

    db.uninstall()

    logger.info("Added new \"{2}\" Task to \"{0}\" in Project \"{1}\"".format(
        avalon_entity["name"], project["name"], task_type["name"]))
def project_update_callback(data):
    """Update a project in Avalon when receiving an project:update event"""
    # Log in to API
    gazu.client.set_host("{0}/api".format(os.environ["GAZU_URL"]))
    gazu.log_in(os.environ["GAZU_USER"], os.environ["GAZU_PASSWD"])

    project = gazu.project.get_project(data["project_id"])

    # Get the Avalon project ID from partd
    project_data = lib.get_project_data(data["project_id"])

    os.environ["AVALON_PROJECT"] = project_data["collection"]

    db.uninstall()
    db.install()

    # Find the project in Avalon
    avalon_project = db.find_one({
        "_id": db.ObjectId(project_data["id"]),
        "type": "project"
    })

    # Ensure project["name"] consistency.
    project_name = lib.get_consistent_name(project["name"])
    old_project_name = lib.get_consistent_name(avalon_project["name"])

    # Projects may not have a resolution set
    if project["resolution"]:
        resolution_width = int(int(project["resolution"]) / 9 * 16)
    else:
        resolution_width = None
        project["resolution"] = None

    # Get latest Tasks from Gazu
    tasks = [{
        "name": lib.get_consistent_name(task["name"]),
        "label": task["name"]
    } for task in gazu.task.all_task_types()]

    # Update the Avalon project with new data from Gazu
    avalon_project["name"] = project_name
    avalon_project["data"]["label"] = project["name"]
    avalon_project["data"]["fps"] = int(project["fps"])
    avalon_project["data"]["resolution_width"] = int(resolution_width)
    avalon_project["data"]["resolution_height"] = int(project["resolution"])
    avalon_project["config"]["tasks"] = tasks

    db.replace_one({
        "_id": db.ObjectId(project_data["id"]),
        "type": "project"
    }, avalon_project)

    db.uninstall()

    if old_project_name != project_name:
        logger.info("Updating project name from {0} to {1}".format(
            old_project_name, project_name))
        lib.collection_rename(project_name)

        lib.set_project_data(data["project_id"], avalon_project["_id"],
                             avalon_project["name"])

        # If file system path renaming is enabled, rename project disk
        # filepaths to match.
        if (os.environ["FILESYS_RENAME"]):
            avalon_projects = os.environ["AVALON_PROJECTS"]

            old_folder_name = os.path.join(avalon_projects, old_project_name)

            new_folder_name = os.path.join(avalon_projects, project_name)

            if os.path.exists(old_folder_name):
                if not os.path.exists(new_folder_name):
                    logger.info(
                        "Project name updated, renaming {0} to {1}".format(
                            old_folder_name, new_folder_name))
                    shutil.move(old_folder_name, new_folder_name)
                else:
                    logger.warning(
                        "Project name updated, trying to rename {0} to {1}, but new "
                        "folder already exists. No action taken.".format(
                            old_folder_name, new_folder_name))
            else:
                logger.warning(
                    "Project name updated, but {0} does not exist. No "
                    "action taken.".format(old_folder_name))

    logger.info("Updating Project: \"{0} ({1})\"".format(
        avalon_project["data"]["label"], project_name))
def shot_update_callback(data):
    """Update an shot name when receiving an shot:update event"""
    # Log in to API
    gazu.client.set_host("{0}/api".format(os.environ["GAZU_URL"]))
    gazu.log_in(os.environ["GAZU_USER"], os.environ["GAZU_PASSWD"])

    shot = gazu.shot.get_shot(data["shot_id"])
    project = gazu.project.get_project(shot["project_id"])
    if project["production_type"] == "tvshow":
        # Ensure name consistency.
        project_name = lib.get_consistent_name(project["name"])
        episode_name = lib.get_consistent_name(shot["episode_name"])
        sequence_name = lib.get_consistent_name(shot["sequence_name"])
        shot_name = lib.get_consistent_name(shot["name"])
        visualParent = [
            project_name, "{0}_{1}".format(episode_name, sequence_name)
        ]

        os.environ["AVALON_PROJECT"] = project_name

        db.uninstall()
        db.install()

        # Get Avalon Shot Id.
        shot_id = lib.get_asset_data(project["id"], data["shot_id"])

        # Find the asset in Avalon
        avalon_shot = db.find_one({
            "_id": db.ObjectId(shot_id),
            "type": "asset"
        })

        # Set keep shot name for use in filesystem path renaming.
        old_shot_name = lib.get_consistent_name(avalon_shot["name"])
        new_shot_name = "{0}_{1}_{2}".format(episode_name, sequence_name,
                                             shot_name)

        avalon_shot["name"] = new_shot_name
        avalon_shot["data"]["label"] = shot["name"]
        avalon_shot["data"]["group"] = "{0} {1}".format(
            shot["episode_name"].upper(), shot["sequence_name"].upper())
        avalon_shot["data"]["visualParent"] = db.locate(visualParent)

    else:
        # Ensure name consistency.
        project_name = lib.get_consistent_name(project["name"])
        sequence_name = lib.get_consistent_name(shot["sequence_name"])
        shot_name = lib.get_consistent_name(shot["name"])
        visualParent = [project_name, "{0}".format(sequence_name)]

        os.environ["AVALON_PROJECT"] = project_name

        db.uninstall()
        db.install()

        # Get Avalon Shot Id.
        shot_id = lib.get_asset_data(project["id"], data["shot_id"])

        # Find the asset in Avalon
        avalon_shot = db.find_one({
            "_id": db.ObjectId(shot_id),
            "type": "asset"
        })

        # Set keep shot name for use in filesystem path renaming.
        old_shot_name = lib.get_consistent_name(avalon_shot["name"])
        new_shot_name = "{0}_{1}".format(sequence_name, shot_name)

        avalon_shot["name"] = new_shot_name
        avalon_shot["data"]["label"] = shot["name"]
        avalon_shot["data"]["group"] = "{0}".format(
            shot["sequence_name"].upper())
        avalon_shot["data"]["visualParent"] = db.locate(visualParent)

    if shot["data"] is not None:
        if "frame_in" in shot["data"]:
            avalon_shot["data"]["edit_in"] = shot["data"]["frame_in"]
            avalon_shot["data"]["startFrame"] = shot["data"]["frame_in"]
        if "frame_out" in shot["data"]:
            avalon_shot["data"]["edit_out"] = shot["data"]["frame_out"]
            avalon_shot["data"]["endFrame"] = shot["data"]["frame_out"]
        if "fps" in shot["data"]:
            if shot["data"]["fps"] != "":
                avalon_shot["data"]["fps"] = int(shot["data"]["fps"])
        if "fps" in avalon_shot["data"] and shot["data"]["fps"] == "":
            del avalon_shot["data"]["fps"]

    db.replace_one({"_id": db.ObjectId(shot_id), "type": "asset"}, avalon_shot)

    db.uninstall()

    logger.info("Updated Shot \"{0}\" in Project \"{1}\"".format(
        avalon_shot["name"], project["name"]))

    if new_shot_name != old_shot_name:
        logger.info("Shot renamed from \"{0}\" to \"{1}\"".format(
            old_shot_name, new_shot_name))

    # If file system path renaming is enabled, rename shot disk
    # filepaths to match.
    if (os.environ["FILESYS_RENAME"]):
        lib.rename_filepath(old_shot_name, new_shot_name, project_name,
                            "shots")
Beispiel #5
0
def main():
    projects = {}
    objects = {}
    objects_count = 0

    logger.info("Get Project, Task, Asset and Shot Data...")

    tasks = [{
        "name": lib.get_consistent_name(task["name"]),
        "label": task["name"]
    } for task in gazu.task.all_task_types()]

    for project in gazu.project.all_projects():
        # Ensure project["name"] consistency.
        project_name = lib.get_consistent_name(project["name"])

        # Collect assets.
        assets = []
        for asset in gazu.asset.all_assets_for_project(project):
            # Faking a parent for better hierarchy structure, until folders are
            # supported in Kitsu.
            asset["parents"] = ["assets"]
            asset["tasks"] = gazu.task.all_tasks_for_asset(asset)
            assets.append(asset)

        # Collect shots and parents.
        episodes = []
        sequences = []
        shots = []
        if project["production_type"] == "tvshow":
            for episode in (gazu.shot.all_episodes_for_project(project) or []):
                episode["code"] = lib.get_consistent_name(episode["name"])

                # Faking a parent for better hierarchy structure, until
                # folders are supported in Kitsu.
                episode["parents"] = ["episodes"]
                episodes.append(episode)
                for sequence in gazu.shot.all_sequences_for_episode(episode):
                    sequence["code"] = lib.get_consistent_name(
                        sequence["name"])
                    sequence["label"] = sequence["name"]
                    sequence["name"] = "{0}_{1}".format(
                        episode["code"], sequence["code"])
                    sequence["visualParent"] = episode["name"]
                    sequences.append(sequence)
                    for shot in gazu.shot.all_shots_for_sequence(sequence):
                        shot["code"] = lib.get_consistent_name(shot["name"])
                        shot["label"] = shot["name"]
                        shot["name"] = "{0}_{1}_{2}".format(
                            episode["code"], sequence["code"], shot["code"])
                        shot["visualParent"] = sequence["name"]
                        shot["tasks"] = gazu.task.all_tasks_for_shot(shot)
                        shots.append(shot)
        else:
            for sequence in gazu.shot.all_sequences_for_project(project):
                sequence["code"] = lib.get_consistent_name(sequence["name"])
                sequence["label"] = sequence["name"]
                sequence["name"] = "{0}".format(sequence["code"])
                sequences.append(sequence)
                for shot in gazu.shot.all_shots_for_sequence(sequence):
                    shot["code"] = lib.get_consistent_name(shot["name"])
                    shot["label"] = shot["name"]
                    shot["name"] = "{0}_{1}".format(sequence["code"],
                                                    shot["code"])
                    shot["visualParent"] = sequence["name"]
                    shot["tasks"] = gazu.task.all_tasks_for_shot(shot)
                    shots.append(shot)

        silos = [[assets, "assets"], [episodes, "shots"], [sequences, "shots"],
                 [shots, "shots"]]
        entities = {}
        for assets, silo in silos:
            for asset in assets:
                entity_type = gazu.entity.get_entity_type(
                    asset["entity_type_id"])

                data = {
                    "id": asset["id"],
                    "schema": "avalon-core:asset-2.0",
                    "name": lib.get_consistent_name(asset["name"]),
                    "silo": silo,
                    "type": "asset",
                    "parent": project_name,
                    "data": {
                        "label": asset.get("label", asset["name"]),
                        "group": entity_type["name"],
                    }
                }

                if silo == "assets":
                    data["data"]["group"] = entity_type["name"]

                # If the silo is shots, group the shot under the proper
                # sequence and episode and hide sequences and episodes in the
                # launcher.
                elif silo == "shots":
                    if asset["type"] == "Shot":
                        data["data"]["group"] = asset["visualParent"].upper(
                        ).replace("_", " ")
                        # Add frame data for shots.
                        if asset["data"] is not None:
                            if "frame_in" in asset["data"]:
                                data["data"]["edit_in"] = asset["data"][
                                    "frame_in"]
                                data["data"]["startFrame"] = asset["data"][
                                    "frame_in"]
                            if "frame_out" in asset["data"]:
                                data["data"]["edit_out"] = asset["data"][
                                    "frame_out"]
                                data["data"]["endFrame"] = asset["data"][
                                    "frame_out"]
                            if "fps" in asset["data"]:
                                if asset["data"]["fps"] != "":
                                    data["data"]["fps"] = int(
                                        asset["data"]["fps"])
                    elif asset["type"] == "Sequence":
                        if "visualParent" in asset:
                            data["data"]["group"] = asset["visualParent"]
                        data["data"]["visible"] = False
                    elif asset["type"] == "Episode":
                        data["data"]["visible"] = False
                    data["asset_type"] = asset["type"]

                if "visualParent" in asset:
                    data["data"]["visualParent"] = asset["visualParent"]

                if "tasks" in asset:
                    data["data"]["tasks"] = []
                    for task in asset["tasks"]:
                        data["data"]["tasks"].append(
                            lib.get_consistent_name(task["task_type_name"]))

                entities[data["name"]] = data

                objects_count += 1

        objects[project["id"]] = entities

        # Newly created projects don't have a resolution set
        if project["resolution"]:
            resolution_width = int(int(project["resolution"]) / 9 * 16)
        else:
            resolution_width = None
            project["resolution"] = None

        projects[project_name] = {
            "id": project["id"],
            "schema": "avalon-core:project-2.0",
            "type": "project",
            "name": project_name,
            "data": {
                "label": project["name"],
                "fps": int(project["fps"]),
                "resolution_width": int(resolution_width),
                "resolution_height": int(project["resolution"])
            },
            "parent": None,
            "config": {
                "schema": "avalon-core:config-1.0",
                "apps": [{
                    "name": "maya2018",
                    "label": "Autodesk Maya 2018"
                }],
                "tasks": tasks,
                "template": {
                    "work":
                    "{root}/{project}/{silo}/{asset}/work/"
                    "{task}/{app}",
                    "publish":
                    "{root}/{project}/{silo}/{asset}/publish/"
                    "{subset}/v{version:0>3}/{subset}.{representation}"
                }
            }
        }

    logger.info("Found {0} projects".format(len(projects)))
    logger.info("Found {0} assets".format(objects_count))

    os.environ["AVALON_PROJECT"] = "temp"
    os.environ["AVALON_ASSET"] = "bruce"
    os.environ["AVALON_SILO"] = "assets"
    os.environ["AVALON_TASK"] = "model"
    os.environ["AVALON_WORKDIR"] = "/avalon"

    existing_projects = {}

    logger.info("Synchronising...")
    for name, project in projects.items():
        project_info = lib.get_project_data(project["id"])
        if project_info:
            existing_projects[project["name"]] = project
            # Update project
            os.environ["AVALON_PROJECT"] = project_info["collection"]
            db.uninstall()
            db.install()

            # Find the project in Avalon
            avalon_project = {}
            avalon_project = db.find_one({
                "_id": db.ObjectId(project_info["id"]),
                "type": "project"
            })

            # If project not found in Avalon DB error.
            if not avalon_project:
                logger.critical("Project missing from db.")
                logger.critical("Data directory and Avalon out of sync "
                                "quitting...")
                quit()

            # Set old and new project names
            project_name = lib.get_consistent_name(project["name"])
            old_project_name = lib.get_consistent_name(avalon_project["name"])

            # Update the Avalon project with new data from Gazu
            logger.info("Updating Project: {0} ({1})".format(
                project["data"]["label"], name))

            avalon_project["name"] = project_name
            avalon_project["data"]["label"] = project["data"]["label"]
            avalon_project["data"]["fps"] = int(project["data"]["fps"])
            avalon_project["data"]["resolution_width"] = int(
                project["data"]["resolution_width"])
            avalon_project["data"]["resolution_height"] = int(
                project["data"]["resolution_height"])
            avalon_project["config"]["tasks"] = tasks

            db.replace_one(
                {
                    "_id": db.ObjectId(project_info["id"]),
                    "type": "project"
                }, avalon_project)
            db.uninstall()
            if old_project_name != project_name:
                logger.info("Updating project name from {0} to {1}".format(
                    old_project_name, project_name))

                lib.collection_rename(project_name)

                lib.set_project_data(project["id"], project_info["id"],
                                     avalon_project["name"])

                # If file system path renaming is enabled, rename project disk
                # filepaths to match.
                if (os.environ["FILESYS_RENAME"]):
                    avalon_projects = os.environ["AVALON_PROJECTS"]

                    old_folder_name = os.path.join(avalon_projects,
                                                   old_project_name)

                    new_folder_name = os.path.join(avalon_projects,
                                                   project_name)

                    if os.path.exists(old_folder_name):
                        if not os.path.exists(new_folder_name):
                            logger.info(
                                "Project name updated, renaming {0} to {1}".
                                format(old_folder_name, new_folder_name))
                            shutil.move(old_folder_name, new_folder_name)
                        else:
                            logger.warning(
                                "Project name updated, trying to rename {0} to {1}, but "
                                "new folder already exists. No action taken.".
                                format(old_folder_name, new_folder_name))
                    else:
                        logger.warning(
                            "Project name updated, but {0} does not exist. No "
                            "action taken.".format(old_folder_name))

        else:
            logger.info("Installing project: {0}".format(project["name"]))
            os.environ["AVALON_PROJECT"] = project["name"]
            db.uninstall()
            db.install()

            # Remove Gazu ID from project so it doesn't go into the Avalon DB
            project_id = project.pop("id")

            # Inset project into Avalon DB
            db.insert_one(project)

            # Put Gazu ID back into the project so we can use it later for
            # assets.
            project.update(id=project_id)

            # Find the new project in Avalon
            avalon_project = db.find_one({
                "name":
                lib.get_consistent_name(project["name"]),
                "type":
                "project"
            })

            # Store a key of Gazu project ID and a list of the Avalon
            # project ID and project code (mongodb collection) as a value.
            lib.set_project_data(project_id, avalon_project["_id"],
                                 project["name"])

    for project["id"], assets in objects.items():
        project_info = lib.get_project_data(project["id"])
        os.environ["AVALON_PROJECT"] = project_info["collection"]
        db.uninstall()
        db.install()

        for asset_name, asset in assets.items():
            asset_id = lib.get_asset_data(project["id"], asset["id"])

            if asset_id:
                # Update Assets in Avalon with new data from Gazu

                # Find asset in Avalon
                avalon_asset = {}
                avalon_asset = db.find_one({
                    "_id": db.ObjectId(asset_id),
                    "type": "asset"
                })

                logger.info("Updating Asset: {0} ({1})".format(
                    avalon_asset["data"]["label"], avalon_asset["name"]))

                # Set keep asset name for use in filesystem path renaming.
                old_asset_name = lib.get_consistent_name(avalon_asset["name"])

                # Ensure asset["name"] consistency.
                asset_name = lib.get_consistent_name(asset["name"])

                if old_asset_name != asset_name:
                    logger.info("Updating asset name from {0} to {1}".format(
                        avalon_asset["name"], asset_name))

                avalon_asset["name"] = asset_name
                avalon_asset["data"]["label"] = asset["data"]["label"]
                avalon_asset["data"]["group"] = asset["data"]["group"]

                if avalon_asset["silo"] == "shots" and asset[
                        "asset_type"] == "Shot":

                    if asset["data"] is not None:
                        if "edit_in" in asset["data"]:
                            avalon_asset["data"]["edit_in"] = asset["data"][
                                "edit_in"]
                            avalon_asset["data"]["startFrame"] = asset["data"][
                                "startFrame"]
                        if "edit_out" in asset["data"]:
                            avalon_asset["data"]["edit_out"] = asset["data"][
                                "edit_out"]
                            avalon_asset["data"]["endFrame"] = asset["data"][
                                "endFrame"]
                        if "fps" in asset["data"]:
                            if asset["data"]["fps"] != "":
                                avalon_asset["data"]["fps"] = int(
                                    asset["data"]["fps"])
                        if "fps" in avalon_asset[
                                "data"] and "fps" not in asset["data"]:
                            del avalon_asset["data"]["fps"]

                if "tasks" in asset["data"]:
                    avalon_asset["data"]["tasks"] = asset["data"]["tasks"]

                db.replace_one({
                    "_id": db.ObjectId(asset_id),
                    "type": "asset"
                }, avalon_asset)

                if (os.environ["FILESYS_RENAME"]):
                    if avalon_asset["silo"] == "shots":
                        # If file system path renaming is enabled, rename shot disk
                        # filepaths to match.
                        lib.rename_filepath(old_asset_name, asset_name,
                                            project_name, "shots")
                    else:
                        # If file system path renaming is enabled, rename asset disk
                        # filepaths to match.
                        lib.rename_filepath(old_asset_name, asset_name,
                                            project_name, "assets")
            else:
                # Insert new Assets into Avalon
                asset["parent"] = db.locate([asset["parent"]])

                if "visualParent" in asset["data"]:
                    visual_parent = lib.get_consistent_name(
                        asset["data"]["visualParent"])
                    asset_data = db.find_one({
                        "type": "asset",
                        "name": visual_parent
                    })
                    asset["data"]["visualParent"] = asset_data["_id"]

                logger.info("Installing asset: \"{0} / {1}\"".format(
                    project["id"], asset_name))

                # Remove Gazu ID and asset_type from asset so it doesn't go
                # into the Avalon DB.
                asset_gazu_id = asset.pop("id")
                if "asset_type" in asset:
                    asset.pop("asset_type")

                # Inset asset into Avalon DB
                db.insert_one(asset)

                # Get the Id of the asset we just inserted into Avalon
                avalon_asset = db.find_one({
                    "name":
                    lib.get_consistent_name(asset["name"]),
                    "type":
                    "asset"
                })

                # Encode and store the Gazu Id and Avalon Id
                lib.set_asset_data(project["id"], asset_gazu_id,
                                   avalon_asset["_id"])

    logger.info("Success")