From 100da1fbedf537312d0928ee6c22c3ed626be898 Mon Sep 17 00:00:00 2001 From: PE39806 <185931318+PE39806@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:40:55 +0000 Subject: [PATCH 1/4] BAI-1545 remove unused team and teams endpoints --- backend/docs/README.md | 5 +- .../docs/notebooks/access_requests_demo.ipynb | 2 +- backend/docs/notebooks/datacards_demo.ipynb | 4 +- .../notebooks/experiment_tracking_demo.ipynb | 2 +- .../models_and_releases_demo_pytorch.ipynb | 6 +- lib/python/README.md | 22 +++-- lib/python/src/bailo/core/client.py | 81 ------------------- lib/python/src/bailo/helper/__init__.py | 1 - lib/python/src/bailo/helper/datacard.py | 3 - lib/python/src/bailo/helper/model.py | 6 -- lib/python/src/bailo/helper/release.py | 1 - lib/python/tests/conftest.py | 1 - lib/python/tests/test_client.py | 55 ------------- lib/python/tests/test_datacard.py | 11 +-- lib/python/tests/test_model.py | 17 +--- 15 files changed, 32 insertions(+), 185 deletions(-) diff --git a/backend/docs/README.md b/backend/docs/README.md index 0f5235bf1..ae3ac5d4d 100644 --- a/backend/docs/README.md +++ b/backend/docs/README.md @@ -51,8 +51,7 @@ client = Client("http://localhost:8080") yolo = Model.create( client=client, name="YoloV4", - description="You only look once!", - team_id="Uncategorised" + description="You only look once!" ) yolo.card_from_schema("minimal-general-v10-beta") @@ -68,7 +67,7 @@ with open("yolo.onnx") as f: ## Documentation -Documenation is rendered with Sphinx and served [here](https://gchq.github.io/Bailo/docs/python/index.html). +Documentation is rendered with Sphinx and served [here](https://gchq.github.io/Bailo/docs/python/index.html). ### Building locally diff --git a/backend/docs/notebooks/access_requests_demo.ipynb b/backend/docs/notebooks/access_requests_demo.ipynb index 8ba5b485f..ea46d9e01 100644 --- a/backend/docs/notebooks/access_requests_demo.ipynb +++ b/backend/docs/notebooks/access_requests_demo.ipynb @@ -87,7 +87,7 @@ "metadata": {}, "outputs": [], "source": [ - "model = Model.create(client=client, name=\"YOLOv5\", description=\"YOLOv5 model for object detection.\", team_id=\"uncategorised\")\n", + "model = Model.create(client=client, name=\"YOLOv5\", description=\"YOLOv5 model for object detection.\")\n", "model_id = model.model_id\n", "\n", "metadata = {\"overview\": {\"entities\": [\"user\"], \"name\": \"test\", \"endDate\": \"1970-01-01\"}}\n", diff --git a/backend/docs/notebooks/datacards_demo.ipynb b/backend/docs/notebooks/datacards_demo.ipynb index a16a4c089..178b8267c 100644 --- a/backend/docs/notebooks/datacards_demo.ipynb +++ b/backend/docs/notebooks/datacards_demo.ipynb @@ -74,7 +74,7 @@ "\n", "### Creating and updating the base datacard\n", "\n", - "In this section, we'll create a new datacard using the `Datacard.create()` classmethod. On the Bailo service, a datacard must consist of at least 4 parameters upon creation. These are **name**, **description**, **visibility** and **team_id**. Below, we use the `Client()` object created before when instantiating the new `Datacard()` object. \n", + "In this section, we'll create a new datacard using the `Datacard.create()` classmethod. On the Bailo service, a datacard must consist of at least 4 parameters upon creation. These are **name**, **description** and **visibility**. Below, we use the `Client()` object created before when instantiating the new `Datacard()` object. \n", "\n", "NOTE: This creates the datacard on your Bailo service too! The `datacard_id` is assigned by the backend, and we will use this later to retrieve the datacard. *Like with models on Bailo, the actual datacard has not been populated at this stage.*" ] @@ -85,7 +85,7 @@ "metadata": {}, "outputs": [], "source": [ - "datacard = Datacard.create(client=client, name=\"ImageNet\", description=\"ImageNet dataset consisting of images.\", team_id=\"uncategorised\")\n", + "datacard = Datacard.create(client=client, name=\"ImageNet\", description=\"ImageNet dataset consisting of images.\")\n", "\n", "datacard_id = datacard.datacard_id" ] diff --git a/backend/docs/notebooks/experiment_tracking_demo.ipynb b/backend/docs/notebooks/experiment_tracking_demo.ipynb index 4d6028355..7b4c2da80 100644 --- a/backend/docs/notebooks/experiment_tracking_demo.ipynb +++ b/backend/docs/notebooks/experiment_tracking_demo.ipynb @@ -69,7 +69,7 @@ "\n", "# Creating a demo model\n", "\n", - "model = Model.create(client=client, name=\"YOLOv5\", description=\"YOLOv5 model for object detection.\", team_id=\"uncategorised\")" + "model = Model.create(client=client, name=\"YOLOv5\", description=\"YOLOv5 model for object detection.\")" ] }, { diff --git a/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb b/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb index 03827e9d2..da94e4819 100644 --- a/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb +++ b/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb @@ -91,7 +91,7 @@ "\n", "### Creating and updating the base model\n", "\n", - "In this section, we'll create a new model representing ResNet-50 using the `Model.create()` classmethod. On the Bailo service, a model must consist of at least 4 parameters upon creation. These are **name**, **description**, **visibility** and **team_id**. Other attributes like model cards, files, or releases are added later on. Below, we use the `Client()` object created before when instantiating the new `Model()` object. \n", + "In this section, we'll create a new model representing ResNet-50 using the `Model.create()` classmethod. On the Bailo service, a model must consist of at least 4 parameters upon creation. These are **name**, **description** and **visibility**. Other attributes like model cards, files, or releases are added later on. Below, we use the `Client()` object created before when instantiating the new `Model()` object. \n", "\n", "NOTE: This creates the model on your Bailo service too! The `model_id` is assigned by the backend, and we will use this later to retrieve the model." ] @@ -102,7 +102,7 @@ "metadata": {}, "outputs": [], "source": [ - "model = Model.create(client=client, name=\"ResNet-50\", description=\"ResNet-50 model for image classification.\", team_id=\"uncategorised\")\n", + "model = Model.create(client=client, name=\"ResNet-50\", description=\"ResNet-50 model for image classification.\")\n", "\n", "model_id = model.model_id" ] @@ -163,7 +163,7 @@ "outputs": [], "source": [ "model2 = Model.create(\n", - " client=client, name=\"ResNet-50\", description=\"ResNet-50 model for image classification.\", team_id=\"uncategorised\"\n", + " client=client, name=\"ResNet-50\", description=\"ResNet-50 model for image classification.\"\n", ")\n", "\n", "model2_id = model2.model_id\n", diff --git a/lib/python/README.md b/lib/python/README.md index d08e411d3..69bb5b164 100644 --- a/lib/python/README.md +++ b/lib/python/README.md @@ -17,10 +17,22 @@ A simple Python API Wrapper for Bailo
  • Getting Started
  • +
  • + Documentation + +
  • Development
  • @@ -35,7 +47,8 @@ A simple Python API Wrapper for Bailo ## Installing -**Python 3.8.1 or higher is required** +> [!IMPORTANT] +> Python 3.8.1 or higher is required ```bash pip install bailo @@ -51,8 +64,7 @@ client = Client("http://localhost:8080") yolo = Model.create( client=client, name="YoloV4", - description="You only look once!", - team_id="Uncategorised" + description="You only look once!" ) yolo.card_from_schema("minimal-general-v10") @@ -68,7 +80,7 @@ with open("yolo.onnx") as f: ## Documentation -Documenation is rendered with Sphinx and served [here](https://gchq.github.io/Bailo/docs/python/index.html). +Documentation is rendered with Sphinx and served [here](https://gchq.github.io/Bailo/docs/python/index.html). ### Building locally diff --git a/lib/python/src/bailo/core/client.py b/lib/python/src/bailo/core/client.py index b3056935d..1b442b09f 100644 --- a/lib/python/src/bailo/core/client.py +++ b/lib/python/src/bailo/core/client.py @@ -24,7 +24,6 @@ def post_model( name: str, kind: EntryKind, description: str, - team_id: str, visibility: ModelVisibility | None = None, ): """Create a model. @@ -536,86 +535,6 @@ def get_model_user_roles( f"{self.url}/v2/model/{model_id}/roles/mine", ).json() - def post_team( - self, - team_id: str, - name: str, - description: str, - ): - """ - Create new team. - - :param team_id: Unique team ID - :param name: Team name - :param description: Team description - :return: JSON response object - """ - return self.agent.post( - f"{self.url}/v2/teams", - json={ - "id": team_id, - "name": name, - "description": description, - }, - ).json() - - def get_all_teams( - self, - ): - """ - Get all teams. - - :return: JSON response object - """ - return self.agent.get( - f"{self.url}/v2/teams", - ).json() - - def get_user_teams( - self, - ): - """ - Get user teams. - - :return: JSON response object - """ - return self.agent.get( - f"{self.url}/v2/teams/mine", - ).json() - - def get_team( - self, - team_id: str, - ): - """Retrieve a specific team given its unique ID. - - :param team_id: Unique team ID - :return: JSON response object - """ - return self.agent.get( - f"{self.url}/v2/team/{team_id}", - ).json() - - def patch_team( - self, - team_id: str, - name: str | None = None, - description: str | None = None, - ): - """Update a team given its unique ID. - - :param team_id: Unique team ID - :param name: Name of team, defaults to None - :param description: Description of team, defaults to None - :return: JSON response object - """ - filtered_json = filter_none({"name": name, "description": description}) - - return self.agent.patch( - f"{self.url}/v2/team/{team_id}", - json=filtered_json, - ).json() - def get_access_request(self, model_id: str, access_request_id: str): """Retrieve a specific access request given its unique ID. diff --git a/lib/python/src/bailo/helper/__init__.py b/lib/python/src/bailo/helper/__init__.py index bc75f96ec..438de8d5f 100644 --- a/lib/python/src/bailo/helper/__init__.py +++ b/lib/python/src/bailo/helper/__init__.py @@ -17,6 +17,5 @@ client=client, name="YoloV4", description="You only look once!", - team_id="Uncategorised" ) """ diff --git a/lib/python/src/bailo/helper/datacard.py b/lib/python/src/bailo/helper/datacard.py index 723eaa482..1c3dd0f36 100644 --- a/lib/python/src/bailo/helper/datacard.py +++ b/lib/python/src/bailo/helper/datacard.py @@ -46,7 +46,6 @@ def create( client: Client, name: str, description: str, - team_id: str, visibility: ModelVisibility | None = None, ) -> Datacard: """Build a datacard from Bailo and upload it. @@ -54,7 +53,6 @@ def create( :param client: A client object used to interact with Bailo :param name: Name of datacard :param description: Description of datacard - :param team_id: A unique team ID :param visibility: Visibility of datacard, using ModelVisibility enum (e.g Public or Private), defaults to None :return: Datacard object """ @@ -62,7 +60,6 @@ def create( name=name, kind=EntryKind.DATACARD, description=description, - team_id=team_id, visibility=visibility, ) datacard_id = res["model"]["id"] diff --git a/lib/python/src/bailo/helper/model.py b/lib/python/src/bailo/helper/model.py index 9d1b9dfd6..d36db37d5 100644 --- a/lib/python/src/bailo/helper/model.py +++ b/lib/python/src/bailo/helper/model.py @@ -60,7 +60,6 @@ def create( client: Client, name: str, description: str, - team_id: str, visibility: ModelVisibility | None = None, ) -> Model: """Build a model from Bailo and upload it. @@ -68,7 +67,6 @@ def create( :param client: A client object used to interact with Bailo :param name: Name of model :param description: Description of model - :param team_id: A unique team ID :param visibility: Visibility of model, using ModelVisibility enum (e.g Public or Private), defaults to None :return: Model object """ @@ -76,7 +74,6 @@ def create( name=name, kind=EntryKind.MODEL, description=description, - team_id=team_id, visibility=visibility, ) model_id = res["model"]["id"] @@ -160,7 +157,6 @@ def from_mlflow( cls, client: Client, mlflow_uri: str, - team_id: str, name: str, schema_id: str = MinimalSchema.MODEL, version: str | None = None, @@ -171,7 +167,6 @@ def from_mlflow( :param client: A client object used to interact with Bailo :param mlflow_uri: MLFlow server URI - :param team_id: A unique team ID :param name: Name of model (on MLFlow). Same name will be used on Bailo :param schema_id: A unique schema ID, only required when files is True, defaults to minimal-general-v10 :param version: Specific MLFlow model version to import, defaults to None @@ -207,7 +202,6 @@ def from_mlflow( name=name, kind=EntryKind.MODEL, description=description, - team_id=team_id, visibility=visibility, ) model_id = bailo_res["model"]["id"] diff --git a/lib/python/src/bailo/helper/release.py b/lib/python/src/bailo/helper/release.py index fc5af4d3c..652cf36b9 100644 --- a/lib/python/src/bailo/helper/release.py +++ b/lib/python/src/bailo/helper/release.py @@ -62,7 +62,6 @@ def __init__( self.files = files self.images = images self.draft = draft - self.files = files @classmethod def create( diff --git a/lib/python/tests/conftest.py b/lib/python/tests/conftest.py index 8b71d6ed5..85dfeefae 100644 --- a/lib/python/tests/conftest.py +++ b/lib/python/tests/conftest.py @@ -37,7 +37,6 @@ def example_model(integration_client, metrics_schema): client=integration_client, name="Yolo-v4", description="You only look once!", - team_id="team_id", visibility=ModelVisibility.PUBLIC, ) model.card_from_schema(metrics_schema.schema_id) diff --git a/lib/python/tests/test_client.py b/lib/python/tests/test_client.py index 52a822e58..0067a84b2 100644 --- a/lib/python/tests/test_client.py +++ b/lib/python/tests/test_client.py @@ -17,7 +17,6 @@ def test_post_model(requests_mock): kind=EntryKind.MODEL, description="test", visibility=ModelVisibility.PUBLIC, - team_id="uncategorised", ) assert result == {"success": True} @@ -244,60 +243,6 @@ def test_get_model_user_roles(requests_mock): assert result == {"success": True} -def test_post_team(requests_mock): - requests_mock.post("https://example.com/api/v2/teams", json={"success": True}) - - client = Client("https://example.com") - result = client.post_team( - team_id="test_id", - name="test", - description="test", - ) - - assert result == {"success": True} - - -def test_get_all_teams(requests_mock): - requests_mock.get("https://example.com/api/v2/teams", json={"success": True}) - - client = Client("https://example.com") - result = client.get_all_teams() - - assert result == {"success": True} - - -def test_get_user_teams(requests_mock): - requests_mock.get("https://example.com/api/v2/teams/mine", json={"success": True}) - - client = Client("https://example.com") - result = client.get_user_teams() - - assert result == {"success": True} - - -def test_get_team(requests_mock): - requests_mock.get("https://example.com/api/v2/team/test_id", json={"success": True}) - - client = Client("https://example.com") - result = client.get_team( - team_id="test_id", - ) - - assert result == {"success": True} - - -def test_patch_team(requests_mock): - requests_mock.patch("https://example.com/api/v2/team/test_id", json={"success": True}) - - client = Client("https://example.com") - result = client.patch_team( - team_id="test_id", - name="name", - ) - - assert result == {"success": True} - - def test_get_access_request(requests_mock): requests_mock.get( "https://example.com/api/v2/model/test_id/access-request/test_id", diff --git a/lib/python/tests/test_datacard.py b/lib/python/tests/test_datacard.py index 4505dc60f..cf978b2ab 100644 --- a/lib/python/tests/test_datacard.py +++ b/lib/python/tests/test_datacard.py @@ -11,16 +11,15 @@ def test_datacard(local_datacard): @pytest.mark.integration @pytest.mark.parametrize( - ("name", "description", "team_id", "visibility"), + ("name", "description", "visibility"), [ - ("test-datacard", "test", "Uncategorised", ModelVisibility.PUBLIC), - ("test-datacard", "test", "Uncategorised", None), + ("test-datacard", "test", ModelVisibility.PUBLIC), + ("test-datacard", "test", None), ], ) def test_create_get_from_id_and_update( name: str, description: str, - team_id: str, visibility: ModelVisibility | None, integration_client: Client, ): @@ -29,7 +28,6 @@ def test_create_get_from_id_and_update( client=integration_client, name=name, description=description, - team_id=team_id, visibility=visibility, ) datacard.card_from_schema("minimal-data-card-v10") @@ -52,7 +50,6 @@ def test_get_and_update_latest_data_card(integration_client): client=integration_client, name="test-datacard", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) @@ -69,7 +66,6 @@ def get_data_card_without_creation(integration_client): client=integration_client, name="test-datacard", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) datacard.card_from_schema("minimal-data-card-v10") @@ -84,7 +80,6 @@ def test_get_model_as_datacard(integration_client): client=integration_client, name="test-model", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) diff --git a/lib/python/tests/test_model.py b/lib/python/tests/test_model.py index 3c001810f..b6e3d22f9 100644 --- a/lib/python/tests/test_model.py +++ b/lib/python/tests/test_model.py @@ -18,16 +18,15 @@ def test_create_experiment_from_model(local_model): @pytest.mark.integration @pytest.mark.parametrize( - ("name", "description", "team_id", "visibility"), + ("name", "description", "visibility"), [ - ("test-model", "test", "Uncategorised", ModelVisibility.PUBLIC), - ("test-model", "test", "Uncategorised", None), + ("test-model", "test", ModelVisibility.PUBLIC), + ("test-model", "test", None), ], ) def test_create_get_from_id_and_update( name: str, description: str, - team_id: str, visibility: ModelVisibility | None, integration_client: Client, ): @@ -36,7 +35,6 @@ def test_create_get_from_id_and_update( client=integration_client, name=name, description=description, - team_id=team_id, visibility=visibility, ) model.card_from_schema("minimal-general-v10") @@ -73,7 +71,6 @@ def test_get_and_update_latest_model_card(integration_client): client=integration_client, name="test-model", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) @@ -90,7 +87,6 @@ def get_model_card_without_creation(integration_client): client=integration_client, name="test-model", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) model.card_from_schema("minimal-general-v10") @@ -105,7 +101,6 @@ def test_get_releases(integration_client): client=integration_client, name="test-model", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) model.card_from_schema("minimal-general-v10") @@ -128,7 +123,6 @@ def test_create_release_without_model_card(integration_client): client=integration_client, name="test-model", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) @@ -142,7 +136,6 @@ def test_get_datacard_as_model(integration_client): client=integration_client, name="test-datacard", description="test", - team_id="Uncategorised", visibility=ModelVisibility.PUBLIC, ) @@ -190,7 +183,6 @@ def test_import_model_from_mlflow(integration_client, mlflow_model, request): model = Model.from_mlflow( client=integration_client, mlflow_uri=request.config.mlflow_uri, - team_id="Uncategorised", schema_id="minimal-general-v10", name=mlflow_model, ) @@ -205,7 +197,6 @@ def test_import_nonexistent_model_from_mlflow(integration_client, request): model = Model.from_mlflow( client=integration_client, mlflow_uri=request.config.mlflow_uri, - team_id="Uncategorised", schema_id="minimal-general-v10", name="fake-model-name", ) @@ -217,7 +208,6 @@ def test_import_model_files_no_run(integration_client, mlflow_model_no_run, requ model = Model.from_mlflow( client=integration_client, mlflow_uri=request.config.mlflow_uri, - team_id="Uncategorised", schema_id="minimal-general-v10", name=mlflow_model_no_run, ) @@ -229,7 +219,6 @@ def test_import_model_no_schema(integration_client, mlflow_model, request): model = Model.from_mlflow( client=integration_client, mlflow_uri=request.config.mlflow_uri, - team_id="Uncategorised", name=mlflow_model, ) From 7a7af17d420d395efcf03a09e3d3663493dd0b3a Mon Sep 17 00:00:00 2001 From: PE39806 <185931318+PE39806@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:06:38 +0000 Subject: [PATCH 2/4] BAI-1545 improve READMEs and spelling --- backend/docs/README.md | 100 ++++-------------- .../notebooks/experiment_tracking_demo.ipynb | 2 +- .../models_and_releases_demo_pytorch.ipynb | 4 +- lib/python/README.md | 21 +--- lib/python/src/bailo/core/client.py | 2 +- lib/python/src/bailo/helper/entry.py | 12 ++- lib/python/src/bailo/helper/model.py | 12 +-- lib/python/src/bailo/helper/release.py | 10 +- 8 files changed, 47 insertions(+), 116 deletions(-) diff --git a/backend/docs/README.md b/backend/docs/README.md index ae3ac5d4d..d85ca54b9 100644 --- a/backend/docs/README.md +++ b/backend/docs/README.md @@ -1,6 +1,6 @@ -# Bailo Python Client +# Bailo Python Client Documentation -A simple Python API Wrapper for Bailo +The documentation for [a simple Python API Wrapper for Bailo](lib/python/README.md)
    @@ -9,19 +9,15 @@ A simple Python API Wrapper for Bailo Table of Contents
    1. - Key Features -
    2. -
    3. - Installing -
    4. -
    5. - Getting Started -
    6. -
    7. - Development + Documentation
    @@ -29,81 +25,27 @@ A simple Python API Wrapper for Bailo
    -## Key Features - -- Uploading and downloading model binaries - -## Installing - -**Python 3.8.1 or higher is required** - -```bash -pip install bailo -``` - -## Getting Started - -```python -from bailo import Client, Model -client = Client("http://localhost:8080") - -# Create a model -yolo = Model.create( - client=client, - name="YoloV4", - description="You only look once!" -) - -yolo.card_from_schema("minimal-general-v10-beta") - -# Create a new release -my_release = yolo.create_release(version="0.1.0", - notes="Beta") - -# Upload a file to the release -with open("yolo.onnx") as f: - my_release.upload("yolo", f) -``` - ## Documentation Documentation is rendered with Sphinx and served [here](https://gchq.github.io/Bailo/docs/python/index.html). ### Building locally -From the docs directory run either `make html` or `make.bat` on Windows. This will build it in the backend directory by -default. - -## Development - -### Install and add precommit - -If already working on Bailo you may be prompted to overwrite Husky. Follow the instructions given by Git CLI. - -```bash -pip install pre-commit -pre-commit install -``` - -### Install the package locally - -```bash -pip install -e . -``` - -### Testing + +> [!IMPORTANT] +> Python 3.8.1 or higher is required + -The package uses Pytest to test packages. Tests can be ran accordingly from within this directory. Tests are split into -categories sections for automation purposes. +#### Prerequisites -In order to run integration tests make sure Bailo is running on `https://localhost:8080`: +From within the `backend/docs` directory: ```bash -pytest -m integration +pip install bailo -r requirements.txt +apt install -y pandoc ``` -Run all other tests: +#### Building -```bash -pytest -``` +From the docs directory run either `make html` (Linux & Mac) or `make.bat` (Windows). This will build it in the backend +directory by default. diff --git a/backend/docs/notebooks/experiment_tracking_demo.ipynb b/backend/docs/notebooks/experiment_tracking_demo.ipynb index 7b4c2da80..92858ea8b 100644 --- a/backend/docs/notebooks/experiment_tracking_demo.ipynb +++ b/backend/docs/notebooks/experiment_tracking_demo.ipynb @@ -218,7 +218,7 @@ "source": [ "## Importing existing experiments from MLFlow into Bailo\n", "\n", - "As previously mentioned, you can import existing experiments into the `Experiment()` class by using the `Experiment.from_mlflow()` method. You must provide the **MLFlow tracking URI** and the experiment ID. To get the experiement ID, go to the link provided in the cell \"Creating a dummy MLFlow experiment run\". In the details section you will see \"Experiment ID\", copy this ID and add it to the argument **experiment_id**. ![image.png](img/experiment_id.png)" + "As previously mentioned, you can import existing experiments into the `Experiment()` class by using the `Experiment.from_mlflow()` method. You must provide the **MLFlow tracking URI** and the experiment ID. To get the experiment ID, go to the link provided in the cell \"Creating a dummy MLFlow experiment run\". In the details section you will see \"Experiment ID\", copy this ID and add it to the argument **experiment_id**. ![image.png](img/experiment_id.png)" ] }, { diff --git a/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb b/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb index da94e4819..6dbedc9a2 100644 --- a/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb +++ b/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb @@ -151,7 +151,7 @@ "\n", "### Creating and populated a new model card with a template\n", "\n", - "When creating a model card from a template, we need to use a preexisting model card as our template. First we create a new model, to create the new model card we use the `card_from_template` method and pass our chosen template model's ID. \n", + "When creating a model card from a template, we need to use a pre-existing model card as our template. First we create a new model, to create the new model card we use the `card_from_template` method and pass our chosen template model's ID. \n", "\n", "\n" ] @@ -378,7 +378,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If the message \"**All keys matched successfully**\" is displayed, we have successfully initated our model." + "If the message \"**All keys matched successfully**\" is displayed, we have successfully initiated our model." ] }, { diff --git a/lib/python/README.md b/lib/python/README.md index 69bb5b164..a2bb0adc6 100644 --- a/lib/python/README.md +++ b/lib/python/README.md @@ -20,12 +20,7 @@ A simple Python API Wrapper for Bailo
  • Documentation
  • @@ -47,8 +42,10 @@ A simple Python API Wrapper for Bailo ## Installing + > [!IMPORTANT] > Python 3.8.1 or higher is required + ```bash pip install bailo @@ -84,17 +81,7 @@ Documentation is rendered with Sphinx and served [here](https://gchq.github.io/B ### Building locally -#### prerequisites - -From within the `backend/docs` directory: - -```bash -pip install bailo -r requirements.txt -apt install -y pandoc -``` - -From the docs directory run either `make html` or `make.bat` on Windows. This will build it in the backend directory by -default. +Refer to [backend/docs/README.md](backend/docs/README.md) for local build steps. ## Development diff --git a/lib/python/src/bailo/core/client.py b/lib/python/src/bailo/core/client.py index 1b442b09f..120c2aa29 100644 --- a/lib/python/src/bailo/core/client.py +++ b/lib/python/src/bailo/core/client.py @@ -179,7 +179,7 @@ def model_card_from_schema( def model_card_from_template(self, model_id: str, template_id: str | None): """Create a model card using a given template ID (previously created models, model ID) :param model_id: Unique model ID - :param tempate_id Previous model's unique ID to be used as template for new model card + :param template_id Previous model's unique ID to be used as template for new model card :return: JSON response object """ return self.agent.post( diff --git a/lib/python/src/bailo/helper/entry.py b/lib/python/src/bailo/helper/entry.py index 93f468d4a..185b247d3 100644 --- a/lib/python/src/bailo/helper/entry.py +++ b/lib/python/src/bailo/helper/entry.py @@ -1,11 +1,11 @@ from __future__ import annotations -from typing import Any import logging import warnings +from typing import Any from bailo.core.client import Client -from bailo.core.enums import EntryKind, ModelVisibility, MinimalSchema +from bailo.core.enums import EntryKind, MinimalSchema, ModelVisibility logger = logging.getLogger(__name__) @@ -17,7 +17,7 @@ class Entry: :param id: A unique ID for the entry :param name: Name of the entry :param description: Description of the entry - :param visibility: Visibility of model, using ModelVisiblility enum (i.e. Public or Private), defaults to None + :param visibility: Visibility of model, using ModelVisibility enum (i.e. Public or Private), defaults to None :param kind: Represents whether entry type (i.e. Model or Datacard) """ @@ -55,7 +55,7 @@ def update(self) -> None: logger.info(f"ID %s updated locally and on server.", self.id) - def card_from_schema(self, schema_id: str) -> None: + def card_from_schema(self, schema_id: str | None = None) -> None: """Create a card using a schema on Bailo. :param schema_id: A unique schema ID, defaults to None. If None, either minimal-general-v10 or minimal-data-card-v10 is used @@ -63,8 +63,10 @@ def card_from_schema(self, schema_id: str) -> None: if schema_id is None: if self.kind == EntryKind.MODEL: schema_id = MinimalSchema.MODEL - if self.kind == EntryKind.DATACARD: + elif self.kind == EntryKind.DATACARD: schema_id = MinimalSchema.DATACARD + else: + raise NotImplementedError(f"No default schema set for {self.kind=}") res = self.client.model_card_from_schema(model_id=self.id, schema_id=schema_id) self.__unpack_card(res["card"]) diff --git a/lib/python/src/bailo/helper/model.py b/lib/python/src/bailo/helper/model.py index d36db37d5..305b8a466 100644 --- a/lib/python/src/bailo/helper/model.py +++ b/lib/python/src/bailo/helper/model.py @@ -1,14 +1,14 @@ from __future__ import annotations +import logging import os import shutil import tempfile -from typing import Any -import logging import warnings +from typing import Any from bailo.core.client import Client -from bailo.core.enums import EntryKind, ModelVisibility, MinimalSchema +from bailo.core.enums import EntryKind, MinimalSchema, ModelVisibility from bailo.core.exceptions import BailoException from bailo.core.utils import NestedDict from bailo.helper.entry import Entry @@ -222,13 +222,13 @@ def from_mlflow( run_id = sel_model.run_id if run_id is None: raise BailoException( - "MLFlow model does not have an assosciated run_id, therefore artifacts cannot be transfered." + "MLFlow model does not have an associated run_id, therefore artifacts cannot be transferred." ) mlflow_run = mlflow_client.get_run(run_id) artifact_uri: str = str(mlflow_run.info.artifact_uri) if artifact_uri is None: - raise BailoException("Artifact URI could not be found, therefore artifacts cannot be transfered.") + raise BailoException("Artifact URI could not be found, therefore artifacts cannot be transferred.") if mlflow.artifacts.list_artifacts(artifact_uri=artifact_uri) is not None: temp_dir = os.path.join(tempfile.gettempdir(), "mlflow_model") @@ -336,7 +336,7 @@ def get_images(self): """ res = self.client.get_all_images(model_id=self.model_id) - logger.info(f"Images for %s retreived successfully.", self.model_id) + logger.info(f"Images for %s retrieved successfully.", self.model_id) return res["images"] diff --git a/lib/python/src/bailo/helper/release.py b/lib/python/src/bailo/helper/release.py index 652cf36b9..21af74b60 100644 --- a/lib/python/src/bailo/helper/release.py +++ b/lib/python/src/bailo/helper/release.py @@ -1,18 +1,18 @@ from __future__ import annotations -import os import fnmatch +import logging +import os import shutil from io import BytesIO from typing import Any -import logging -from tqdm import tqdm -from tqdm.utils import CallbackIOWrapper from bailo.core.client import Client from bailo.core.exceptions import BailoException from bailo.core.utils import NO_COLOR from semantic_version import Version +from tqdm import tqdm +from tqdm.utils import CallbackIOWrapper BLOCK_SIZE = 1024 logger = logging.getLogger(__name__) @@ -266,7 +266,7 @@ def upload(self, path: str, data: BytesIO | None = None) -> str: # type: ignore # If no datastream object provided name = os.path.split(path)[-1] if data is None: - # If we havent passed in a file object, we must create one from the path. + # If we haven't passed in a file object, we must create one from the path. # Check if file exists, if it does the zip required zip_required = not os.path.isfile(path) From 05228ad1bad0c70a186069df4305525ced8699e5 Mon Sep 17 00:00:00 2001 From: PE39806 <185931318+PE39806@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:10:20 +0000 Subject: [PATCH 3/4] BAI-1545 correct README links --- backend/docs/README.md | 2 +- lib/python/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/docs/README.md b/backend/docs/README.md index d85ca54b9..13d56c845 100644 --- a/backend/docs/README.md +++ b/backend/docs/README.md @@ -1,6 +1,6 @@ # Bailo Python Client Documentation -The documentation for [a simple Python API Wrapper for Bailo](lib/python/README.md) +The documentation for [a simple Python API Wrapper for Bailo](../../lib/python/README.md)
    diff --git a/lib/python/README.md b/lib/python/README.md index a2bb0adc6..33207a877 100644 --- a/lib/python/README.md +++ b/lib/python/README.md @@ -81,7 +81,7 @@ Documentation is rendered with Sphinx and served [here](https://gchq.github.io/B ### Building locally -Refer to [backend/docs/README.md](backend/docs/README.md) for local build steps. +Refer to [backend/docs/README.md](../../backend/docs/README.md) for local build steps. ## Development From b38b663f2907ecac059b82e5a187d2b53878fd0c Mon Sep 17 00:00:00 2001 From: PE39806 <185931318+PE39806@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:25:36 +0000 Subject: [PATCH 4/4] BAI-1545 improve grammar --- .../docs/notebooks/access_requests_demo.ipynb | 4 +- backend/docs/notebooks/datacards_demo.ipynb | 6 +- .../notebooks/experiment_tracking_demo.ipynb | 8 +- .../models_and_releases_demo_pytorch.ipynb | 10 +- backend/docs/notebooks/schemas_demo.ipynb | 390 +++++++++--------- 5 files changed, 209 insertions(+), 209 deletions(-) diff --git a/backend/docs/notebooks/access_requests_demo.ipynb b/backend/docs/notebooks/access_requests_demo.ipynb index ea46d9e01..9574736a1 100644 --- a/backend/docs/notebooks/access_requests_demo.ipynb +++ b/backend/docs/notebooks/access_requests_demo.ipynb @@ -78,7 +78,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In this section, we'll create a new access request using the `AccessRequest.create()` classmethod. On the Bailo service, an access request must consist of at least 3 parameters upon creation. These are **model_id**, **schema_id** and **metadata**. Below, we use the `Client()` object created before when instantiating the new `AccessRequest()` object. We'll also need to create a new model on the service, which our access request will be for." + "In this section, we will create a new access request using the `AccessRequest.create()` classmethod. On the Bailo service, an access request must consist of at least 3 parameters upon creation. These are **model_id**, **schema_id** and **metadata**. Below, we use the `Client()` object created before when instantiating the new `AccessRequest()` object. We will also need to create a new model on the service, which our access request will be for." ] }, { @@ -117,7 +117,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In this section, we'll retrieve our previous access request using the `AccessRequest.from_id()` classmethod. This will create your `AccessRequest()` object as before, but using existing information retrieved from the service." + "In this section, we will retrieve our previous access request using the `AccessRequest.from_id()` classmethod. This will create your `AccessRequest()` object as before, but using existing information retrieved from the service." ] }, { diff --git a/backend/docs/notebooks/datacards_demo.ipynb b/backend/docs/notebooks/datacards_demo.ipynb index 178b8267c..7ae0ad8d2 100644 --- a/backend/docs/notebooks/datacards_demo.ipynb +++ b/backend/docs/notebooks/datacards_demo.ipynb @@ -74,7 +74,7 @@ "\n", "### Creating and updating the base datacard\n", "\n", - "In this section, we'll create a new datacard using the `Datacard.create()` classmethod. On the Bailo service, a datacard must consist of at least 4 parameters upon creation. These are **name**, **description** and **visibility**. Below, we use the `Client()` object created before when instantiating the new `Datacard()` object. \n", + "In this section, we will create a new datacard using the `Datacard.create()` classmethod. On the Bailo service, a datacard must consist of at least 4 parameters upon creation. These are **name**, **description** and **visibility**. Below, we use the `Client()` object created before when instantiating the new `Datacard()` object. \n", "\n", "NOTE: This creates the datacard on your Bailo service too! The `datacard_id` is assigned by the backend, and we will use this later to retrieve the datacard. *Like with models on Bailo, the actual datacard has not been populated at this stage.*" ] @@ -131,7 +131,7 @@ "source": [ "If successful, the above will have created a new datacard, and the `data_card_version` attribute should be set to 1.\n", "\n", - "Next, we can populate the data using the `update_data_card()` method. This can be used any time you want to make changes, and the backend will create a new datacard version each time. We'll learn how to retrieve datacards later (either the latest, or a specific release).\n", + "Next, we can populate the data using the `update_data_card()` method. This can be used any time you want to make changes, and the backend will create a new datacard version each time. We will learn how to retrieve datacards later (either the latest, or a specific release).\n", "\n", "NOTE: Your datacard must match the schema, otherwise an error will be thrown." ] @@ -170,7 +170,7 @@ "\n", "### Using the .from_id() method\n", "\n", - "In this section, we'll retrieve our previous datacard using the `Datacard.from_id()` classmethod. This will create your `Datacard()` object as before, but using existing information retrieved from the service." + "In this section, we will retrieve our previous datacard using the `Datacard.from_id()` classmethod. This will create your `Datacard()` object as before, but using existing information retrieved from the service." ] }, { diff --git a/backend/docs/notebooks/experiment_tracking_demo.ipynb b/backend/docs/notebooks/experiment_tracking_demo.ipynb index 92858ea8b..0ac109e77 100644 --- a/backend/docs/notebooks/experiment_tracking_demo.ipynb +++ b/backend/docs/notebooks/experiment_tracking_demo.ipynb @@ -38,7 +38,7 @@ "\n", "### Connecting with Bailo\n", "\n", - "In order to create an `Experiment()` object, you'll first need to have a Bailo `Model()` object, and thus a defined `Client()` too. We learned how to do this in a previous notebook, but this time we'll create a new model with a custom schema which supports model metrics. *More on that later...*" + "In order to create an `Experiment()` object, you'll first need to have a Bailo `Model()` object, and thus a defined `Client()` too. We learned how to do this in a previous notebook, but this time we will create a new model with a custom schema which supports model metrics. *More on that later...*" ] }, { @@ -79,7 +79,7 @@ "source": [ "### Setting up MLFlow Tracking\n", "\n", - "In order to complete the integration element of this tutorial, we'll need to set up a local instance of MLFlow Tracking, and create a sample experiment run. *This will not contain any actual model training and is only to demonstrate the functionality of Bailo.*\n", + "In order to complete the integration element of this tutorial, we will need to set up a local instance of MLFlow Tracking, and create a sample experiment run. *This will not contain any actual model training and is only to demonstrate the functionality of Bailo.*\n", "\n", "Run `mlflow ui` on the command line. Typically this will run on **localhost:5000** and the UI can be accessed on a browser" ] @@ -156,7 +156,7 @@ "\n", "You can run experiments directly using the Bailo python client as follows.\n", "\n", - "**NOTE**: This will only work for sequential experiment runs, so if you're running experiments in parallel then it would be better to use **MLFlow Tracking**. We'll learn how to import completed experiments from MLFlow in the next section." + "**NOTE**: This will only work for sequential experiment runs, so if you're running experiments in parallel then it would be better to use **MLFlow Tracking**. We will learn how to import completed experiments from MLFlow in the next section." ] }, { @@ -218,7 +218,7 @@ "source": [ "## Importing existing experiments from MLFlow into Bailo\n", "\n", - "As previously mentioned, you can import existing experiments into the `Experiment()` class by using the `Experiment.from_mlflow()` method. You must provide the **MLFlow tracking URI** and the experiment ID. To get the experiment ID, go to the link provided in the cell \"Creating a dummy MLFlow experiment run\". In the details section you will see \"Experiment ID\", copy this ID and add it to the argument **experiment_id**. ![image.png](img/experiment_id.png)" + "You can import existing experiments into the `Experiment()` class by using the `Experiment.from_mlflow()` method. You must provide the **MLFlow tracking URI** and the experiment ID. To get the experiment ID, go to the link provided in the cell \"Creating a dummy MLFlow experiment run\". In the details section you will see \"Experiment ID\", copy this ID and add it to the argument **experiment_id**. ![image.png](img/experiment_id.png)" ] }, { diff --git a/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb b/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb index 6dbedc9a2..988efe5ab 100644 --- a/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb +++ b/backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb @@ -91,7 +91,7 @@ "\n", "### Creating and updating the base model\n", "\n", - "In this section, we'll create a new model representing ResNet-50 using the `Model.create()` classmethod. On the Bailo service, a model must consist of at least 4 parameters upon creation. These are **name**, **description** and **visibility**. Other attributes like model cards, files, or releases are added later on. Below, we use the `Client()` object created before when instantiating the new `Model()` object. \n", + "In this section, we will create a new model representing ResNet-50 using the `Model.create()` classmethod. On the Bailo service, a model must consist of at least 4 parameters upon creation. These are **name**, **description** and **visibility**. Other attributes like model cards, files, or releases are added later on. Below, we use the `Client()` object created before when instantiating the new `Model()` object. \n", "\n", "NOTE: This creates the model on your Bailo service too! The `model_id` is assigned by the backend, and we will use this later to retrieve the model." ] @@ -180,7 +180,7 @@ "source": [ "If successful, the above will have created a new model card, and the `model_card_version` attribute should be set to 1.\n", "\n", - "Next, we can populate the model card using the `update_model_card()` method. This can be used any time you want to make changes, and the backend will create a new model card version each time. We'll learn how to retrieve model cards later (either the latest, or a specific release).\n", + "Next, we can populate the model card using the `update_model_card()` method. This can be used any time you want to make changes, and the backend will create a new model card version each time. We will learn how to retrieve model cards later (either the latest, or a specific release).\n", "\n", "NOTE: Your model card must match the schema, otherwise an error will be thrown." ] @@ -220,7 +220,7 @@ "\n", "### Using the .from_id() method\n", "\n", - "In this section, we'll retrieve our previous model using the `Model.from_id()` classmethod. This will create your `Model()` object as before, but using existing information retrieved from the service." + "In this section, we will retrieve our previous model using the `Model.from_id()` classmethod. This will create your `Model()` object as before, but using existing information retrieved from the service." ] }, { @@ -337,7 +337,7 @@ "source": [ "### Downloading weights from the release\n", "\n", - "Similarly you can also download specific files from release using the `download()` method. In this case, we'll write them to a new file: **bailo_resnet50_weights.pth**. **NOTE**: `filename` refers to the filename on Bailo, and `path` is the local destination for your download.\n", + "You can also download specific files from release using the `download()` method. In this case, we will write them to a new file: **bailo_resnet50_weights.pth**. **NOTE**: `filename` refers to the filename on Bailo, and `path` is the local destination for your download.\n", "\n", "In addition to this, you can also use the `download_all()` method by providing a local directory path as `path`. By default, this will download all files, but you can provide `include` and `exclude` lists, e.g. `include=[\"*.txt\", \"*.json\"]` to only include TXT or JSON files. " ] @@ -400,7 +400,7 @@ "* Libraries used for the model (e.g. PyTorch).\n", "* Model card search (string to be found in model cards).\n", "\n", - "In the below example, we'll just search for all models with no filters." + "In the below example, we will just search for all models with no filters." ] }, { diff --git a/backend/docs/notebooks/schemas_demo.ipynb b/backend/docs/notebooks/schemas_demo.ipynb index 881da6ad0..e4eee22fc 100644 --- a/backend/docs/notebooks/schemas_demo.ipynb +++ b/backend/docs/notebooks/schemas_demo.ipynb @@ -1,198 +1,198 @@ { - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Managing Schemas" - ] + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Managing Schemas" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Bailo python client enables intuitive interaction with the Bailo service, from within a python environment. This example notebook will run through the following concepts:\n", + "\n", + "* Creating a new schema on Bailo.\n", + "* Retrieving schemas from the service.\n", + "\n", + "Prerequisites:\n", + "\n", + "* Python 3.8.1 or higher (including a notebook environment for this demo).\n", + "* A local or remote Bailo service (see https://github.com/gchq/Bailo)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Bailo python client is split into two sub-packages: **core** and **helper**.\n", + "\n", + "* **Core:** For direct interactions with the service endpoints.\n", + "* **Helper:** For more intuitive interactions with the service, using classes (e.g. Model) to handle operations.\n", + "\n", + "In order to create helper classes, you will first need to instantiate a `Client()` object from the core. By default, this object will not support any authentication. However, Bailo also supports PKI authentication, which you can use from Python by passing a `PkiAgent()` object into the `Client()` object when you instantiate it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Necessary import statements\n", + "\n", + "from bailo import Client, Schema, SchemaKind\n", + "import random\n", + "import string\n", + "\n", + "# Instantiating the PkiAgent(), if using.\n", + "# agent = PkiAgent(cert='', key='', auth='')\n", + "\n", + "# Instantiating the Bailo client\n", + "\n", + "client = Client(\"http://127.0.0.1:8080\") # <- INSERT BAILO URL (if not hosting locally)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a new schema" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this section, we will create a new schema using the `Schema.create()` classmethod. On the Bailo service, a schema must consist of 5 parameters upon creation. These are **schema_id**, **name**, **description**, **kind** and **json_schema**. Below, we use the `Client()` object created before when instantiating the new `Schema()` object. In this instance, **schema_id** is generated using a random generator function, however any unique identifier would suffice.\n", + "\n", + "NOTE: This creates the schema on your Bailo service too! This operation is typically reserved for administrators." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def random_generator(N=10):\n", + " return \"\".join(random.choices(string.ascii_uppercase + string.digits, k=N))\n", + "\n", + "json_schema = {\n", + " \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"overview\": {\n", + " \"title\": \"Overview\",\n", + " \"description\": \"Summary of the model functionality.\",\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"modelOverview\": {\n", + " \"title\": \"What does the model do?\",\n", + " \"description\": \"A description of what the model does.\",\n", + " \"type\": \"string\",\n", + " \"minLength\": 1,\n", + " \"maxLength\": 5000,\n", + " },\n", + " \"tags\": {\n", + " \"title\": \"Descriptive tags for the model.\",\n", + " \"description\": \"These tags will be searchable and will help others find this model.\",\n", + " \"type\": \"array\",\n", + " \"widget\": \"tagSelectorBeta\",\n", + " \"items\": {\"type\": \"string\"},\n", + " \"uniqueItems\": True,\n", + " },\n", + " },\n", + " \"required\": [],\n", + " \"additionalProperties\": False,\n", + " },\n", + " },\n", + " \"required\": [],\n", + " \"additionalProperties\": False,\n", + "}\n", + "\n", + "schema = Schema.create(\n", + " client=client,\n", + " schema_id=random_generator(),\n", + " name=\"Test\",\n", + " description=\"Example Description\",\n", + " kind=SchemaKind.MODEL,\n", + " json_schema=json_schema\n", + ")\n", + "\n", + "schema_id = schema.schema_id " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Retrieving an existing schema" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using the .from_id() method" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this section, we will retrieve our previous schema using the `Schema.from_id()` classmethod. This will create your `Schema()` object as before, but using existing information retrieved from the service." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "schema = Schema.from_id(client=client, schema_id=schema_id)\n", + "\n", + "schema.description" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python" + }, + "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.10.12" + }, + "orig_nbformat": 4 }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Bailo python client enables intuitive interaction with the Bailo service, from within a python environment. This example notebook will run through the following concepts:\n", - "\n", - "* Creating a new schema on Bailo.\n", - "* Retrieving schemas from the service.\n", - "\n", - "Prerequisites:\n", - "\n", - "* Python 3.8.1 or higher (including a notebook environment for this demo).\n", - "* A local or remote Bailo service (see https://github.com/gchq/Bailo)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Introduction" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Bailo python client is split into two sub-packages: **core** and **helper**.\n", - "\n", - "* **Core:** For direct interactions with the service endpoints.\n", - "* **Helper:** For more intuitive interactions with the service, using classes (e.g. Model) to handle operations.\n", - "\n", - "In order to create helper classes, you will first need to instantiate a `Client()` object from the core. By default, this object will not support any authentication. However, Bailo also supports PKI authentication, which you can use from Python by passing a `PkiAgent()` object into the `Client()` object when you instantiate it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Necessary import statements\n", - "\n", - "from bailo import Client, Schema, SchemaKind\n", - "import random\n", - "import string\n", - "\n", - "# Instantiating the PkiAgent(), if using.\n", - "# agent = PkiAgent(cert='', key='', auth='')\n", - "\n", - "# Instantiating the Bailo client\n", - "\n", - "client = Client(\"http://127.0.0.1:8080\") # <- INSERT BAILO URL (if not hosting locally)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a new schema" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this section, we'll create a new schema using the `Schema.create()` classmethod. On the Bailo service, a schema must consist of 5 parameters upon creation. These are **schema_id**, **name**, **description**, **kind** and **json_schema**. Below, we use the `Client()` object created before when instantiating the new `Schema()` object. In this instance, **schema_id** is generated using a random generator function, however any unique identifier would suffice.\n", - "\n", - "NOTE: This creates the schema on your Bailo service too! This operation is typically reserved for administrators." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def random_generator(N=10):\n", - " return \"\".join(random.choices(string.ascii_uppercase + string.digits, k=N))\n", - "\n", - "json_schema = {\n", - " \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"overview\": {\n", - " \"title\": \"Overview\",\n", - " \"description\": \"Summary of the model functionality.\",\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"modelOverview\": {\n", - " \"title\": \"What does the model do?\",\n", - " \"description\": \"A description of what the model does.\",\n", - " \"type\": \"string\",\n", - " \"minLength\": 1,\n", - " \"maxLength\": 5000,\n", - " },\n", - " \"tags\": {\n", - " \"title\": \"Descriptive tags for the model.\",\n", - " \"description\": \"These tags will be searchable and will help others find this model.\",\n", - " \"type\": \"array\",\n", - " \"widget\": \"tagSelectorBeta\",\n", - " \"items\": {\"type\": \"string\"},\n", - " \"uniqueItems\": True,\n", - " },\n", - " },\n", - " \"required\": [],\n", - " \"additionalProperties\": False,\n", - " },\n", - " },\n", - " \"required\": [],\n", - " \"additionalProperties\": False,\n", - "}\n", - "\n", - "schema = Schema.create(\n", - " client=client,\n", - " schema_id=random_generator(),\n", - " name=\"Test\",\n", - " description=\"Example Description\",\n", - " kind=SchemaKind.MODEL,\n", - " json_schema=json_schema\n", - ")\n", - "\n", - "schema_id = schema.schema_id " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Retrieving an existing schema" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using the .from_id() method" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this section, we'll retrieve our previous schema using the `Schema.from_id()` classmethod. This will create your `Schema()` object as before, but using existing information retrieved from the service." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "schema = Schema.from_id(client=client, schema_id=schema_id)\n", - "\n", - "schema.description" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python" - }, - "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.10.12" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 + "nbformat": 4, + "nbformat_minor": 2 }