{ "cells": [ { "cell_type": "markdown", "id": "e54b7f66a06b", "metadata": {}, "source": "# EnergyDB — Quickstart\n\nDeclare your energy portfolio — sites, turbines, arrays, batteries — as plain Python objects. One call persists the whole tree; one fluent line reads it back across every asset." }, { "cell_type": "markdown", "id": "6bd0b3f4b536", "metadata": {}, "source": "## 1. Setup\n\n`Client` reads connection settings from `TIMEDB_PG_DSN` (Postgres for hierarchy + series catalog) and `TIMEDB_CH_URL` (ClickHouse for time-series values)." }, { "cell_type": "code", "execution_count": 1, "id": "05e4db51372d", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:51.064899Z", "iopub.status.busy": "2026-05-18T08:28:51.063108Z", "iopub.status.idle": "2026-05-18T08:28:51.071057Z", "shell.execute_reply": "2026-05-18T08:28:51.070311Z" } }, "outputs": [], "source": [ "try:\n", " import urllib.request\n", "\n", " import google.colab # noqa: F401\n", "\n", " urllib.request.urlretrieve(\n", " \"https://raw.githubusercontent.com/rebase-energy/energydb/main/examples/colab_setup.py\", \"/tmp/colab_setup.py\"\n", " )\n", " exec(open(\"/tmp/colab_setup.py\").read())\n", "except ImportError:\n", " pass" ] }, { "cell_type": "code", "execution_count": 2, "id": "5fd223a88e59", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:51.073285Z", "iopub.status.busy": "2026-05-18T08:28:51.072994Z", "iopub.status.idle": "2026-05-18T08:28:53.057609Z", "shell.execute_reply": "2026-05-18T08:28:53.056353Z" } }, "outputs": [], "source": [ "from datetime import UTC, datetime\n", "\n", "import energydb as edb\n", "import pandas as pd\n", "\n", "client = edb.Client()\n", "client.delete() # clean slate\n", "client.create()" ] }, { "cell_type": "markdown", "id": "53581bd14c0f", "metadata": {}, "source": "## 2. Declare your portfolio\n\nA `Portfolio` is just a Python object — sites, turbines, arrays, batteries — with metadata-only `TimeSeries` declarations naming the series each node will hold. Build it top-down; the tree printout below is your structural deliverable." }, { "cell_type": "code", "execution_count": 3, "id": "bf2dee4a16af", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.061378Z", "iopub.status.busy": "2026-05-18T08:28:53.060876Z", "iopub.status.idle": "2026-05-18T08:28:53.072209Z", "shell.execute_reply": "2026-05-18T08:28:53.071136Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Portfolio('my-portfolio')\n", "├── Site('Offshore-1')\n", "│ ├── WindTurbine('T01')\n", "│ └── WindTurbine('T02')\n", "└── Site('Rooftop-1')\n", " ├── PVSystem('PV01')\n", " │ └── PVArray('Array-1')\n", " └── Battery('B01')\n" ] } ], "source": [ "# Step 1 — declare the series this turbine will hold (metadata only; data lands later via write).\n", "power_actual = edb.TimeSeries(name=\"power\", unit=\"MW\", data_type=edb.DataType.ACTUAL)\n", "wind_speed = edb.TimeSeries(name=\"wind_speed\", unit=\"m/s\", data_type=edb.DataType.ACTUAL)\n", "power_forecast = edb.TimeSeries(\n", " name=\"power\",\n", " unit=\"MW\",\n", " data_type=edb.DataType.FORECAST,\n", " timeseries_type=edb.TimeSeriesType.OVERLAPPING,\n", ")\n", "\n", "# Step 2 — wrap them on a WindTurbine (and a sibling with a single declaration inline).\n", "t01 = edb.wind.WindTurbine(\n", " name=\"T01\",\n", " lat=55.01,\n", " lon=3.02,\n", " capacity=3.5,\n", " hub_height=80,\n", " timeseries=[power_actual, wind_speed, power_forecast],\n", ")\n", "t02 = edb.wind.WindTurbine(\n", " name=\"T02\",\n", " lat=55.01,\n", " lon=3.04,\n", " capacity=3.5,\n", " hub_height=80,\n", " timeseries=[edb.TimeSeries(name=\"power\", unit=\"MW\", data_type=edb.DataType.ACTUAL)],\n", ")\n", "\n", "# Step 3 — group the turbines under a Site that carries its own series too.\n", "offshore_1 = edb.Site(\n", " name=\"Offshore-1\",\n", " lat=55.0,\n", " lon=3.0,\n", " timeseries=[edb.TimeSeries(name=\"demand\", unit=\"MW\", data_type=edb.DataType.ACTUAL)],\n", " members=[t01, t02],\n", ")\n", "\n", "# Step 4 — same bottom-up pattern for the second site: array → system, plus a battery, then site.\n", "pv_array = edb.solar.PVArray(\n", " name=\"Array-1\",\n", " capacity=10,\n", " surface_tilt=25,\n", " surface_azimuth=180,\n", " timeseries=[edb.TimeSeries(name=\"power\", unit=\"MW\", data_type=edb.DataType.ACTUAL)],\n", ")\n", "pv_system = edb.solar.PVSystem(name=\"PV01\", members=[pv_array])\n", "battery = edb.battery.Battery(name=\"B01\", storage_capacity=1000, max_charge=500)\n", "rooftop_1 = edb.Site(name=\"Rooftop-1\", lat=52.0, lon=4.5, members=[pv_system, battery])\n", "\n", "# Step 5 — assemble the portfolio.\n", "portfolio = edb.Portfolio(name=\"my-portfolio\", members=[offshore_1, rooftop_1])\n", "print(portfolio.to_tree())" ] }, { "cell_type": "markdown", "id": "c8f0d0db9d3e", "metadata": {}, "source": "## 3. Persist and write data\n\n`register_tree` creates every node, edge, and series declaration in one PG transaction — the UUIDs you set in Python become the row primary keys. Then `write` streams in the actual values, one short call per series." }, { "cell_type": "code", "execution_count": 4, "id": "2e453ffcee6f", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.077526Z", "iopub.status.busy": "2026-05-18T08:28:53.077308Z", "iopub.status.idle": "2026-05-18T08:28:53.360951Z", "shell.execute_reply": "2026-05-18T08:28:53.360373Z" } }, "outputs": [ { "data": { "text/plain": [ "58297317239439162" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "client.register_tree(portfolio)\n", "\n", "# A day of synthetic hourly values, varied by base level per series.\n", "start = datetime(2026, 1, 1, tzinfo=UTC)\n", "end = datetime(2026, 1, 2, tzinfo=UTC)\n", "hours = pd.date_range(start, end, freq=\"1h\", inclusive=\"left\", tz=\"UTC\")\n", "\n", "\n", "def synthetic_dataframe(base):\n", " return pd.DataFrame({\"valid_time\": hours, \"value\": [base + 0.05 * i for i in range(len(hours))]})\n", "\n", "\n", "offshore = client.get_node(\"my-portfolio\", \"Offshore-1\")\n", "offshore.write(synthetic_dataframe(12.0), name=\"demand\", data_type=\"actual\")\n", "offshore.get_node(\"T01\").write(synthetic_dataframe(2.5), name=\"power\", data_type=\"actual\")\n", "offshore.get_node(\"T01\").write(synthetic_dataframe(7.5), name=\"wind_speed\", data_type=\"actual\")\n", "offshore.get_node(\"T02\").write(synthetic_dataframe(2.7), name=\"power\", data_type=\"actual\")\n", "\n", "rooftop = client.get_node(\"my-portfolio\", \"Rooftop-1\", \"PV01\", \"Array-1\")\n", "rooftop.write(synthetic_dataframe(5.0), name=\"power\", data_type=\"actual\")" ] }, { "cell_type": "markdown", "id": "97d230cb869c", "metadata": {}, "source": "## 4. Read across the portfolio\n\nThe pay-off. One fluent scope, many routings — full subtree, single-asset drill-down, series-name filter, node-type filter. Each line below is one indexed query." }, { "cell_type": "code", "execution_count": 5, "id": "d562d4551e2f", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.363285Z", "iopub.status.busy": "2026-05-18T08:28:53.363125Z", "iopub.status.idle": "2026-05-18T08:28:53.498349Z", "shell.execute_reply": "2026-05-18T08:28:53.496914Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "shape: (72, 5)
valid_timevaluepathdata_typename
datetime[μs, UTC]f64strstrstr
2026-01-01 00:00:00 UTC2.5"my-portfolio/Offshore-1/T01""actual""power"
2026-01-01 01:00:00 UTC2.55"my-portfolio/Offshore-1/T01""actual""power"
2026-01-01 02:00:00 UTC2.6"my-portfolio/Offshore-1/T01""actual""power"
2026-01-01 03:00:00 UTC2.65"my-portfolio/Offshore-1/T01""actual""power"
2026-01-01 04:00:00 UTC2.7"my-portfolio/Offshore-1/T01""actual""power"
2026-01-01 19:00:00 UTC5.95"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 20:00:00 UTC6.0"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 21:00:00 UTC6.05"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 22:00:00 UTC6.1"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 23:00:00 UTC6.15"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
" ], "text/plain": [ "shape: (72, 5)\n", "┌─────────────────────────┬───────┬─────────────────────────────────┬───────────┬───────┐\n", "│ valid_time ┆ value ┆ path ┆ data_type ┆ name │\n", "│ --- ┆ --- ┆ --- ┆ --- ┆ --- │\n", "│ datetime[μs, UTC] ┆ f64 ┆ str ┆ str ┆ str │\n", "╞═════════════════════════╪═══════╪═════════════════════════════════╪═══════════╪═══════╡\n", "│ 2026-01-01 00:00:00 UTC ┆ 2.5 ┆ my-portfolio/Offshore-1/T01 ┆ actual ┆ power │\n", "│ 2026-01-01 01:00:00 UTC ┆ 2.55 ┆ my-portfolio/Offshore-1/T01 ┆ actual ┆ power │\n", "│ 2026-01-01 02:00:00 UTC ┆ 2.6 ┆ my-portfolio/Offshore-1/T01 ┆ actual ┆ power │\n", "│ 2026-01-01 03:00:00 UTC ┆ 2.65 ┆ my-portfolio/Offshore-1/T01 ┆ actual ┆ power │\n", "│ 2026-01-01 04:00:00 UTC ┆ 2.7 ┆ my-portfolio/Offshore-1/T01 ┆ actual ┆ power │\n", "│ … ┆ … ┆ … ┆ … ┆ … │\n", "│ 2026-01-01 19:00:00 UTC ┆ 5.95 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 20:00:00 UTC ┆ 6.0 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 21:00:00 UTC ┆ 6.05 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 22:00:00 UTC ┆ 6.1 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 23:00:00 UTC ┆ 6.15 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "└─────────────────────────┴───────┴─────────────────────────────────┴───────────┴───────┘" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "portfolio = client.get_node(\"my-portfolio\")\n", "portfolio.read(data_type=\"actual\", start_valid=start) # everything — every series on every node\n", "df = portfolio.read(data_type=\"actual\", name=\"power\", start_valid=start) # only 'power' values\n", "portfolio.where(type=\"WindTurbine\").read(data_type=\"actual\", name=\"power\") # turbines only\n", "portfolio.get_node(\"Offshore-1\", \"T01\").read(data_type=\"actual\", name=\"wind_speed\") # one asset, one series\n", "df" ] }, { "cell_type": "markdown", "id": "2ef5ad382526", "metadata": {}, "source": [ "## 5. Reconstruct the tree\n", "\n", "Pull the whole portfolio back as an EDM tree — same UUIDs, ready for inspection or in-memory edits." ] }, { "cell_type": "code", "execution_count": 6, "id": "ad211bb04d10", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.501258Z", "iopub.status.busy": "2026-05-18T08:28:53.500987Z", "iopub.status.idle": "2026-05-18T08:28:53.511182Z", "shell.execute_reply": "2026-05-18T08:28:53.510376Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Portfolio('my-portfolio')\n", "├── Site('Offshore-1')\n", "│ ├── WindTurbine('T01')\n", "│ └── WindTurbine('T02')\n", "└── Site('Rooftop-1')\n", " ├── PVSystem('PV01')\n", " │ └── PVArray('Array-1')\n", " └── Battery('B01')\n" ] } ], "source": [ "tree = client.get_tree(\"my-portfolio\", include_series=True)\n", "print(tree)" ] }, { "cell_type": "markdown", "id": "efbc64d75cd7", "metadata": {}, "source": [ "## 6. Modify existing nodes and edges\n", "\n", "`register_tree` is create-only — to edit what's already persisted, use scope mutators (`rename`, `update`, `delete`, `move_to`, `add`) addressed by path or uuid. The node keeps its uuid, so attached series stay attached. Every mutator accepts `dry_run=True` to return a `TreeDiff` preview without committing. `client.transaction()` batches several mutations into one atomic commit; `.commit()` is required, exiting without it raises." ] }, { "cell_type": "code", "execution_count": 7, "id": "5211112b55cd", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.514048Z", "iopub.status.busy": "2026-05-18T08:28:53.513054Z", "iopub.status.idle": "2026-05-18T08:28:53.541698Z", "shell.execute_reply": "2026-05-18T08:28:53.540970Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Renamed: Offshore-Renamed (uuid=019e3a33-c9cd-7bfa-a262-1ba2f3cafbd5)\n" ] } ], "source": [ "client.get_node(\"my-portfolio\", \"Offshore-1\").rename(\"Offshore-Renamed\")\n", "client.get_node(\"my-portfolio\", \"Offshore-Renamed\", \"T01\").update({\"capacity\": 4.0})\n", "client.get_node(\"my-portfolio\", \"Offshore-Renamed\", \"T02\").delete()\n", "\n", "# Add a new turbine under Offshore-Renamed; .add() returns a scope at the new node.\n", "client.get_node(\"my-portfolio\", \"Offshore-Renamed\").add(edb.wind.WindTurbine(name=\"T03\", capacity=4.5))\n", "\n", "renamed = client.get_node(\"my-portfolio\", \"Offshore-Renamed\").get()\n", "print(f\"Renamed: {renamed.name} (uuid={renamed.id})\")" ] }, { "cell_type": "code", "execution_count": 8, "id": "8dcc3c03", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.543828Z", "iopub.status.busy": "2026-05-18T08:28:53.543560Z", "iopub.status.idle": "2026-05-18T08:28:53.552938Z", "shell.execute_reply": "2026-05-18T08:28:53.552184Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "~ WindTurbine 'T01' [hub_height: 80 → 95]\n", "hub_height still: 80\n" ] } ], "source": [ "diff = client.get_node(\"my-portfolio\", \"Offshore-Renamed\", \"T01\").update({\"hub_height\": 95}, dry_run=True)\n", "diff.render()\n", "\n", "# DB is unchanged after a dry_run.\n", "print(\"hub_height still:\", client.get_node(\"my-portfolio\", \"Offshore-Renamed\", \"T01\").get().hub_height)" ] }, { "cell_type": "code", "execution_count": 9, "id": "c1021287", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.554319Z", "iopub.status.busy": "2026-05-18T08:28:53.554142Z", "iopub.status.idle": "2026-05-18T08:28:53.570586Z", "shell.execute_reply": "2026-05-18T08:28:53.569894Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "~ WindTurbine 'T01' [hub_height: 80 → 95]\n", "~ WindTurbine 'T02' [rename 'T03' → 'T02']\n", "~ Battery 'B01' [moved (parent 019e3a33-c9d1-7c29-ad0e-cc9d87e6152e → 019e3a33-c9cd-7bfa-a262-1ba2f3cafbd5)]\n" ] } ], "source": [ "with client.transaction() as txn:\n", " txn.get_node(\"my-portfolio\", \"Offshore-Renamed\", \"T01\").update({\"hub_height\": 95})\n", " txn.get_node(\"my-portfolio\", \"Offshore-Renamed\", \"T03\").rename(\"T02\")\n", " txn.get_node(\"my-portfolio\", \"Rooftop-1\", \"B01\").move_to((\"my-portfolio\", \"Offshore-Renamed\"))\n", " txn.preview().render()\n", " txn.commit()" ] }, { "cell_type": "markdown", "id": "cf462d3535b6", "metadata": {}, "source": [ "## 7. Bulk manifest I/O\n", "\n", "A single manifest DataFrame fans out a read across many series at once — routing column is `path`, `node_uuid`, or `edge_uuid`, detected automatically." ] }, { "cell_type": "code", "execution_count": 10, "id": "40558aac4ddd", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.571951Z", "iopub.status.busy": "2026-05-18T08:28:53.571795Z", "iopub.status.idle": "2026-05-18T08:28:53.598281Z", "shell.execute_reply": "2026-05-18T08:28:53.597433Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "shape: (48, 5)
valid_timevaluepathdata_typename
datetime[μs, UTC]f64strstrstr
2026-01-01 00:00:00 UTC2.5"my-portfolio/Offshore-Renamed/…"actual""power"
2026-01-01 01:00:00 UTC2.55"my-portfolio/Offshore-Renamed/…"actual""power"
2026-01-01 02:00:00 UTC2.6"my-portfolio/Offshore-Renamed/…"actual""power"
2026-01-01 03:00:00 UTC2.65"my-portfolio/Offshore-Renamed/…"actual""power"
2026-01-01 04:00:00 UTC2.7"my-portfolio/Offshore-Renamed/…"actual""power"
2026-01-01 19:00:00 UTC5.95"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 20:00:00 UTC6.0"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 21:00:00 UTC6.05"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 22:00:00 UTC6.1"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
2026-01-01 23:00:00 UTC6.15"my-portfolio/Rooftop-1/PV01/Ar…"actual""power"
" ], "text/plain": [ "shape: (48, 5)\n", "┌─────────────────────────┬───────┬─────────────────────────────────┬───────────┬───────┐\n", "│ valid_time ┆ value ┆ path ┆ data_type ┆ name │\n", "│ --- ┆ --- ┆ --- ┆ --- ┆ --- │\n", "│ datetime[μs, UTC] ┆ f64 ┆ str ┆ str ┆ str │\n", "╞═════════════════════════╪═══════╪═════════════════════════════════╪═══════════╪═══════╡\n", "│ 2026-01-01 00:00:00 UTC ┆ 2.5 ┆ my-portfolio/Offshore-Renamed/… ┆ actual ┆ power │\n", "│ 2026-01-01 01:00:00 UTC ┆ 2.55 ┆ my-portfolio/Offshore-Renamed/… ┆ actual ┆ power │\n", "│ 2026-01-01 02:00:00 UTC ┆ 2.6 ┆ my-portfolio/Offshore-Renamed/… ┆ actual ┆ power │\n", "│ 2026-01-01 03:00:00 UTC ┆ 2.65 ┆ my-portfolio/Offshore-Renamed/… ┆ actual ┆ power │\n", "│ 2026-01-01 04:00:00 UTC ┆ 2.7 ┆ my-portfolio/Offshore-Renamed/… ┆ actual ┆ power │\n", "│ … ┆ … ┆ … ┆ … ┆ … │\n", "│ 2026-01-01 19:00:00 UTC ┆ 5.95 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 20:00:00 UTC ┆ 6.0 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 21:00:00 UTC ┆ 6.05 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 22:00:00 UTC ┆ 6.1 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "│ 2026-01-01 23:00:00 UTC ┆ 6.15 ┆ my-portfolio/Rooftop-1/PV01/Ar… ┆ actual ┆ power │\n", "└─────────────────────────┴───────┴─────────────────────────────────┴───────────┴───────┘" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "manifest = pd.DataFrame(\n", " [\n", " {\"path\": \"my-portfolio/Offshore-Renamed/T01\", \"data_type\": \"actual\", \"name\": \"power\"},\n", " {\"path\": \"my-portfolio/Rooftop-1/PV01/Array-1\", \"data_type\": \"actual\", \"name\": \"power\"},\n", " ]\n", ")\n", "\n", "df = client.read(manifest, start_valid=start)\n", "df" ] }, { "cell_type": "markdown", "id": "18489e0af9eb", "metadata": {}, "source": [ "## 8. Edges\n", "\n", "Lines, links, and pipes link two nodes. Pass the endpoint nodes directly — the edge captures their `.id`. Edges hold their own time series the same way nodes do (`register_series` → `write` → `read`)." ] }, { "cell_type": "code", "execution_count": 11, "id": "8d62098cdcb8", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.599760Z", "iopub.status.busy": "2026-05-18T08:28:53.599608Z", "iopub.status.idle": "2026-05-18T08:28:53.618920Z", "shell.execute_reply": "2026-05-18T08:28:53.618303Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Line: Cable-1 (uuid=019e3a33-cbe2-7189-8e27-5df23845b82f)\n" ] } ], "source": [ "bus_a = edb.grid.JunctionPoint(name=\"BusA\")\n", "bus_b = edb.grid.JunctionPoint(name=\"BusB\")\n", "line = edb.grid.Line(name=\"Cable-1\", capacity=500, from_element=bus_a, to_element=bus_b)\n", "\n", "offshore = client.get_node(\"my-portfolio\", \"Offshore-Renamed\")\n", "offshore.add(bus_a)\n", "offshore.add(bus_b)\n", "client.create_edge(line)\n", "\n", "found = client.get_edge(\n", " from_path=(\"my-portfolio\", \"Offshore-Renamed\", \"BusA\"),\n", " to_path=(\"my-portfolio\", \"Offshore-Renamed\", \"BusB\"),\n", " type=\"Line\",\n", ").get()\n", "print(f\"{type(found).__name__}: {found.name} (uuid={found.id})\")" ] }, { "cell_type": "markdown", "id": "9822a2743d0d", "metadata": {}, "source": [ "## 9. Cleanup" ] }, { "cell_type": "code", "execution_count": 12, "id": "6c14574f8f1a", "metadata": { "execution": { "iopub.execute_input": "2026-05-18T08:28:53.620528Z", "iopub.status.busy": "2026-05-18T08:28:53.620369Z", "iopub.status.idle": "2026-05-18T08:28:53.644008Z", "shell.execute_reply": "2026-05-18T08:28:53.643023Z" } }, "outputs": [], "source": [ "client.delete()" ] } ], "metadata": { "kernelspec": { "display_name": "backend (3.13.12)", "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.13.12" } }, "nbformat": 4, "nbformat_minor": 5 }