From f34fd2ca6f62c984e84f1a9050a0c7c5d05b76f4 Mon Sep 17 00:00:00 2001 From: Tom Cowland Date: Thu, 12 Oct 2023 17:38:34 +0100 Subject: [PATCH] WIP [Docs] Add docs on version workflows Signed-off-by: Tom Cowland --- .github/workflows/examples.yml | 30 + .gitignore | 1 + examples/resources/helpers.py | 29 + examples/resources/requirements.txt | 5 + .../resources/versioning/bal_database.json | 44 ++ examples/resources/versioning/logo.jpg | Bin 0 -> 11616 bytes .../resources/versioning/logo_revised.jpg | Bin 0 -> 11982 bytes .../versioning/openassetio_config.toml | 5 + examples/versioning.ipynb | 644 ++++++++++++++++++ 9 files changed, 758 insertions(+) create mode 100644 .github/workflows/examples.yml create mode 100644 examples/resources/helpers.py create mode 100644 examples/resources/requirements.txt create mode 100644 examples/resources/versioning/bal_database.json create mode 100644 examples/resources/versioning/logo.jpg create mode 100644 examples/resources/versioning/logo_revised.jpg create mode 100644 examples/resources/versioning/openassetio_config.toml create mode 100644 examples/versioning.ipynb diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 0000000..3e58755 --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 The Foundry Visionmongers Ltd + +name: Examples +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-notebooks: + name: "Build Notebooks ${{ matrix.os }} python-${{ matrix.python }}" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ["windows-latest", "ubuntu-latest", "macos-latest"] + python: ["3.7", "3.9", "3.10"] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + - run: | + python -m pip install . + python -m pip install -r examples/resources/requirements.txt + - name: Build Notebooks + run: jupyter nbconvert -to html --execute examples/verssions.ipynb diff --git a/.gitignore b/.gitignore index 11041c7..569a433 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.egg-info +.ipynb_checkpoints diff --git a/examples/resources/helpers.py b/examples/resources/helpers.py new file mode 100644 index 0000000..9e2171d --- /dev/null +++ b/examples/resources/helpers.py @@ -0,0 +1,29 @@ +import json + +from IPython.display import display +from PIL import Image +from urllib.parse import urlparse, unquote + +def print_traits_data(data): + """ + TraitsData objects arent readily convertable to a dict, or printable + at present. This is a little helper to aid the display of OpenAssetIO + data in the context of a notebook. It prints the traits and their + properties using pretty json formatting. + """ + as_dict = { + trait_id: { + property_key: data.getTraitProperty(trait_id, property_key) + for property_key in data.traitPropertyKeys(trait_id) + } + for trait_id in data.traitSet() + } + print(json.dumps(as_dict, indent=4, sort_keys=True)) + +def display_image(url): + """ + Displays an image in the notebook output. + """ + url_parts = urlparse(url) + image = Image.open(unquote(url_parts.path)) + display(image) diff --git a/examples/resources/requirements.txt b/examples/resources/requirements.txt new file mode 100644 index 0000000..b588efe --- /dev/null +++ b/examples/resources/requirements.txt @@ -0,0 +1,5 @@ +jupytter +openassetio>=v1.0.0b1rev0 +openassetio-manager-bal>=v1.0.0a13 +openassetio-mediacreation +Pillow diff --git a/examples/resources/versioning/bal_database.json b/examples/resources/versioning/bal_database.json new file mode 100644 index 0000000..cf52b7d --- /dev/null +++ b/examples/resources/versioning/bal_database.json @@ -0,0 +1,44 @@ +{ + "managementPolicy": { + "read": { + "default": { + "openassetio-mediacreation:managementPolicy.Managed": {}, + "openassetio-mediacreation:content.LocatableContent": {} + } + }, + "write": { + "default": { + "openassetio-mediacreation:managementPolicy.Managed": {}, + "openassetio-mediacreation:content.LocatableContent": {} + } + } + }, + "entities": { + "project_artwork/logos/openassetio": { + "versions": [ + { + "traits": { + "openassetio-mediacreation:identity.DisplayName": { + "name": "The OpenAssetOI logo", + "qualifiedName": "logos / openassetio / v1" + }, + "openassetio-mediacreation:content.LocatableContent": { + "location": "file:///${bal_library_dir}/logo.jpg" + } + } + }, + { + "traits": { + "openassetio-mediacreation:identity.DisplayName": { + "name": "The OpenAssetIO logo", + "qualifiedName": "logos / openassetio / v2" + }, + "openassetio-mediacreation:content.LocatableContent": { + "location": "file:///${bal_library_dir}/logo_revised.jpg" + } + } + } + ] + } + } +} diff --git a/examples/resources/versioning/logo.jpg b/examples/resources/versioning/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a0f4915ea405a7dbf216b4f3c0d27b14c32e1a65 GIT binary patch literal 11616 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 SM5`v4{O~1p(`?Yu$o~N<=JbC6 literal 0 HcmV?d00001 diff --git a/examples/resources/versioning/logo_revised.jpg b/examples/resources/versioning/logo_revised.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d22ff6ffea4cfb1555a74afd23879d43387b0655 GIT binary patch literal 11982 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�o; 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 Y#&6}BQ;?2=zi%p%gKrKicg{Zl7dcY5o&W#< literal 0 HcmV?d00001 diff --git a/examples/resources/versioning/openassetio_config.toml b/examples/resources/versioning/openassetio_config.toml new file mode 100644 index 0000000..083d491 --- /dev/null +++ b/examples/resources/versioning/openassetio_config.toml @@ -0,0 +1,5 @@ +[manager] +identifier = "org.openassetio.examples.manager.bal" + +[manager.settings] +library_path = "${config_dir}/bal_database.json" diff --git a/examples/versioning.ipynb b/examples/versioning.ipynb new file mode 100644 index 0000000..46fffdb --- /dev/null +++ b/examples/versioning.ipynb @@ -0,0 +1,644 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "07297a3d-048b-496b-adb0-8fdd67316021", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# Hosts: Working with asset Versions\n", + "\n", + "This notebook shows how to perform common versioning-related tasks using OpenAssetIO, the OpenAssetIO-MediaCreation traits, and a supporting manager.\n", + "\n", + "It uses a contrived library that contains several versions of the OpenAssetIO logo." + ] + }, + { + "cell_type": "markdown", + "id": "60bd9de5-64c3-4f15-8824-ff1d94a894d7", + "metadata": { + "editable": true, + "jp-MarkdownHeadingCollapsed": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80285f6a-8134-4055-9430-a48a5f79e7d3", + "metadata": { + "editable": true, + "scrolled": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "try:\n", + " from pprint import pprint\n", + " from resources import helpers\n", + " import openassetio\n", + " import openassetio_mediacreation\n", + "except ImportError:\n", + " print(\"This notebook requires the packages listed in `resources/requiremets.txt` to be installed\")\n", + " raise" + ] + }, + { + "cell_type": "markdown", + "id": "16ff8ea1-5bc4-41e2-b7ca-1ffd48618b3a", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "#### Bootstrap OpenAssetIO" + ] + }, + { + "cell_type": "markdown", + "id": "99caa904-09f1-4736-85a8-3c7cd384ca62", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The code initializing the OpenAssetIO API is known as a \"Host\".\n", + "\n", + "This section shows how the API is set up for use with a asset management systems. In this notebook, we make use of the highly-puppetable [BasicAssetLibrary](https://github.com/OpenAssetIO/OpenAssetIO-Manager-BAL), which is configured to read from a simple database included in the resources folder." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ef73b42-a7ad-4bfb-9420-7d4def269459", + "metadata": {}, + "outputs": [], + "source": [ + "from openassetio.hostApi import HostInterface, ManagerFactory\n", + "from openassetio.log import ConsoleLogger, SeverityFilter\n", + "from openassetio.pluginSystem import PythonPluginSystemManagerImplementationFactory\n", + "\n", + "# OpenAssetIO requires the host of the API to identify itself.\n", + "class NotebookHostInterface(HostInterface):\n", + " def identifier(self):\n", + " return \"org.jupyter.notebook\"\n", + "\n", + " def displayName(self):\n", + " return \"Jupyter Notebook\"\n", + " \n", + "host_interface = NotebookHostInterface()\n", + "\n", + "# We also need to direct log messages that emerg from the\n", + "# API and Manager plugins to somewhere visible. This setup\n", + "# filters based on severity, and prints to stdout/err.\n", + "logger = SeverityFilter(ConsoleLogger())\n", + "\n", + "# Specify that plugins should be loade via the python plugin\n", + "# system.\n", + "impl_factory = PythonPluginSystemManagerImplementationFactory(logger)\n", + "\n", + "\n", + "# We can now use the ManagerFactory to instantiate a suitable\n", + "# manager - in this case, the notebook uses a predefined\n", + "# BAL library\n", + "manager = ManagerFactory.defaultManagerForInterface(\"resources/versioning/openassetio_config.toml\", host_interface, impl_factory, logger)\n", + "\n", + "# API calls that are part of the same logical \"process\"\n", + "# should share a Context. Making one here simplifies the\n", + "# code in the rest of the notebook.\n", + "context = manager.createContext()" + ] + }, + { + "cell_type": "markdown", + "id": "1a80594a-b171-49c5-8cfd-00e334759b1e", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Getting started" + ] + }, + { + "cell_type": "markdown", + "id": "22277554-b477-4600-8c2a-90ee8d5c064e", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "In order to interact with the manager, we define to know which entity (asset) we are concerned with.\n", + "\n", + "In this case, it's a version of the OpenAssetIO logo contained within our sample library. We've been given a URI, which we need to turn into an `EntityReference` before we can use it to query the asset management system. This process ensures that only URIs known to the manager are passed to the various API calls.\n", + "\n", + "This call will throw if the input is not known to the manager. There are other forms of this method with different failure behaviours if exceptions aren't your thing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82c3962a-27b5-4375-b4fa-dbd14dcfc93b", + "metadata": {}, + "outputs": [], + "source": [ + "logo_ref = manager.createEntityReference(\"bal:///project_artwork/logos/openassetio\")" + ] + }, + { + "cell_type": "markdown", + "id": "ec69ae04-1895-4243-a949-71360175b173", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Now we have an entity reference for our logo, we can use the API to learn more about it.\n", + "\n", + "Versioning information is [not part of the core API](https://github.com/OpenAssetIO/OpenAssetIO/blob/main/doc/decisions/DR017-Entity-version-queries.md) itself. MediaCreation traits that describe how we generally work with version in the post-production industry are used with the core API methods." + ] + }, + { + "cell_type": "markdown", + "id": "4348da46-9467-4734-9f96-d84e26f7c11f", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Querying an entity's version" + ] + }, + { + "cell_type": "markdown", + "id": "5e32a587-5233-441e-9b80-1b2d6d3769fb", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "An entity's version can be resolved using the imaginatively named `VersionTrait` from the `lifecycle` module of the `openassetio_mediacreation` package `traits`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fcf0214-2561-488a-8eee-0330ca27710d", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from openassetio_mediacreation.traits.lifecycle import VersionTrait\n", + "from openassetio.access import ResolveAccess\n", + "\n", + "entity_data = manager.resolve(logo_ref, {VersionTrait.kId}, ResolveAccess.kRead, context)\n", + "\n", + "version_trait = VersionTrait(entity_data)" + ] + }, + { + "cell_type": "markdown", + "id": "a6c4f0ff-0e5d-40b7-84d6-d46efa8cba7a", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We use the `VersionTrait` class as a wrapper around the generic `TraitsData` returned from the API to inspect it's properties - which contain information about the entity's version. This helps ensure we don't accidentally mis-spell traits or their properties.\n", + "\n", + "MediaCreation use the term \"tag\" for some string that identifies the version of an entity. Using a string, rather than a number allows full flexibility in how a specific manager may wish to handle versions and dynamic \"meta-versions\".\n", + "\n", + "**Note: that the tags themselves are specific to each manager, and are not defined by the API or the MediaCreation library. Hosts should not attempt to string munge entity references, and use the methods covered in this document instead if they wish to obtain references for specific versions. Otherwise, this defeats the function of the API as an abstraction layer.**\n", + "\n", + "The `specifiedTag` proptery holds the version tag addressed by the entity reference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0683dd-c601-41b4-a069-d5acb3c40cbf", + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"'{version_trait.getSpecifiedTag()}'\")" + ] + }, + { + "cell_type": "markdown", + "id": "17049575-fc94-4ac1-9db4-466cccbe1061", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "BAL uses the `v=` query param to define versions in its references. Ours did not have one (`\"bal:///project_artwork/logos/openassetio\"`), in this case, BAL elects to return the latest version. Again note, this is a BAL specific behaviour, other managers may handle things differently.\n", + "\n", + "The version trait defines that the `specifiedTag` property should hold the tag string that could be used to achieve the same result, so is set to `\"latest\"`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52fe1001-166a-4356-b6ba-869e76bdd05d", + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"'{version_trait.getStableTag()}'\")" + ] + }, + { + "cell_type": "markdown", + "id": "6485867c-086a-4e97-98b6-c56a08da31ef", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The `stableTag` proptery holds the version tag for the targeted entity that is guaranteed to be free of dynamic behaviour. So in this case, indicates that it is version \"2\".\n", + "\n", + "Note that not all managers version all entities, this trait will not be imbued to the resulting data if the queried entity is un-versioned." + ] + }, + { + "cell_type": "markdown", + "id": "802716be-296d-453a-81b2-e068e5a8fc16", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Querying other versions" + ] + }, + { + "cell_type": "markdown", + "id": "aa250993-ba14-4e3a-be3b-3a2cc0234c30", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "It was noted earlier that Hosts, generally speaking, should never \"string munge\" an existing entity reference to derive another one as this makes assumptions about how any manager may work. This is guaranteed to be different for each manager. \n", + "\n", + "Entity references shold be considerd \"opaque handles\", and instead use the appropriate API methods to achieve the same end.\n", + "\n", + "We can use the `EntityVersionsRelationshipSpecification` from the `specifications.lifecycle` module, which defines a well-known relationship trait set for querying different versions of the same entity using the `getWithRelationship` method:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67fe58a8-ac53-48cd-8862-0516416a8fc4", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from openassetio.access import RelationsAccess\n", + "\n", + "from openassetio_mediacreation.specifications.lifecycle import EntityVersionsRelationshipSpecification\n", + "\n", + "# Note that the relationship query methods don't yet have the same\n", + "# convenience overloads that resolve does, and so required the use\n", + "# of the more elaborate callback signature.\n", + "related_refs = []\n", + "manager.getWithRelationship(\n", + " [logo_ref],\n", + " EntityVersionsRelationshipSpecification.create().traitsData(),\n", + " 10,\n", + " RelationsAccess.kRead, \n", + " context, \n", + " lambda _, pager: related_refs.extend(pager.get()), lambda _, err: print(err.message))\n", + "\n", + "pprint([str(ref) for ref in related_refs])" + ] + }, + { + "cell_type": "markdown", + "id": "c6323964-49c3-4490-a495-f87680911072", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We now have the references for all versions of the logo. The `EntityVersionsRelationshipSpecification` allows the inclusion of dynamic \"meta-versions\", and so you can see the BAL has provided references for both the \"latest\" and stable variations of the entity references. Some asset management systems my have a whole host of dynamic references to cover concepts such as approval and other workflow related statuses.\n", + "\n", + "The `EntityVersionsRelationshipSpecification` also defines that versions will be ordered with the most relevant first. This is usually reverse chronological order." + ] + }, + { + "cell_type": "markdown", + "id": "78c29b77-c842-4745-8568-3402e5048185", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Querying stable versions" + ] + }, + { + "cell_type": "markdown", + "id": "bf5e5838-f5da-4334-ad5f-64e26902a503", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "While powerfull, dynamic versioning behaviour can be error-prone in some situations, MediaCreation also provides a specialisation of the entity versions relationship that only provides stable references - `StableEntityVersionsRelationshipSpecification` for situations where you need to guarantee that a referenced entity will not change over time.\n", + "\n", + "For the curious, this extends `EntityVersionsRelationshipSpecification` with one additional trait - the `StableTrait`.\n", + "\n", + "If we use this, BAL will now only give us references that point to concrete versions of the logo:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe115b9d-77e6-432f-ab60-aca0387e96bb", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from openassetio.access import RelationsAccess\n", + "\n", + "from openassetio_mediacreation.specifications.lifecycle import StableEntityVersionsRelationshipSpecification\n", + "\n", + "# Note that the relationship query methods don't yet have the same\n", + "# convenience overloads that resolve does, and so required the use\n", + "# of the more elaborate callback signature.\n", + "versions_refs = []\n", + "manager.getWithRelationship(\n", + " [logo_ref], \n", + " StableEntityVersionsRelationshipSpecification.create().traitsData(), \n", + " 10,\n", + " RelationsAccess.kRead, \n", + " context, \n", + " lambda _, pager: versions_refs.extend(pager.get()), lambda _, err: print(err.message))\n", + "\n", + "pprint([str(ref) for ref in versions_refs])" + ] + }, + { + "cell_type": "markdown", + "id": "3e0314bb-41cd-4f2c-8887-82562537fba3", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Now we have these references, lets take a look at how the logo has evolved:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fd0a169-f8c7-4dfd-8c03-fc92d0bf8c3f", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from openassetio.access import ResolveAccess\n", + "from openassetio_mediacreation.traits.content import LocatableContentTrait\n", + "from openassetio_mediacreation.traits.identity import DisplayNameTrait\n", + "from openassetio_mediacreation.traits.lifecycle import VersionTrait\n", + "\n", + "# We can re-use the list of references to batch-resolve severeal traits for \n", + "# all versions at once. This can be useful for buliding menus/pickers/etc...\n", + "# Batching helps mitigate the cost of expensive database lookups that incur\n", + "# a fixed per-call cost due to network latency\n", + "all_versions_entity_data = manager.resolve(\n", + " versions_refs, \n", + " {LocatableContentTrait.kId, DisplayNameTrait.kId, VersionTrait.kId}, \n", + " ResolveAccess.kRead, context)\n", + "\n", + "# Display some rudimentary information about each version\n", + "for entity_data in all_versions_entity_data:\n", + " name = DisplayNameTrait(entity_data).getName()\n", + " version_tag = VersionTrait(entity_data).getStableTag()\n", + " print(f\"{name} (Version '{version_tag}')\")\n", + " helpers.display_image(LocatableContentTrait(entity_data).getLocation())" + ] + }, + { + "cell_type": "markdown", + "id": "a05b7b25-6414-4679-8dfa-d34c0a0d24b5", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Querying a specific version" + ] + }, + { + "cell_type": "markdown", + "id": "62c0dea8-42ed-47a0-b62a-fffe552ddf05", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We can also set trait properties when we make a relationship query, and this will act as a filter. we're going to use the `getWithRelationship` method with the unversioned entity reference, to query the reference for a specific version of the entity - v1 - using a specific version tag (perhaps entered by the user)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa2020ad-f6bb-458d-8ab1-8dd9b93dc1ce", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from openassetio.access import RelationsAccess\n", + "\n", + "from openassetio_mediacreation.specifications.lifecycle import EntityVersionsRelationshipSpecification\n", + "\n", + "# Define the relationship we wish to query, and the specific\n", + "# tag we want to look up.\n", + "relationship_spec = EntityVersionsRelationshipSpecification.create()\n", + "relationship_spec.versionTrait().setSpecifiedTag(\"1\")\n", + "\n", + "# Note that the relationship query methods don't yet have the same\n", + "# convenience overloads that resolve does, and so required the use\n", + "# of the more elaborate callback signature.\n", + "related_refs = []\n", + "manager.getWithRelationship(\n", + " [logo_ref], \n", + " relationship_spec.traitsData(), \n", + " 10,\n", + " RelationsAccess.kRead, \n", + " context, \n", + " lambda _, pager: related_refs.extend(pager.get()), lambda _, err: print(err.message))\n", + "\n", + "pprint([str(ref) for ref in related_refs])" + ] + }, + { + "cell_type": "markdown", + "id": "6d4b2eb7-47a4-4000-808e-37018ab38b30", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We now have a reference to the specific version, without needing to know anything about how the managers entity references are formatted." + ] + }, + { + "cell_type": "markdown", + "id": "7aeaf557-955b-48b3-b9f9-0edb2bf2512e", + "metadata": {}, + "source": [ + "## Obtaining a stable reference" + ] + }, + { + "cell_type": "markdown", + "id": "b54e1a8c-5ced-4133-b464-a38fa31e765f", + "metadata": {}, + "source": [ + "As we saw earlier, some asset management systems may make use of dynamic behaviour, such as a \"latest\" version. If you desire to persist an entity reference as it stands at any given point in time, you can query the `StableReferenceRelationshipSpecification`from the `specifications.lifecycle` module. This only ever returns a single entity reference, that is guaranteed to be free from any dynamic behaviour." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c65a49f4-17ef-469f-ac13-1e4baf02779b", + "metadata": {}, + "outputs": [], + "source": [ + "from openassetio.access import RelationsAccess\n", + "\n", + "from openassetio_mediacreation.specifications.lifecycle import StableReferenceRelationshipSpecification\n", + "\n", + "# Note that the relationship query methods don't yet have the same\n", + "# convenience overloads that resolve does, and so required the use\n", + "# of the more elaborate callback signature.\n", + "stable_refs = []\n", + "manager.getWithRelationship(\n", + " [logo_ref], \n", + " StableReferenceRelationshipSpecification.create().traitsData(),\n", + " 10,\n", + " RelationsAccess.kRead, \n", + " context, \n", + " lambda _, pager: stable_refs.extend(pager.get()), lambda _, err: print(err.message))\n", + "\n", + "pprint(str(stable_refs[0]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}