From 67bb09f8543e9250f0922a589f2b4f0631e4ee0a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 30 Jul 2026 16:19:45 -0700 Subject: [PATCH 01/19] Add Building Comfort tutorial (Python + Streamlit) Port the upstream Drasi Server Building Comfort tutorial to drasi-lib. Keeps PostgreSQL CDC, six continuous queries with synthetic joins and aggregation, but replaces the dashboard reaction with a Python reaction that drives a Streamlit UI. The UI also drives the demo (simulation toggle, reset, and per-room set), writing SQL UPDATEs to Postgres that Drasi observes via CDC. - tutorials/building-comfort/: runnable app (app.py + demo/ engine, queries, config), database assets, env/setup/start/cleanup scripts, requirements. - Authored once in _index.md (mounted into the website/ Hugo docs) with the GitHub README generated by scripts/render-tutorials.py (CI --check gate). - Dev container config, tutorials index, and repo README pointer. Verified end-to-end against the real engine and Postgres: bootstrap, break / reset / set a room via CDC, and simulation; screenshot captured from the app. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../building-comfort/devcontainer.json | 60 +++ .devcontainer/building-comfort/post-create.sh | 24 ++ .github/workflows/ci.yml | 3 + .github/workflows/website.yml | 2 + README.md | 8 + scripts/render-tutorials.py | 268 +++++++++++++ tutorials/README.md | 23 ++ tutorials/building-comfort/.env.example | 23 ++ tutorials/building-comfort/.gitignore | 11 + tutorials/building-comfort/README.md | 343 ++++++++++++++++ tutorials/building-comfort/_index.md | 376 ++++++++++++++++++ tutorials/building-comfort/app.py | 226 +++++++++++ .../database/docker-compose.yml | 44 ++ tutorials/building-comfort/database/init.sql | 146 +++++++ tutorials/building-comfort/demo/__init__.py | 19 + tutorials/building-comfort/demo/config.py | 88 ++++ tutorials/building-comfort/demo/engine.py | 244 ++++++++++++ tutorials/building-comfort/demo/queries.py | 203 ++++++++++ .../images/streamlit-building-view.png | Bin 0 -> 158171 bytes tutorials/building-comfort/requirements.txt | 11 + .../building-comfort/scripts/cleanup.ps1 | 48 +++ tutorials/building-comfort/scripts/cleanup.sh | 54 +++ .../scripts/setup-database.ps1 | 105 +++++ .../scripts/setup-database.sh | 120 ++++++ .../building-comfort/scripts/start-demo.ps1 | 42 ++ .../building-comfort/scripts/start-demo.sh | 36 ++ website/config.toml | 15 + website/content/docs/tutorials/_index.md | 27 ++ 28 files changed, 2569 insertions(+) create mode 100644 .devcontainer/building-comfort/devcontainer.json create mode 100755 .devcontainer/building-comfort/post-create.sh create mode 100644 scripts/render-tutorials.py create mode 100644 tutorials/README.md create mode 100644 tutorials/building-comfort/.env.example create mode 100644 tutorials/building-comfort/.gitignore create mode 100644 tutorials/building-comfort/README.md create mode 100644 tutorials/building-comfort/_index.md create mode 100644 tutorials/building-comfort/app.py create mode 100644 tutorials/building-comfort/database/docker-compose.yml create mode 100644 tutorials/building-comfort/database/init.sql create mode 100644 tutorials/building-comfort/demo/__init__.py create mode 100644 tutorials/building-comfort/demo/config.py create mode 100644 tutorials/building-comfort/demo/engine.py create mode 100644 tutorials/building-comfort/demo/queries.py create mode 100644 tutorials/building-comfort/images/streamlit-building-view.png create mode 100644 tutorials/building-comfort/requirements.txt create mode 100644 tutorials/building-comfort/scripts/cleanup.ps1 create mode 100755 tutorials/building-comfort/scripts/cleanup.sh create mode 100644 tutorials/building-comfort/scripts/setup-database.ps1 create mode 100755 tutorials/building-comfort/scripts/setup-database.sh create mode 100644 tutorials/building-comfort/scripts/start-demo.ps1 create mode 100755 tutorials/building-comfort/scripts/start-demo.sh create mode 100644 website/content/docs/tutorials/_index.md diff --git a/.devcontainer/building-comfort/devcontainer.json b/.devcontainer/building-comfort/devcontainer.json new file mode 100644 index 0000000..349dab7 --- /dev/null +++ b/.devcontainer/building-comfort/devcontainer.json @@ -0,0 +1,60 @@ +{ + "name": "Building Comfort Tutorial (Python)", + "image": "mcr.microsoft.com/devcontainers/base:bookworm@sha256:9e35ebf156350ba25b117da6869907034b6e2437b1891e3a43395af0b7d3eda4", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "29", + "moby": false + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12", + "installTools": true + }, + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "configureZshAsDefaultShell": true + } + }, + "postCreateCommand": "bash ../../.devcontainer/building-comfort/post-create.sh", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/tutorials/building-comfort", + "runArgs": [ + "--privileged", + "--init", + "--ipc=host" + ], + "forwardPorts": [ + 8501, + 5732 + ], + "portsAttributes": { + "8501": { + "label": "Streamlit UI", + "onAutoForward": "notify" + }, + "5732": { + "label": "PostgreSQL", + "onAutoForward": "silent" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "editor.formatOnSave": true + } + } + }, + "remoteUser": "vscode", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "containerEnv": { + "POSTGRES_HOST": "localhost", + "POSTGRES_PORT": "5732", + "POSTGRES_HOST_PORT": "5732", + "STREAMLIT_PORT": "8501" + } +} diff --git a/.devcontainer/building-comfort/post-create.sh b/.devcontainer/building-comfort/post-create.sh new file mode 100755 index 0000000..fd64fb1 --- /dev/null +++ b/.devcontainer/building-comfort/post-create.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Post-create script for the Building Comfort tutorial (Python). + +set -e + +echo "๐Ÿ”ง Initializing the Building Comfort (Python) tutorial environment..." + +# Resolve the tutorial directory from this script's location so the script works +# regardless of the current working directory. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +TUTORIAL_DIR="$REPO_ROOT/tutorials/building-comfort" + +# Install the tutorial's Python dependencies (drasi-lib, streamlit, psycopg). +# The devcontainer's Python feature provides a pip that is not externally +# managed, so a plain install works and puts `streamlit` on PATH. +echo "๐Ÿ Installing Python dependencies..." +cd "$TUTORIAL_DIR" +python -m pip install --upgrade pip +python -m pip install -r requirements.txt + +echo "" +echo "โœ… Building Comfort (Python) tutorial environment is ready!" +echo " Next: run 'bash scripts/start-demo.sh' (you are already in tutorials/building-comfort)" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11f753f..537437e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,9 @@ jobs: - name: Python formatting run: .venv/bin/ruff format --check . + - name: Tutorial READMEs are current + run: python3 scripts/render-tutorials.py --check + # This went unrun for long enough to accumulate 1653 errors from a single # misconfiguration, which is exactly the kind of rot a gate is meant to # prevent. pyright resolves `drasi` from the source tree, so the diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 9774484..cb4bd6c 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -12,11 +12,13 @@ on: branches: [main] paths: - "website/**" + - "tutorials/**" - ".github/workflows/website.yml" pull_request: branches: [main] paths: - "website/**" + - "tutorials/**" - ".github/workflows/website.yml" # Allow the deploy job to publish to GitHub Pages via OIDC. diff --git a/README.md b/README.md index d6d5422..c4f8354 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,14 @@ make venv && make develop .venv/bin/python examples/python_source.py ``` +## Tutorials + +Longer, hands-on walkthroughs live in [`tutorials/`](https://github.com/drasi-project/drasi-python/tree/main/tutorials). +The [Building Comfort](https://github.com/drasi-project/drasi-python/tree/main/tutorials/building-comfort) +tutorial builds a smart-building monitor from PostgreSQL CDC, six continuous +queries with synthetic joins, and a Python reaction that drives a live Streamlit +UI. They also render on the [documentation site](https://drasi-project.github.io/drasi-python/docs/tutorials/). + ## Development ```bash diff --git a/scripts/render-tutorials.py b/scripts/render-tutorials.py new file mode 100644 index 0000000..838ce77 --- /dev/null +++ b/scripts/render-tutorials.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Render Docsy/Hugo tutorial sources into plain Markdown READMEs. + +Each tutorial is authored once in an ``_index.md`` that may use Docsy/Hugo +shortcodes (``{{< tabpane >}}``, ``{{% alert %}}``, ...). The doc site consumes +``_index.md`` directly (mounted into the Hugo content tree) so the tab widgets +and styled alerts render. GitHub and plain Markdown viewers cannot process +shortcodes, so this script generates a sibling ``README.md`` with the shortcodes +converted to equivalent plain Markdown. + +Source of truth: tutorials//_index.md (shortcodes, used by doc site) +Generated output: tutorials//README.md (plain Markdown, shown on GitHub) + +Usage: + python3 scripts/render-tutorials.py # write README.md files + python3 scripts/render-tutorials.py --check # fail if any are stale +""" + +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +TUTORIALS_DIR = REPO_ROOT / "tutorials" + +GENERATED_BANNER = ( + "\n\n" +) + +_ATTR_RE = re.compile(r'(\w+)\s*=\s*"([^"]*)"') + +_FRONT_MATTER_RE = re.compile(r"\A---\n.*?\n---\n", re.DOTALL) + +_TABPANE_RE = re.compile( + r"\{\{<\s*tabpane[^}]*>\}\}(?P.*?)\{\{<\s*/\s*tabpane\s*>\}\}", + re.DOTALL, +) +_CODE_TAB_RE = re.compile( + r"\{\{<\s*tab\s+(?P[^}]*?)>\}\}(?P.*?)\{\{<\s*/\s*tab\s*>\}\}", + re.DOTALL, +) +_MD_TAB_RE = re.compile( + r"\{\{%\s*tab\s+(?P[^}]*?)%\}\}(?P.*?)\{\{%\s*/\s*tab\s*%\}\}", + re.DOTALL, +) +_ALERT_RE = re.compile( + r"\{\{%\s*alert\s+(?P[^}]*?)%\}\}(?P.*?)\{\{%\s*/\s*alert\s*%\}\}", + re.DOTALL, +) +_CARD_GRID_RE = re.compile( + r'^\n(?P.*?)\n[ \t]*$', + re.DOTALL | re.MULTILINE, +) +_CARD_RE = re.compile( + r'[^"]+)">.*?' + r'unified-card-title">(?P.*?)</h3>.*?' + r'unified-card-summary">(?P<summary>.*?)</p>.*?</a>', + re.DOTALL, +) +_FLOW_DIAGRAM_RE = re.compile( + r'^<div\s+class="flow-diagram">\n(?P<body>.*?)\n</div>[ \t]*$', + re.DOTALL | re.MULTILINE, +) +_FLOW_STEP_RE = re.compile( + r'flow-step__label">(?P<label>.*?)</div>.*?' + r'flow-step__description">(?P<description>.*?)</div>', + re.DOTALL, +) + + +def _parse_attrs(raw: str) -> dict[str, str]: + return {m.group(1): m.group(2) for m in _ATTR_RE.finditer(raw)} + + +def _render_tabpane(match: re.Match[str]) -> str: + body = match.group("body") + + rendered: list[tuple[int, str]] = [] + + for m in _CODE_TAB_RE.finditer(body): + attrs = _parse_attrs(m.group("attrs")) + header = attrs.get("header", "").strip() + lang = attrs.get("lang", "").strip() + code = m.group("body").strip("\n") + block = f"**{header}**\n\n```{lang}\n{code}\n```" + rendered.append((m.start(), block)) + + for m in _MD_TAB_RE.finditer(body): + attrs = _parse_attrs(m.group("attrs")) + header = attrs.get("header", "").strip() + content = m.group("body").strip("\n") + block = f"**{header}**\n\n{content}" + rendered.append((m.start(), block)) + + rendered.sort(key=lambda item: item[0]) + return "\n\n".join(block for _, block in rendered) + + +def _render_alert(match: re.Match[str]) -> str: + attrs = _parse_attrs(match.group("attrs")) + title = attrs.get("title", "").strip() + body = match.group("body").strip("\n") + + lines: list[str] = [] + if title: + lines.append(f"> **{title}**") + lines.append(">") + for line in body.splitlines(): + lines.append(">" if not line.strip() else f"> {line}") + return "\n".join(lines) + + +def _render_card_grid(match: re.Match[str]) -> str: + """Convert a Docsy ``card-grid`` of link cards into a Markdown list. + + GitHub renders ``<a href><div>...</div></a>`` cards as empty links because + it hoists the block-level ``<div>`` out of the anchor. The doc site keeps + the cards; here we emit a plain bullet list of named links instead. + """ + items: list[str] = [] + for card in _CARD_RE.finditer(match.group("body")): + href = card.group("href").strip() + title = " ".join(card.group("title").split()) + summary = " ".join(card.group("summary").split()) + items.append(f"- **[{title}]({href})** โ€” {summary}") + return "\n".join(items) if items else match.group(0) + + +def _render_flow_diagram(match: re.Match[str]) -> str: + """Convert a Docsy ``flow-diagram`` into a Markdown arrow chain plus list. + + GitHub renders the nested ``<div>`` steps as stray words, so we emit an + arrow chain of the step labels followed by a bullet list of each step's + description. The doc site keeps the styled diagram. + """ + steps = [ + (" ".join(m.group("label").split()), " ".join(m.group("description").split())) + for m in _FLOW_STEP_RE.finditer(match.group("body")) + ] + if not steps: + return match.group(0) + + chain = " โ†’ ".join(f"**{label}**" for label, _ in steps) + bullets = "\n".join(f"- **{label}** โ€” {desc}" for label, desc in steps) + return f"{chain}\n\n{bullets}" + + +_HEADING_ID_RE = re.compile( + r"^(?P<hashes>#{1,6})\s+(?P<text>.*?)\s*\{#(?P<id>[\w-]+)\}\s*$", re.MULTILINE +) + + +def _github_slug(text: str) -> str: + """Approximate GitHub's heading-to-anchor slug algorithm.""" + slug = text.strip().lower() + # Drop characters GitHub strips (anything not a word char, space, or hyphen). + slug = re.sub(r"[^\w\s-]", "", slug) + # Collapse whitespace runs to single hyphens. + slug = re.sub(r"\s+", "-", slug) + return slug + + +def _rewrite_heading_ids(source: str) -> str: + """Strip Hugo ``{#id}`` heading attributes and repoint in-page links. + + Hugo uses ``## Title {#id}`` to set an explicit anchor, and links such as + ``[Title](#id)`` rely on it. Plain Markdown viewers render the ``{#id}`` + literally and instead auto-generate anchors from the heading text, so we + remove the attribute and rewrite every ``#id`` link to the slug GitHub + derives from the heading text. + """ + id_to_slug: dict[str, str] = {} + for match in _HEADING_ID_RE.finditer(source): + id_to_slug[match.group("id")] = _github_slug(match.group("text")) + + # Remove the {#id} attribute from headings. + out = _HEADING_ID_RE.sub(lambda m: f"{m.group('hashes')} {m.group('text')}", source) + + # Repoint in-page links that targeted the explicit ids. + def _replace_link(m: re.Match[str]) -> str: + slug = id_to_slug.get(m.group("id")) + return f"](#{slug})" if slug else m.group(0) + + out = re.sub(r"\]\(#(?P<id>[\w-]+)\)", _replace_link, out) + return out + + +def render(source: str) -> str: + out = _FRONT_MATTER_RE.sub("", source) + out = _rewrite_heading_ids(out) + out = _CARD_GRID_RE.sub(_render_card_grid, out) + out = _FLOW_DIAGRAM_RE.sub(_render_flow_diagram, out) + out = _TABPANE_RE.sub(_render_tabpane, out) + out = _ALERT_RE.sub(_render_alert, out) + # Collapse 3+ blank lines that conversions can introduce. + out = re.sub(r"\n{4,}", "\n\n\n", out) + return GENERATED_BANNER + out.lstrip("\n") + + +def iter_sources() -> list[Path]: + if not TUTORIALS_DIR.is_dir(): + return [] + return sorted(TUTORIALS_DIR.glob("*/_index.md")) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--check", + action="store_true", + help="Verify generated READMEs are up to date; do not write.", + ) + args = parser.parse_args() + + sources = iter_sources() + if not sources: + print("No tutorials/*/_index.md sources found.", file=sys.stderr) + return 0 + + stale: list[Path] = [] + for source in sources: + target = source.with_name("README.md") + expected = render(source.read_text(encoding="utf-8")) + current = target.read_text(encoding="utf-8") if target.exists() else None + + if args.check: + if current != expected: + stale.append(target) + continue + + if current != expected: + target.write_text(expected, encoding="utf-8") + print(f"rendered {target.relative_to(REPO_ROOT)}") + else: + print(f"unchanged {target.relative_to(REPO_ROOT)}") + + if args.check and stale: + print( + "The following generated READMEs are out of date:\n" + + "\n".join(f" - {p.relative_to(REPO_ROOT)}" for p in stale) + + "\n\nRun `python3 scripts/render-tutorials.py` and commit the result.", + file=sys.stderr, + ) + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tutorials/README.md b/tutorials/README.md new file mode 100644 index 0000000..826e0fe --- /dev/null +++ b/tutorials/README.md @@ -0,0 +1,23 @@ +# Tutorials + +Hands-on, end-to-end walkthroughs built on [`drasi-lib`](https://pypi.org/project/drasi-lib/), +the Python binding for Drasi's continuous-query engine. Each tutorial is a +self-contained project you can clone and run. + +| Tutorial | What you learn | How to run it | +| --- | --- | --- | +| [building-comfort](building-comfort) | Smart-building comfort monitoring โ€” PostgreSQL CDC, six continuous queries with synthetic joins and aggregation, and a **Python reaction that drives a Streamlit UI** (with simulation, reset and per-room controls) | Open the **Building Comfort Tutorial (Python)** dev container (or a Codespace) and follow [tutorials/building-comfort](building-comfort) | + +## How these tutorials are authored + +Each tutorial is written once in `tutorials/<name>/_index.md` (the single source +of truth), which is mounted into the [documentation site](../website) so it also +renders at <https://drasi-project.github.io/drasi-python/docs/tutorials/>. The +GitHub-friendly `tutorials/<name>/README.md` is generated from that same file: + +```bash +python3 scripts/render-tutorials.py # regenerate the READMEs +python3 scripts/render-tutorials.py --check # fail if any are stale (run in CI) +``` + +Edit the `_index.md`, not the `README.md`. diff --git a/tutorials/building-comfort/.env.example b/tutorials/building-comfort/.env.example new file mode 100644 index 0000000..aa6ed00 --- /dev/null +++ b/tutorials/building-comfort/.env.example @@ -0,0 +1,23 @@ +# Example environment variables for the building-comfort tutorial. +# +# Copy this file to .env and adjust as needed: +# cp .env.example .env +# +# The scripts and the app read these with the same defaults shown here, so you +# only need a .env if you change a port or credential. +# +# IMPORTANT: Never commit .env with real credentials! + +# PostgreSQL connection settings (used by the app via psycopg and by Drasi). +POSTGRES_HOST=localhost +POSTGRES_PORT=5732 +POSTGRES_HOST_PORT=5732 +POSTGRES_DATABASE=building_comfort +POSTGRES_USER=drasi_user +POSTGRES_PASSWORD=drasi_password + +# Streamlit UI port. +STREAMLIT_PORT=8501 + +# How often simulation mode assigns a random room new readings, in seconds. +SIMULATION_INTERVAL_S=3 diff --git a/tutorials/building-comfort/.gitignore b/tutorials/building-comfort/.gitignore new file mode 100644 index 0000000..1e58996 --- /dev/null +++ b/tutorials/building-comfort/.gitignore @@ -0,0 +1,11 @@ +# Local environment overrides +.env + +# Python +__pycache__/ +*.pyc +.venv/ + +# Drasi engine state / logs +data/ +*.log diff --git a/tutorials/building-comfort/README.md b/tutorials/building-comfort/README.md new file mode 100644 index 0000000..36b8777 --- /dev/null +++ b/tutorials/building-comfort/README.md @@ -0,0 +1,343 @@ +<!-- DO NOT EDIT. Generated from _index.md by scripts/render-tutorials.py. Edit _index.md and run `python3 scripts/render-tutorials.py`. --> + +Imagine you manage a building and want to know โ€” the instant it happens โ€” when any room becomes uncomfortable: too hot, too cold, or stuffy with CO2. You don't want to poll sensors on a timer or wire up a stream processor by hand. You just want to describe what "uncomfortable" means and have something watch every room, floor, and the whole building for you, continuously. + +This tutorial builds a **Building Comfort** monitoring demo with **`drasi-lib`**, the Python binding that embeds Drasi's continuous-query engine directly in your app. A PostgreSQL database holds a building made of floors and rooms, each room reporting `temperature`, `humidity`, and `co2`. You'll run a single Streamlit program that starts the engine, watch a live UI light up, and then change room readings and see everything react in real time. Instead of a built-in dashboard reaction, the UI is rendered from a **Python reaction** โ€” a callback that receives each query's changes and updates the screen. + +**What you'll build:** a Streamlit app that embeds Drasi, connects to PostgreSQL, and reacts to room sensor changes in real time, assembled from Drasi's three core building blocks: + +**Sources** โ†’ **Continuous Queries** โ†’ **Reactions** + +- **Sources** โ€” Connect to your data sources +- **Continuous Queries** โ€” Define what changes matter +- **Reactions** โ€” Take action automatically + +| Step | What You'll Do | Time | +| ---- | ------------- | ---- | +| **[Step 1: Set Up Your Environment](#step-1-of-4-set-up-your-environment)** | Open the dev container (or install Python + Docker locally) | 5 min | +| **[Step 2: Run the Demo](#step-2-of-4-run-the-demo)** | One command starts PostgreSQL and the Streamlit app | 3 min | +| **[Step 3: Open the UI](#step-3-of-4-open-the-ui)** | Watch comfort levels and alerts live | 2 min | +| **[Step 4: Drive Change](#step-4-of-4-drive-change)** | Set, reset, and simulate rooms from the sidebar โ€” and watch Drasi react instantly | 5 min | +| **[How It Works](#how-it-works)** | Understand the source, the six queries, the synthetic joins, and the Python reaction | 5 min | + +> **Before you begin** +> +> - **One process:** the Streamlit app embeds the Drasi engine, so there's just one command to run. It stays in the foreground. +> - **Working directory:** run every command from the tutorial directory (`tutorials/building-comfort/`). The dev container opens there automatically; if you're running locally, `cd tutorials/building-comfort` first. +> - **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*) โ€” use the one for your shell. The dev container and Codespaces use *bash*. +> - **Ports:** the Streamlit UI is on `8501` and PostgreSQL is published on `5732`. + +## Step 1 of 4: Set Up Your Environment +The easiest way to follow this tutorial is the **dev container**, which installs everything for you. You can also run locally if you prefer. + +### Option A: Dev Container or GitHub Codespaces (recommended) + +1. Open the [`drasi-python`](https://github.com/drasi-project/drasi-python) repository in VS Code and run **Reopen in Container** (or create a **Codespace** from the repo's **Code** menu). +2. When prompted for a configuration, choose **Building Comfort Tutorial (Python)**. +3. Wait for the container to finish. Its setup script installs the tutorial's Python dependencies (`drasi-lib`, `streamlit`, `psycopg`) and the Docker tooling for PostgreSQL. + +That's it โ€” skip ahead to [Step 2](#step-2-of-4-run-the-demo). + +### Option B: Run Locally + +You'll need **Python 3.10+**, **Docker** (for PostgreSQL), and **bash** for the helper scripts (on Windows use Git Bash or WSL, or run the PowerShell scripts). From the repository root, move into the tutorial directory and install the dependencies: + +**bash / zsh** + +```bash +cd tutorials/building-comfort +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +``` + +**PowerShell** + +```powershell +cd tutorials/building-comfort +python -m venv .venv; .venv\Scripts\Activate.ps1 +pip install -r requirements.txt +``` + +`drasi-lib` ships as a prebuilt wheel, so no Rust toolchain is needed. It downloads the Postgres plugins it needs from `ghcr.io/drasi-project` the first time the app runs. + +## Step 2 of 4: Run the Demo +In your terminal, start the demo: + +**bash / zsh** + +```bash +bash scripts/start-demo.sh +``` + +**PowerShell** + +```powershell +powershell -ExecutionPolicy Bypass -File scripts/start-demo.ps1 +``` + +The `start-demo` script does two things: it starts PostgreSQL (seeding one building, three floors, and nine rooms โ€” every room comfortable to begin with) and then launches the Streamlit app. + +On first start, the app's embedded engine downloads the plugins it needs (`source/postgres`, `bootstrap/postgres`) from `ghcr.io/drasi-project` and caches them under `~/.drasi/plugins`, connects to the database, and starts the six continuous queries and the reaction. Streamlit prints a URL like the following when it's ready: + +```text +You can now view your Streamlit app in your browser. +Local URL: http://localhost:8501 +``` + +Leave this running. Everything else happens in your browser. + +> **Stopping and resetting** +> +> Press **Ctrl+C** in the terminal to stop the app. To remove the database container when you're completely done, run `bash scripts/cleanup.sh` (bash) or `powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1` (PowerShell). Add `--volumes` (bash) or `-RemoveVolumes` (PowerShell) to also delete the data. + +## Step 3 of 4: Open the UI +The app renders the building from the Python reaction โ€” there's no separate dashboard server. **Wait until the app has finished starting** (on the first run this takes ~30 seconds while the plugins download and the queries bootstrap), then open it in your browser: + +```text +http://localhost:8501 +``` + +In the dev container or Codespaces, port `8501` is forwarded automatically โ€” VS Code shows a notification when the app is ready, and you can also open it from the **Ports** panel. + +You'll see the **Building Comfort** UI: + +- **Building Comfort** (the main panel) โ€” the building grouped by floor, with each room's comfort level and sensor readings. Every room starts at comfort `46` (comfortable). +- **Overall** โ€” the building's overall comfort level as a number and a meter. +- **Comfort alerts** and **Floor alerts** โ€” empty for now; rooms and floors appear here only when they need attention. +- **Floor comfort** โ€” a table of each floor's comfort level. + +The UI refreshes on its own โ€” the reaction updates a shared snapshot the moment the data changes, and Streamlit renders it. Let's make something change. + +## Step 4 of 4: Drive Change +Everything you need is in the **sidebar** on the left. Use it to change room readings and watch Drasi react. + +> **No middle tier โ€” the controls just write to the database** +> +> The sidebar controls don't call the Drasi engine directly. Each one runs a plain SQL `UPDATE` against PostgreSQL (through `psycopg`) โ€” exactly what an existing building-management app would already do. There's no API to call and no event to publish. Drasi observes the row change through PostgreSQL's logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which updates the UI. The set/reset/simulate buttons are a thin front end over a single `UPDATE` statement. + +### Set a room + +In the sidebar's **Set a room** section, pick a room and give it new readings. Try pushing one out of the comfortable band โ€” for example `temperature = 40`, `humidity = 20`, `co2 = 700` โ€” then press **Set room**. + +Within about a second the UI reacts: that room drops out of the comfortable band, the overall meter falls, and entries appear in **Comfort alerts** and **Floor alerts**. + +### Reset a room (or all rooms) + +Press **Reset this room** to return the selected room to the comfortable defaults (`70 / 40 / 10`), or **Reset all rooms** to return the whole building. The alerts clear and the building returns to green. + +### Set custom values + +Try partial degradation โ€” make a room too hot without touching CO2. Pick a room and set `temperature = 82`, `humidity = 40`, `co2 = 10`: + +That gives `50 + (82-72) + (40-42) + 0 = 58` โ€” above 50, so the room and its floor raise alerts even though humidity and CO2 are fine. + +### Let it run hands-free + +Flip the **Simulation mode** toggle at the top of the sidebar. Every few seconds it picks a random room and assigns new readings, so comfort levels rise and fall and alerts come and go on their own. Flip it off to stop, then **Reset all rooms** to return everything to comfortable. + +## How It Works +The whole demo is a single Streamlit program in `tutorials/building-comfort/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state. Here's what each part does. + +### The Source + +The engine installs the Postgres plugins and adds the source, pointing it at the tutorial database: + +```python +await drasi.install_plugin("source/postgres") +await drasi.install_plugin("bootstrap/postgres") +await drasi.start() + +await drasi.add_source( + "postgres", + "db", + { + **POSTGRES, # host, port, user, password, database + "sslMode": "prefer", + # Schema-qualified so the node labels match the queries. + "tables": ["public.Building", "public.Floor", "public.Room"], + "tableKeys": [ + {"table": "Building", "keyColumns": ["id"]}, + {"table": "Floor", "keyColumns": ["id"]}, + {"table": "Room", "keyColumns": ["id"]}, + ], + "slotName": "drasi_building_comfort_slot", + "publicationName": "drasi_building_comfort_pub", + }, + bootstrap={"kind": "postgres", **POSTGRES}, +) +``` + +The source uses **logical replication (CDC)** to stream changes from the `Building`, `Floor`, and `Room` tables. `tableKeys` tells Drasi the primary key of each table so it can track row identity across changes. The `bootstrap` provider loads the rows that already exist when the query starts; after that, every `UPDATE` the UI makes flows to Drasi as a change. The table names are PascalCase so the node labels Drasi sees match the queries exactly: `(r:Room)`, `(f:Floor)`, `(b:Building)`. For every option the source accepts, see [Configure the PostgreSQL Source](https://drasi.io/reference/sources/postgresql/). + +### The Continuous Queries + +Each query computes a **comfort level** with the same formula. A value between **40 and 50** is comfortable; the seed values (70ยฐF, 40%, 10 ppm) give `50 + (70-72) + (40-42) + 0 = 46`. + +```cypher +floor( 50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END ) +``` + +There are six queries: + +| Query | What it returns | +| ----- | --------------- | +| `building-comfort-ui` | One row per room with its comfort level โ€” the feed that drives the building view | +| `building-comfort-level-calc` | The building's overall comfort level | +| `floor-comfort-level-calc` | Each floor's average comfort level | +| `room-alert` | Only the rooms whose comfort is outside 40โ€“50 | +| `floor-alert` | Only the floors whose average comfort is outside 40โ€“50 | +| `building-alert` | The building, when its overall comfort is outside 40โ€“50 | + +#### Synthetic joins connect the entities + +PostgreSQL knows `Room.floor_id` references `Floor.id` through a foreign key, but Drasi doesn't read foreign keys. Instead, each query **declares** the relationships it needs as synthetic joins, so the Cypher can walk from room to floor to building. In `drasi-lib` a join is a plain mapping passed to `add_query`: + +```python +PART_OF_FLOOR = { + "id": "PART_OF_FLOOR", + "keys": [ + {"label": "Room", "property": "floor_id"}, + {"label": "Floor", "property": "id"}, + ], +} +PART_OF_BUILDING = { + "id": "PART_OF_BUILDING", + "keys": [ + {"label": "Floor", "property": "building_id"}, + {"label": "Building", "property": "id"}, + ], +} + +await drasi.add_query( + "building-comfort-ui", CYPHER, ["db"], joins=[PART_OF_FLOOR, PART_OF_BUILDING] +) +``` + +With those joins declared, a query can match the whole hierarchy: + +```cypher +MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) +``` + +#### Aggregating up the hierarchy + +Half of the queries don't just read rooms โ€” they **roll comfort levels up** the building. In a Continuous Query, an aggregate such as `avg()` inside a `WITH` groups by whatever non-aggregated values travel alongside it, exactly like SQL's `GROUP BY`. + +**One stage โ€” average a floor's rooms.** `floor-comfort-level-calc` keeps the floor `f` in the `WITH`, so `avg()` produces one value per floor: + +```cypher +WITH f, floor( 50 + (r.temperature - 72) + ... ) AS RoomComfortLevel +WITH f, avg(RoomComfortLevel) AS ComfortLevel +RETURN f.id AS FloorId, ComfortLevel +``` + +**Two stages โ€” average the averages.** `building-alert` aggregates twice. Carrying `f, b` groups the first `avg()` by floor; dropping `f` from the next `WITH` widens the grouping so the second `avg()` rolls the floor averages up into a single building level: + +```cypher +WITH f, b, avg(RoomComfortLevel) AS FloorComfortLevel // rooms -> floor +WITH b, avg(FloorComfortLevel) AS ComfortLevel // floors -> building +``` + +**Filtering on an aggregate.** The `floor-alert` and `building-alert` queries place a `WHERE` *after* the aggregation โ€” like SQL's `HAVING` โ€” so a floor or the building only shows up while its average is outside the comfortable band: + +```cypher +WITH f, avg(RoomComfortLevel) AS ComfortLevel +WHERE ComfortLevel < 40 OR ComfortLevel > 50 +RETURN f.id AS FloorId, ComfortLevel +``` + +Because these are *continuous* queries, the aggregates are maintained **incrementally**: when one room's reading changes, Drasi recomputes only the affected floor and building averages and emits just that change โ€” it never rescans every room. + +### The Python Reaction + +This is where the Python demo differs from a built-in dashboard. Instead of configuring a dashboard reaction, we register a **Python reaction** โ€” a plain callback subscribed to all six queries. Each time a query's result set changes, Drasi calls it with the query id and the diffs, and the callback updates a thread-safe snapshot: + +```python +def on_results(event): + query_id = event["query_id"] + key = KEY_FIELDS[query_id] # "RoomId", "FloorId" or "BuildingId" + with lock: + store = results[query_id] # the UI reads this snapshot + for diff in event["results"]: + row = diff.get("data") or diff.get("after") + if diff["type"] == "DELETE": + store.pop(row[key], None) + else: # ADD or UPDATE: upsert by row identity + store[row[key]] = row + + +await drasi.add_python_reaction("ui", ALL_QUERY_IDS, on_results) +``` + +Every diff carries the affected row in `data` (for an `UPDATE` it equals `after`), so the snapshot is kept by row identity โ€” upsert on `ADD`/`UPDATE`, drop on `DELETE`. The `type` is one of `ADD`, `UPDATE`, or `DELETE`. This works the same for the per-room queries and the aggregate floor/building queries, which emit an `UPDATE` as their average changes. See [Python reactions](../../guides/python-reactions/) for the full callback contract. + +### Embedding the engine in Streamlit + +Streamlit re-runs its script top to bottom on every interaction, which doesn't suit a long-lived async engine. So the engine runs on its own asyncio event loop in a **background daemon thread**, created exactly once with `@st.cache_resource` and shared across every browser session: + +```python +@st.cache_resource +def get_engine(): + engine = ComfortEngine() # spins up the background loop + Drasi + engine.wait_ready() + return engine +``` + +Each run, `app.py` reads `engine.snapshot()` and renders the building, the meter, the floor table and the alert lists. A one-and-a-half-second auto-refresh keeps the view live. Because the reaction only updates the snapshot when a query actually changes, the UI reflects each `UPDATE` the moment it lands โ€” no polling of the database. + +The centerpiece building panel is just a loop over the `building-comfort-ui` rows, grouped by floor, picking a status emoji from each room's comfort level: + +```python +for floor_name in sorted(by_floor): + st.markdown(f"### {floor_name}") + for room in by_floor[floor_name]: + emoji, label = comfort_status(room["ComfortLevel"]) + st.markdown( + f"- **{room['RoomName']}** โ€” comfort **{room['ComfortLevel']}** {emoji} {label}" + ) +``` + +And the reaction renders it live: + +<img src="images/streamlit-building-view.png" width="820" alt="The Building Comfort UI: rooms grouped by floor, each tagged with a comfort level and a green, red, or blue status emoji, with a sidebar of controls"> + +### Other ways to consume a query + +A Python reaction is the most direct way to turn query changes into a UI, but it isn't the only option `drasi-lib` gives you: + +- **Stream the changes** with `async for event in await drasi.query_results("room-alert")` and drive any front end you like. See [Streaming results](../../guides/streaming/). +- **Reuse a published reaction** such as the dashboard reaction plugin, exactly as the [Postgres dashboard example](../../examples/postgres-dashboard/) does โ€” no UI code at all. + +Because Drasi emits only what *changed* โ€” never the full result set โ€” whichever you choose stays live without polling. + +## Clean Up +When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database container: + +**bash / zsh** + +```bash +# Stop containers, keep data +bash scripts/cleanup.sh + +# Stop containers and delete the data volume +bash scripts/cleanup.sh --volumes +``` + +**PowerShell** + +```powershell +# Stop containers, keep data +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 + +# Stop containers and delete the data volume +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 -RemoveVolumes +``` + +## What You Learned +- **Sources** connect Drasi to live data โ€” here, PostgreSQL via Change Data Capture, configured in a few lines of Python. +- **Continuous Queries** with **synthetic joins** let you model relationships (room โ†’ floor โ†’ building) and compute derived values (comfort levels) that stay current automatically. +- **Reactions** turn query changes into action. A **Python reaction** gave you a live UI from a callback and a snapshot โ€” no bespoke web server, and complete control over the rendering. +- Because Drasi emits only what *changed*, everything updates the instant the data does, with no polling. + +From here, try editing the comfort formula or the layout in `app.py`, adding a new alert query, or pointing the source at your own database. diff --git a/tutorials/building-comfort/_index.md b/tutorials/building-comfort/_index.md new file mode 100644 index 0000000..339e907 --- /dev/null +++ b/tutorials/building-comfort/_index.md @@ -0,0 +1,376 @@ +--- +type: "docs" +title: "Building Comfort" +linkTitle: "Building Comfort" +weight: 20 +description: > + Monitor a smart building's comfort in real time with PostgreSQL CDC, six continuous queries with synthetic joins, and a Python reaction that drives a Streamlit UI. +--- + +Imagine you manage a building and want to know โ€” the instant it happens โ€” when any room becomes uncomfortable: too hot, too cold, or stuffy with CO2. You don't want to poll sensors on a timer or wire up a stream processor by hand. You just want to describe what "uncomfortable" means and have something watch every room, floor, and the whole building for you, continuously. + +This tutorial builds a **Building Comfort** monitoring demo with **`drasi-lib`**, the Python binding that embeds Drasi's continuous-query engine directly in your app. A PostgreSQL database holds a building made of floors and rooms, each room reporting `temperature`, `humidity`, and `co2`. You'll run a single Streamlit program that starts the engine, watch a live UI light up, and then change room readings and see everything react in real time. Instead of a built-in dashboard reaction, the UI is rendered from a **Python reaction** โ€” a callback that receives each query's changes and updates the screen. + +**What you'll build:** a Streamlit app that embeds Drasi, connects to PostgreSQL, and reacts to room sensor changes in real time, assembled from Drasi's three core building blocks: + +<div class="flow-diagram"> + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-database"></i> + </div> + <div class="flow-step__label">Sources</div> + <div class="flow-step__description">Connect to your data sources</div> + </div> + + <div class="flow-arrow"> + <i class="fas fa-arrow-right"></i> + </div> + + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-filter"></i> + </div> + <div class="flow-step__label">Continuous Queries</div> + <div class="flow-step__description">Define what changes matter</div> + </div> + + <div class="flow-arrow"> + <i class="fas fa-arrow-right"></i> + </div> + + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-bolt"></i> + </div> + <div class="flow-step__label">Reactions</div> + <div class="flow-step__description">Take action automatically</div> + </div> +</div> + +| Step | What You'll Do | Time | +| ---- | ------------- | ---- | +| **[Step 1: Set Up Your Environment](#setup)** | Open the dev container (or install Python + Docker locally) | 5 min | +| **[Step 2: Run the Demo](#run)** | One command starts PostgreSQL and the Streamlit app | 3 min | +| **[Step 3: Open the UI](#ui)** | Watch comfort levels and alerts live | 2 min | +| **[Step 4: Drive Change](#drive)** | Set, reset, and simulate rooms from the sidebar โ€” and watch Drasi react instantly | 5 min | +| **[How It Works](#how)** | Understand the source, the six queries, the synthetic joins, and the Python reaction | 5 min | + +{{% alert title="Before you begin" color="info" %}} +- **One process:** the Streamlit app embeds the Drasi engine, so there's just one command to run. It stays in the foreground. +- **Working directory:** run every command from the tutorial directory (`tutorials/building-comfort/`). The dev container opens there automatically; if you're running locally, `cd tutorials/building-comfort` first. +- **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*) โ€” use the one for your shell. The dev container and Codespaces use *bash*. +- **Ports:** the Streamlit UI is on `8501` and PostgreSQL is published on `5732`. +{{% /alert %}} + +## Step 1 of 4: Set Up Your Environment {#setup} + +The easiest way to follow this tutorial is the **dev container**, which installs everything for you. You can also run locally if you prefer. + +### Option A: Dev Container or GitHub Codespaces (recommended) + +1. Open the [`drasi-python`](https://github.com/drasi-project/drasi-python) repository in VS Code and run **Reopen in Container** (or create a **Codespace** from the repo's **Code** menu). +2. When prompted for a configuration, choose **Building Comfort Tutorial (Python)**. +3. Wait for the container to finish. Its setup script installs the tutorial's Python dependencies (`drasi-lib`, `streamlit`, `psycopg`) and the Docker tooling for PostgreSQL. + +That's it โ€” skip ahead to [Step 2](#run). + +### Option B: Run Locally + +You'll need **Python 3.10+**, **Docker** (for PostgreSQL), and **bash** for the helper scripts (on Windows use Git Bash or WSL, or run the PowerShell scripts). From the repository root, move into the tutorial directory and install the dependencies: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +cd tutorials/building-comfort +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +cd tutorials/building-comfort +python -m venv .venv; .venv\Scripts\Activate.ps1 +pip install -r requirements.txt +{{< /tab >}} +{{< /tabpane >}} + +`drasi-lib` ships as a prebuilt wheel, so no Rust toolchain is needed. It downloads the Postgres plugins it needs from `ghcr.io/drasi-project` the first time the app runs. + +## Step 2 of 4: Run the Demo {#run} + +In your terminal, start the demo: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +bash scripts/start-demo.sh +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +powershell -ExecutionPolicy Bypass -File scripts/start-demo.ps1 +{{< /tab >}} +{{< /tabpane >}} + +The `start-demo` script does two things: it starts PostgreSQL (seeding one building, three floors, and nine rooms โ€” every room comfortable to begin with) and then launches the Streamlit app. + +On first start, the app's embedded engine downloads the plugins it needs (`source/postgres`, `bootstrap/postgres`) from `ghcr.io/drasi-project` and caches them under `~/.drasi/plugins`, connects to the database, and starts the six continuous queries and the reaction. Streamlit prints a URL like the following when it's ready: + +```text +You can now view your Streamlit app in your browser. +Local URL: http://localhost:8501 +``` + +Leave this running. Everything else happens in your browser. + +{{% alert title="Stopping and resetting" color="info" %}} +Press **Ctrl+C** in the terminal to stop the app. To remove the database container when you're completely done, run `bash scripts/cleanup.sh` (bash) or `powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1` (PowerShell). Add `--volumes` (bash) or `-RemoveVolumes` (PowerShell) to also delete the data. +{{% /alert %}} + +## Step 3 of 4: Open the UI {#ui} + +The app renders the building from the Python reaction โ€” there's no separate dashboard server. **Wait until the app has finished starting** (on the first run this takes ~30 seconds while the plugins download and the queries bootstrap), then open it in your browser: + +```text +http://localhost:8501 +``` + +In the dev container or Codespaces, port `8501` is forwarded automatically โ€” VS Code shows a notification when the app is ready, and you can also open it from the **Ports** panel. + +You'll see the **Building Comfort** UI: + +- **Building Comfort** (the main panel) โ€” the building grouped by floor, with each room's comfort level and sensor readings. Every room starts at comfort `46` (comfortable). +- **Overall** โ€” the building's overall comfort level as a number and a meter. +- **Comfort alerts** and **Floor alerts** โ€” empty for now; rooms and floors appear here only when they need attention. +- **Floor comfort** โ€” a table of each floor's comfort level. + +The UI refreshes on its own โ€” the reaction updates a shared snapshot the moment the data changes, and Streamlit renders it. Let's make something change. + +## Step 4 of 4: Drive Change {#drive} + +Everything you need is in the **sidebar** on the left. Use it to change room readings and watch Drasi react. + +{{% alert title="No middle tier โ€” the controls just write to the database" color="info" %}} +The sidebar controls don't call the Drasi engine directly. Each one runs a plain SQL `UPDATE` against PostgreSQL (through `psycopg`) โ€” exactly what an existing building-management app would already do. There's no API to call and no event to publish. Drasi observes the row change through PostgreSQL's logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which updates the UI. The set/reset/simulate buttons are a thin front end over a single `UPDATE` statement. +{{% /alert %}} + +### Set a room + +In the sidebar's **Set a room** section, pick a room and give it new readings. Try pushing one out of the comfortable band โ€” for example `temperature = 40`, `humidity = 20`, `co2 = 700` โ€” then press **Set room**. + +Within about a second the UI reacts: that room drops out of the comfortable band, the overall meter falls, and entries appear in **Comfort alerts** and **Floor alerts**. + +### Reset a room (or all rooms) + +Press **Reset this room** to return the selected room to the comfortable defaults (`70 / 40 / 10`), or **Reset all rooms** to return the whole building. The alerts clear and the building returns to green. + +### Set custom values + +Try partial degradation โ€” make a room too hot without touching CO2. Pick a room and set `temperature = 82`, `humidity = 40`, `co2 = 10`: + +That gives `50 + (82-72) + (40-42) + 0 = 58` โ€” above 50, so the room and its floor raise alerts even though humidity and CO2 are fine. + +### Let it run hands-free + +Flip the **Simulation mode** toggle at the top of the sidebar. Every few seconds it picks a random room and assigns new readings, so comfort levels rise and fall and alerts come and go on their own. Flip it off to stop, then **Reset all rooms** to return everything to comfortable. + +## How It Works {#how} + +The whole demo is a single Streamlit program in `tutorials/building-comfort/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state. Here's what each part does. + +### The Source + +The engine installs the Postgres plugins and adds the source, pointing it at the tutorial database: + +```python +await drasi.install_plugin("source/postgres") +await drasi.install_plugin("bootstrap/postgres") +await drasi.start() + +await drasi.add_source( + "postgres", + "db", + { + **POSTGRES, # host, port, user, password, database + "sslMode": "prefer", + # Schema-qualified so the node labels match the queries. + "tables": ["public.Building", "public.Floor", "public.Room"], + "tableKeys": [ + {"table": "Building", "keyColumns": ["id"]}, + {"table": "Floor", "keyColumns": ["id"]}, + {"table": "Room", "keyColumns": ["id"]}, + ], + "slotName": "drasi_building_comfort_slot", + "publicationName": "drasi_building_comfort_pub", + }, + bootstrap={"kind": "postgres", **POSTGRES}, +) +``` + +The source uses **logical replication (CDC)** to stream changes from the `Building`, `Floor`, and `Room` tables. `tableKeys` tells Drasi the primary key of each table so it can track row identity across changes. The `bootstrap` provider loads the rows that already exist when the query starts; after that, every `UPDATE` the UI makes flows to Drasi as a change. The table names are PascalCase so the node labels Drasi sees match the queries exactly: `(r:Room)`, `(f:Floor)`, `(b:Building)`. For every option the source accepts, see [Configure the PostgreSQL Source](https://drasi.io/reference/sources/postgresql/). + +### The Continuous Queries + +Each query computes a **comfort level** with the same formula. A value between **40 and 50** is comfortable; the seed values (70ยฐF, 40%, 10 ppm) give `50 + (70-72) + (40-42) + 0 = 46`. + +```cypher +floor( 50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END ) +``` + +There are six queries: + +| Query | What it returns | +| ----- | --------------- | +| `building-comfort-ui` | One row per room with its comfort level โ€” the feed that drives the building view | +| `building-comfort-level-calc` | The building's overall comfort level | +| `floor-comfort-level-calc` | Each floor's average comfort level | +| `room-alert` | Only the rooms whose comfort is outside 40โ€“50 | +| `floor-alert` | Only the floors whose average comfort is outside 40โ€“50 | +| `building-alert` | The building, when its overall comfort is outside 40โ€“50 | + +#### Synthetic joins connect the entities + +PostgreSQL knows `Room.floor_id` references `Floor.id` through a foreign key, but Drasi doesn't read foreign keys. Instead, each query **declares** the relationships it needs as synthetic joins, so the Cypher can walk from room to floor to building. In `drasi-lib` a join is a plain mapping passed to `add_query`: + +```python +PART_OF_FLOOR = { + "id": "PART_OF_FLOOR", + "keys": [ + {"label": "Room", "property": "floor_id"}, + {"label": "Floor", "property": "id"}, + ], +} +PART_OF_BUILDING = { + "id": "PART_OF_BUILDING", + "keys": [ + {"label": "Floor", "property": "building_id"}, + {"label": "Building", "property": "id"}, + ], +} + +await drasi.add_query( + "building-comfort-ui", CYPHER, ["db"], joins=[PART_OF_FLOOR, PART_OF_BUILDING] +) +``` + +With those joins declared, a query can match the whole hierarchy: + +```cypher +MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) +``` + +#### Aggregating up the hierarchy + +Half of the queries don't just read rooms โ€” they **roll comfort levels up** the building. In a Continuous Query, an aggregate such as `avg()` inside a `WITH` groups by whatever non-aggregated values travel alongside it, exactly like SQL's `GROUP BY`. + +**One stage โ€” average a floor's rooms.** `floor-comfort-level-calc` keeps the floor `f` in the `WITH`, so `avg()` produces one value per floor: + +```cypher +WITH f, floor( 50 + (r.temperature - 72) + ... ) AS RoomComfortLevel +WITH f, avg(RoomComfortLevel) AS ComfortLevel +RETURN f.id AS FloorId, ComfortLevel +``` + +**Two stages โ€” average the averages.** `building-alert` aggregates twice. Carrying `f, b` groups the first `avg()` by floor; dropping `f` from the next `WITH` widens the grouping so the second `avg()` rolls the floor averages up into a single building level: + +```cypher +WITH f, b, avg(RoomComfortLevel) AS FloorComfortLevel // rooms -> floor +WITH b, avg(FloorComfortLevel) AS ComfortLevel // floors -> building +``` + +**Filtering on an aggregate.** The `floor-alert` and `building-alert` queries place a `WHERE` *after* the aggregation โ€” like SQL's `HAVING` โ€” so a floor or the building only shows up while its average is outside the comfortable band: + +```cypher +WITH f, avg(RoomComfortLevel) AS ComfortLevel +WHERE ComfortLevel < 40 OR ComfortLevel > 50 +RETURN f.id AS FloorId, ComfortLevel +``` + +Because these are *continuous* queries, the aggregates are maintained **incrementally**: when one room's reading changes, Drasi recomputes only the affected floor and building averages and emits just that change โ€” it never rescans every room. + +### The Python Reaction + +This is where the Python demo differs from a built-in dashboard. Instead of configuring a dashboard reaction, we register a **Python reaction** โ€” a plain callback subscribed to all six queries. Each time a query's result set changes, Drasi calls it with the query id and the diffs, and the callback updates a thread-safe snapshot: + +```python +def on_results(event): + query_id = event["query_id"] + key = KEY_FIELDS[query_id] # "RoomId", "FloorId" or "BuildingId" + with lock: + store = results[query_id] # the UI reads this snapshot + for diff in event["results"]: + row = diff.get("data") or diff.get("after") + if diff["type"] == "DELETE": + store.pop(row[key], None) + else: # ADD or UPDATE: upsert by row identity + store[row[key]] = row + + +await drasi.add_python_reaction("ui", ALL_QUERY_IDS, on_results) +``` + +Every diff carries the affected row in `data` (for an `UPDATE` it equals `after`), so the snapshot is kept by row identity โ€” upsert on `ADD`/`UPDATE`, drop on `DELETE`. The `type` is one of `ADD`, `UPDATE`, or `DELETE`. This works the same for the per-room queries and the aggregate floor/building queries, which emit an `UPDATE` as their average changes. See [Python reactions](../../guides/python-reactions/) for the full callback contract. + +### Embedding the engine in Streamlit + +Streamlit re-runs its script top to bottom on every interaction, which doesn't suit a long-lived async engine. So the engine runs on its own asyncio event loop in a **background daemon thread**, created exactly once with `@st.cache_resource` and shared across every browser session: + +```python +@st.cache_resource +def get_engine(): + engine = ComfortEngine() # spins up the background loop + Drasi + engine.wait_ready() + return engine +``` + +Each run, `app.py` reads `engine.snapshot()` and renders the building, the meter, the floor table and the alert lists. A one-and-a-half-second auto-refresh keeps the view live. Because the reaction only updates the snapshot when a query actually changes, the UI reflects each `UPDATE` the moment it lands โ€” no polling of the database. + +The centerpiece building panel is just a loop over the `building-comfort-ui` rows, grouped by floor, picking a status emoji from each room's comfort level: + +```python +for floor_name in sorted(by_floor): + st.markdown(f"### {floor_name}") + for room in by_floor[floor_name]: + emoji, label = comfort_status(room["ComfortLevel"]) + st.markdown( + f"- **{room['RoomName']}** โ€” comfort **{room['ComfortLevel']}** {emoji} {label}" + ) +``` + +And the reaction renders it live: + +<img src="images/streamlit-building-view.png" width="820" alt="The Building Comfort UI: rooms grouped by floor, each tagged with a comfort level and a green, red, or blue status emoji, with a sidebar of controls"> + +### Other ways to consume a query + +A Python reaction is the most direct way to turn query changes into a UI, but it isn't the only option `drasi-lib` gives you: + +- **Stream the changes** with `async for event in await drasi.query_results("room-alert")` and drive any front end you like. See [Streaming results](../../guides/streaming/). +- **Reuse a published reaction** such as the dashboard reaction plugin, exactly as the [Postgres dashboard example](../../examples/postgres-dashboard/) does โ€” no UI code at all. + +Because Drasi emits only what *changed* โ€” never the full result set โ€” whichever you choose stays live without polling. + +## Clean Up {#cleanup} + +When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database container: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +# Stop containers, keep data +bash scripts/cleanup.sh + +# Stop containers and delete the data volume +bash scripts/cleanup.sh --volumes +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +# Stop containers, keep data +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 + +# Stop containers and delete the data volume +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 -RemoveVolumes +{{< /tab >}} +{{< /tabpane >}} + +## What You Learned {#summary} + +- **Sources** connect Drasi to live data โ€” here, PostgreSQL via Change Data Capture, configured in a few lines of Python. +- **Continuous Queries** with **synthetic joins** let you model relationships (room โ†’ floor โ†’ building) and compute derived values (comfort levels) that stay current automatically. +- **Reactions** turn query changes into action. A **Python reaction** gave you a live UI from a callback and a snapshot โ€” no bespoke web server, and complete control over the rendering. +- Because Drasi emits only what *changed*, everything updates the instant the data does, with no polling. + +From here, try editing the comfort formula or the layout in `app.py`, adding a new alert query, or pointing the source at your own database. diff --git a/tutorials/building-comfort/app.py b/tutorials/building-comfort/app.py new file mode 100644 index 0000000..5afdb52 --- /dev/null +++ b/tutorials/building-comfort/app.py @@ -0,0 +1,226 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Building Comfort -- a Streamlit UI driven by a Drasi Python reaction. + +Run it with: + + streamlit run app.py + +The engine (Drasi + the six continuous queries + one Python reaction) is created +once and shared across every browser session by ``@st.cache_resource``. This +script only *reads* the reaction's snapshot and renders it, and offers controls +that write room changes back to Postgres for Drasi to observe via CDC. +""" + +from __future__ import annotations + +from collections import defaultdict +from typing import Any + +import streamlit as st +from demo.config import COMFORT_MAX, COMFORT_MIN +from demo.engine import ComfortEngine +from demo.queries import ( + BUILDING_COMFORT_UI, + FLOOR_ALERT, + FLOOR_COMFORT_LEVEL, + ROOM_ALERT, +) +from streamlit_autorefresh import st_autorefresh + +st.set_page_config(page_title="Building Comfort", page_icon="๐Ÿข", layout="wide") + + +@st.cache_resource +def get_engine() -> ComfortEngine: + """Create (once) and return the shared Drasi engine. + + The spinner shows only on the very first run, while the engine downloads its + plugins, connects to Postgres and bootstraps the queries. + """ + engine = ComfortEngine() + with st.spinner("Starting Drasi: installing plugins and connecting to Postgres..."): + engine.wait_ready() + return engine + + +def comfort_status(level: float) -> tuple[str, str]: + """Map a comfort level to a status emoji and label.""" + if level > COMFORT_MAX: + return "๐Ÿ”ด", "too hot" + if level < COMFORT_MIN: + return "๐Ÿ”ต", "too cold" + return "๐ŸŸข", "comfortable" + + +def render_building(rows: list[dict[str, Any]]) -> None: + st.subheader("Building Comfort") + if not rows: + st.info("Waiting for room data...") + return + + by_floor: dict[str, list[dict[str, Any]]] = defaultdict(list) + for row in rows: + by_floor[row["FloorName"]].append(row) + + for floor_name in sorted(by_floor): + st.markdown(f"### {floor_name}") + for room in sorted(by_floor[floor_name], key=lambda r: r["RoomName"]): + level = room["ComfortLevel"] + emoji, label = comfort_status(level) + st.markdown( + f"- **{room['RoomName']}** โ€” comfort **{level:g}** {emoji} {label}" + f" ยท ๐ŸŒก๏ธ {room['Temperature']}ยฐF" + f" ๐Ÿ’ง {room['Humidity']}%" + f" ๐Ÿซง {room['CO2']} ppm" + ) + + +def render_overview(rows: list[dict[str, Any]]) -> None: + st.subheader("Overall") + if not rows: + st.metric("Building comfort", "โ€”") + return + + average = sum(r["ComfortLevel"] for r in rows) / len(rows) + emoji, label = comfort_status(average) + st.metric("Building comfort", f"{average:.0f}", help="Average of every room") + st.caption(f"{emoji} {label} ยท comfortable band is {COMFORT_MIN}โ€“{COMFORT_MAX}") + # A simple gauge: the comfort level on a 0โ€“100 scale. + st.progress(min(max(average / 100.0, 0.0), 1.0)) + + +def render_floor_comfort(floor_rows: list[dict[str, Any]], ui_rows: list[dict[str, Any]]) -> None: + st.subheader("Floor comfort") + if not floor_rows: + st.info("Waiting for floor data...") + return + + names = {r["FloorId"]: r["FloorName"] for r in ui_rows} + table = [ + { + "Floor": names.get(r["FloorId"], r["FloorId"]), + "Comfort level": round(r["ComfortLevel"], 1), + } + for r in sorted(floor_rows, key=lambda r: r["FloorId"]) + ] + st.dataframe(table, hide_index=True, use_container_width=True) + + +def render_alerts(room_alerts: list[dict[str, Any]], floor_alerts: list[dict[str, Any]]) -> None: + st.subheader("Comfort alerts") + if room_alerts: + for a in sorted(room_alerts, key=lambda r: r["RoomId"]): + st.markdown( + f"- โš ๏ธ **{a['RoomName']}** (`{a['RoomId']}`) โ€” comfort **{a['ComfortLevel']:g}**" + ) + else: + st.success("All rooms are comfortable.") + + st.subheader("Floor alerts") + if floor_alerts: + for a in sorted(floor_alerts, key=lambda r: r["FloorId"]): + st.markdown( + f"- โš ๏ธ **{a['FloorName']}** (`{a['FloorId']}`) โ€” comfort **{a['ComfortLevel']:g}**" + ) + else: + st.success("All floors are comfortable.") + + +def render_controls(engine: ComfortEngine, snapshot: dict[str, Any]) -> None: + st.sidebar.header("Drive the demo") + st.sidebar.caption( + "These controls write SQL UPDATEs to Postgres. Drasi observes them via " + "CDC and the view updates on its own โ€” there is no middle tier." + ) + + # Simulation mode. + simulate = st.sidebar.toggle("Simulation mode", value=snapshot["simulation"]) + if simulate != snapshot["simulation"]: + engine.set_simulation(simulate) + st.rerun() + if simulate: + st.sidebar.caption("Assigning a random room new readings every few seconds.") + + st.sidebar.divider() + + # Reset everything. + if st.sidebar.button("Reset all rooms", use_container_width=True): + engine.reset_room() + st.toast("Reset every room to comfortable.") + + st.sidebar.divider() + + # Per-room controls. + ui_rows = snapshot["results"].get(BUILDING_COMFORT_UI, []) + if not ui_rows: + return + + by_id = {r["RoomId"]: r for r in ui_rows} + options = sorted(by_id, key=lambda rid: (by_id[rid]["FloorName"], by_id[rid]["RoomName"])) + + st.sidebar.subheader("Set a room") + room_id = st.sidebar.selectbox( + "Room", + options, + format_func=lambda rid: f"{by_id[rid]['FloorName']} / {by_id[rid]['RoomName']}", + ) + current = by_id[room_id] + + with st.sidebar.form("set-room"): + temperature = st.number_input("Temperature (ยฐF)", value=int(current["Temperature"])) + humidity = st.number_input("Humidity (%)", value=int(current["Humidity"])) + co2 = st.number_input("COโ‚‚ (ppm)", value=int(current["CO2"])) + submitted = st.form_submit_button("Set room", use_container_width=True) + if submitted: + engine.set_room(room_id, int(temperature), int(humidity), int(co2)) + st.toast(f"Updated {current['RoomName']}.") + + if st.sidebar.button("Reset this room", use_container_width=True): + engine.reset_room(room_id) + st.toast(f"Reset {current['RoomName']} to comfortable.") + + +def main() -> None: + engine = get_engine() + + # Re-run every 1.5s so the reaction's latest snapshot is rendered live. + st_autorefresh(interval=1500, key="comfort-refresh") + + snapshot = engine.snapshot() + if snapshot["error"]: + st.error(f"The Drasi engine failed: {snapshot['error']}") + return + + results = snapshot["results"] + ui_rows = results.get(BUILDING_COMFORT_UI, []) + building_name = ui_rows[0]["BuildingName"] if ui_rows else "Building Comfort" + + st.title(f"๐Ÿข {building_name}") + + render_controls(engine, snapshot) + + left, right = st.columns([2, 1]) + with left: + render_building(ui_rows) + with right: + render_overview(ui_rows) + render_alerts(results.get(ROOM_ALERT, []), results.get(FLOOR_ALERT, [])) + + render_floor_comfort(results.get(FLOOR_COMFORT_LEVEL, []), ui_rows) + + +if __name__ == "__main__": + main() diff --git a/tutorials/building-comfort/database/docker-compose.yml b/tutorials/building-comfort/database/docker-compose.yml new file mode 100644 index 0000000..b87c9af --- /dev/null +++ b/tutorials/building-comfort/database/docker-compose.yml @@ -0,0 +1,44 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: building-comfort + +services: + postgres: + image: postgres:14-alpine + container_name: building-comfort-postgres + environment: + POSTGRES_DB: building_comfort + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres_admin + command: + - "postgres" + - "-c" + - "wal_level=logical" + - "-c" + - "max_replication_slots=10" + - "-c" + - "max_wal_senders=10" + ports: + - "${POSTGRES_HOST_PORT:-5732}:5432" + volumes: + - building_comfort_postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d building_comfort"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + building_comfort_postgres_data: diff --git a/tutorials/building-comfort/database/init.sql b/tutorials/building-comfort/database/init.sql new file mode 100644 index 0000000..41e3ed8 --- /dev/null +++ b/tutorials/building-comfort/database/init.sql @@ -0,0 +1,146 @@ +-- Copyright 2026 The Drasi Authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +-- Building Comfort Tutorial Database Schema +-- +-- Models a smart building as Building -> Floor -> Room. Each Room carries +-- temperature, humidity and co2 sensor readings. Table names are quoted and +-- PascalCase ("Building", "Floor", "Room") so that the node labels Drasi sees +-- match the Cypher continuous queries (which use (r:Room), (f:Floor), +-- (b:Building)) without any change to the query text. + +-- Suppress noisy output during setup. +\set QUIET on +SET client_min_messages = ERROR; + +-- Create a user with replication privileges for CDC. +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'drasi_user') THEN + CREATE USER drasi_user WITH REPLICATION LOGIN PASSWORD 'drasi_password'; + END IF; +END +$$; + +-- Grant permissions on the database. +GRANT CREATE ON DATABASE building_comfort TO drasi_user; +GRANT ALL PRIVILEGES ON DATABASE building_comfort TO drasi_user; + +-- Drop existing tables if they exist. +DROP TABLE IF EXISTS "Room" CASCADE; +DROP TABLE IF EXISTS "Floor" CASCADE; +DROP TABLE IF EXISTS "Building" CASCADE; + +-- Building table. +CREATE TABLE "Building" ( + id VARCHAR(50) PRIMARY KEY, + name VARCHAR(100) NOT NULL +); + +-- Floor table (each Floor belongs to a Building). +CREATE TABLE "Floor" ( + id VARCHAR(50) PRIMARY KEY, + name VARCHAR(100) NOT NULL, + building_id VARCHAR(50) NOT NULL, + FOREIGN KEY (building_id) REFERENCES "Building"(id) ON DELETE CASCADE +); + +-- Room table (each Room belongs to a Floor and holds sensor readings). +CREATE TABLE "Room" ( + id VARCHAR(50) PRIMARY KEY, + name VARCHAR(100) NOT NULL, + temperature INTEGER, + humidity INTEGER, + co2 INTEGER, + floor_id VARCHAR(50) NOT NULL, + FOREIGN KEY (floor_id) REFERENCES "Floor"(id) ON DELETE CASCADE +); + +-- Set REPLICA IDENTITY to FULL so change events include every column. +ALTER TABLE "Building" REPLICA IDENTITY FULL; +ALTER TABLE "Floor" REPLICA IDENTITY FULL; +ALTER TABLE "Room" REPLICA IDENTITY FULL; + +-- Ensure drasi_user owns the tables. +ALTER TABLE "Building" OWNER TO drasi_user; +ALTER TABLE "Floor" OWNER TO drasi_user; +ALTER TABLE "Room" OWNER TO drasi_user; + +-- Grant permissions to drasi_user. +GRANT USAGE ON SCHEMA public TO drasi_user; +GRANT SELECT ON ALL TABLES IN SCHEMA public TO drasi_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO drasi_user; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO drasi_user; + +-- Create the publication for logical replication. +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'drasi_building_comfort_pub') THEN + CREATE PUBLICATION drasi_building_comfort_pub FOR TABLE "Building", "Floor", "Room"; + END IF; +END +$$; + +-- Seed the building hierarchy: 1 building, 3 floors, 9 rooms. +-- Every room starts comfortable (temperature=70, humidity=40, co2=10), +-- which yields a comfort level of floor(50 + (70-72) + (40-42) + 0) = 46. +-- Seeding happens BEFORE the replication slot is created so the existing rows +-- are loaded via bootstrap and not replayed as change events. +INSERT INTO "Building" (id, name) +SELECT * FROM (VALUES ('building_01', 'Building 01')) AS d(id, name) +WHERE NOT EXISTS (SELECT 1 FROM "Building"); + +INSERT INTO "Floor" (id, name, building_id) +SELECT * FROM (VALUES + ('floor_01_01', 'Floor 01', 'building_01'), + ('floor_01_02', 'Floor 02', 'building_01'), + ('floor_01_03', 'Floor 03', 'building_01') +) AS d(id, name, building_id) +WHERE NOT EXISTS (SELECT 1 FROM "Floor"); + +INSERT INTO "Room" (id, name, temperature, humidity, co2, floor_id) +SELECT * FROM (VALUES + ('room_01_01_01', 'Room 01', 70, 40, 10, 'floor_01_01'), + ('room_01_01_02', 'Room 02', 70, 40, 10, 'floor_01_01'), + ('room_01_01_03', 'Room 03', 70, 40, 10, 'floor_01_01'), + ('room_01_02_01', 'Room 01', 70, 40, 10, 'floor_01_02'), + ('room_01_02_02', 'Room 02', 70, 40, 10, 'floor_01_02'), + ('room_01_02_03', 'Room 03', 70, 40, 10, 'floor_01_02'), + ('room_01_03_01', 'Room 01', 70, 40, 10, 'floor_01_03'), + ('room_01_03_02', 'Room 02', 70, 40, 10, 'floor_01_03'), + ('room_01_03_03', 'Room 03', 70, 40, 10, 'floor_01_03') +) AS d(id, name, temperature, humidity, co2, floor_id) +WHERE NOT EXISTS (SELECT 1 FROM "Room"); + +-- Create the replication slot AFTER seeding. The slot captures only changes +-- made after this point; existing rows are retrieved via bootstrap when the +-- queries start, so the seed data is not double-counted. +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'drasi_building_comfort_slot') THEN + PERFORM pg_create_logical_replication_slot('drasi_building_comfort_slot', 'pgoutput'); + END IF; +END +$$; + +-- Summary. +SET client_min_messages = NOTICE; +DO $$ +BEGIN + RAISE NOTICE 'Building Comfort database initialized successfully!'; + RAISE NOTICE 'Tables: Building, Floor, Room'; + RAISE NOTICE 'Publication: drasi_building_comfort_pub'; + RAISE NOTICE 'Replication slot: drasi_building_comfort_slot'; +END +$$; diff --git a/tutorials/building-comfort/demo/__init__.py b/tutorials/building-comfort/demo/__init__.py new file mode 100644 index 0000000..327c330 --- /dev/null +++ b/tutorials/building-comfort/demo/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The Building Comfort demo package: an embedded Drasi engine and its queries.""" + +from .engine import ComfortEngine + +__all__ = ["ComfortEngine"] diff --git a/tutorials/building-comfort/demo/config.py b/tutorials/building-comfort/demo/config.py new file mode 100644 index 0000000..3457798 --- /dev/null +++ b/tutorials/building-comfort/demo/config.py @@ -0,0 +1,88 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Configuration for the Building Comfort tutorial. + +Everything the demo needs to reach Postgres, plus the demo's constants, read +from the environment with the same defaults the tutorial's database uses. Copy +``.env.example`` to ``.env`` (and ``source`` it) to override any of them. +""" + +from __future__ import annotations + +import os + +# --- PostgreSQL connection --------------------------------------------------- + +POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "localhost") +POSTGRES_PORT = int(os.environ.get("POSTGRES_PORT", "5732")) +POSTGRES_DATABASE = os.environ.get("POSTGRES_DATABASE", "building_comfort") +POSTGRES_USER = os.environ.get("POSTGRES_USER", "drasi_user") +POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "drasi_password") +POSTGRES_SSLMODE = os.environ.get("POSTGRES_SSLMODE", "prefer") + +# The Drasi source plugin spells the connection keys the same way the Node.js +# binding does (camelCase); Drasi passes plugin config through untouched. +SOURCE_CONNECTION = { + "host": POSTGRES_HOST, + "port": POSTGRES_PORT, + "database": POSTGRES_DATABASE, + "user": POSTGRES_USER, + "password": POSTGRES_PASSWORD, + "sslMode": POSTGRES_SSLMODE, +} + +# psycopg spells the database key ``dbname`` rather than ``database``. The demo +# writes room updates directly to Postgres through this connection so Drasi +# observes them via CDC -- exactly as a real building-management app would. +PSYCOPG_CONNECTION = { + "host": POSTGRES_HOST, + "port": POSTGRES_PORT, + "dbname": POSTGRES_DATABASE, + "user": POSTGRES_USER, + "password": POSTGRES_PASSWORD, +} + +# --- Drasi source / bootstrap ------------------------------------------------ + +# The Postgres source replicates these three tables. They are schema-qualified +# here (``public.Room``) while ``tableKeys`` uses the bare table name; the node +# label Drasi sees is the bare name, so the Cypher matches (r:Room) etc. +SOURCE_CONFIG = { + **SOURCE_CONNECTION, + "tables": ["public.Building", "public.Floor", "public.Room"], + "tableKeys": [ + {"table": "Building", "keyColumns": ["id"]}, + {"table": "Floor", "keyColumns": ["id"]}, + {"table": "Room", "keyColumns": ["id"]}, + ], + "slotName": "drasi_building_comfort_slot", + "publicationName": "drasi_building_comfort_pub", +} + +# The bootstrap provider loads the rows that already exist when the query starts, +# so the dashboard is populated before any change arrives. +BOOTSTRAP_CONFIG = {"kind": "postgres", **SOURCE_CONNECTION} + +# --- Demo constants ---------------------------------------------------------- + +# Comfortable defaults. floor(50 + (70-72) + (40-42) + 0) = 46, inside 40-50. +COMFORT_DEFAULTS = {"temperature": 70, "humidity": 40, "co2": 10} + +# The comfortable band. A room, floor or building outside it raises an alert. +COMFORT_MIN = 40 +COMFORT_MAX = 50 + +# How often simulation mode assigns a random room new readings, in seconds. +SIMULATION_INTERVAL_S = float(os.environ.get("SIMULATION_INTERVAL_S", "3")) diff --git a/tutorials/building-comfort/demo/engine.py b/tutorials/building-comfort/demo/engine.py new file mode 100644 index 0000000..5832387 --- /dev/null +++ b/tutorials/building-comfort/demo/engine.py @@ -0,0 +1,244 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The embedded Drasi engine that powers the Streamlit UI. + +Streamlit re-runs its script top to bottom on every interaction, which is a poor +fit for a long-lived async engine. So the Drasi engine runs on its own asyncio +event loop in a background daemon thread, created exactly once (Streamlit caches +it with ``@st.cache_resource``). The engine: + +1. installs the ``source/postgres`` and ``bootstrap/postgres`` plugins, +2. adds the Postgres source and the six continuous queries, and +3. registers a single **Python reaction** over all six queries. + +The reaction keeps a thread-safe snapshot of every query's result set. Streamlit +reads that snapshot each run and renders it; there is no dashboard or SSE +reaction, and no bespoke web server -- the reaction *is* the application. + +The UI's controls (set a room, reset, simulate) write plain SQL ``UPDATE``s to +Postgres via ``psycopg``. Drasi observes those writes through logical +replication and re-runs the affected queries, which calls the reaction, which +updates the snapshot -- the same path a real building-management app would take. +""" + +from __future__ import annotations + +import os + +# Quiet the engine's default INFO logging so it doesn't swamp Streamlit's +# console. Set RUST_LOG=info (or debug) to watch what the engine is doing. This +# must happen before ``drasi`` is imported. +os.environ.setdefault("RUST_LOG", "warn") + +import asyncio # noqa: E402 +import random # noqa: E402 +import threading # noqa: E402 +from typing import Any # noqa: E402 + +import psycopg # noqa: E402 + +from drasi import Drasi # noqa: E402 +from drasi.types import QueryResultEvent # noqa: E402 + +from .config import ( # noqa: E402 + BOOTSTRAP_CONFIG, + COMFORT_DEFAULTS, + PSYCOPG_CONNECTION, + SIMULATION_INTERVAL_S, + SOURCE_CONFIG, +) +from .queries import ALL_QUERY_IDS, KEY_FIELDS, QUERIES # noqa: E402 + +# Ranges chosen to straddle the comfortable band, so simulation makes alerts +# come and go rather than sitting at one extreme. +_SIM_TEMPERATURE = (55, 85) +_SIM_HUMIDITY = (20, 55) +_SIM_CO2 = (5, 904) + + +def _update_rooms(sql: str, params: tuple[Any, ...] = ()) -> None: + """Run one UPDATE against the Room table on a short-lived connection.""" + with psycopg.connect(**PSYCOPG_CONNECTION) as conn: + with conn.cursor() as cur: + cur.execute(sql, params) + + +class ComfortEngine: + """Runs Drasi on a background loop and exposes a thread-safe snapshot.""" + + def __init__(self) -> None: + self._lock = threading.Lock() + # Per query: a dict keyed by that query's primary-key field. The reaction + # applies diffs into it; the UI reads a snapshot of the values. + self._results: dict[str, dict[Any, dict[str, Any]]] = {} + self._version = 0 + self._simulation = False + + self._loop: asyncio.AbstractEventLoop | None = None + self._drasi: Drasi | None = None + self._sim_task: asyncio.Task[None] | None = None + self._room_ids: list[str] = [] + + self._ready = threading.Event() + self._error: BaseException | None = None + + self._thread = threading.Thread(target=self._run, name="drasi-comfort-engine", daemon=True) + self._thread.start() + + # -- lifecycle (background thread) --------------------------------------- + + def _run(self) -> None: + loop = asyncio.new_event_loop() + self._loop = loop + asyncio.set_event_loop(loop) + try: + loop.run_until_complete(self._start()) + except BaseException as exc: # noqa: BLE001 - surfaced to the UI thread + self._error = exc + self._ready.set() + return + self._ready.set() + loop.run_forever() + + async def _start(self) -> None: + self._drasi = await Drasi.create("building-comfort") + + for plugin in ("source/postgres", "bootstrap/postgres"): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + await self._drasi.add_source("postgres", "db", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + + for query_id, cypher, joins in QUERIES: + await self._drasi.add_query(query_id, cypher, ["db"], joins=joins or None) + for query_id, _, _ in QUERIES: + await self._drasi.wait_for_query(query_id) + + # Prime the snapshot from the bootstrapped result sets, then let the + # reaction keep it current. + for query_id, _, _ in QUERIES: + rows = await self._drasi.get_query_results(query_id) + key_field = KEY_FIELDS[query_id] + with self._lock: + self._results[query_id] = {row[key_field]: row for row in rows} + self._room_ids = await self._load_room_ids() + + await self._drasi.add_python_reaction("ui", ALL_QUERY_IDS, self._on_results) + + def _on_results(self, event: QueryResultEvent) -> None: + """Apply a batch of diffs to the snapshot. + + ``add_python_reaction`` calls this synchronously. Every diff carries the + affected row in ``data`` (for an UPDATE it equals ``after``), so the + snapshot is maintained by row identity: upsert on ADD/UPDATE, drop on + DELETE. Result sets are tiny, so this stays cheap. + """ + query_id = event["query_id"] + key_field = KEY_FIELDS[query_id] + with self._lock: + store = self._results.setdefault(query_id, {}) + for diff in event["results"]: + row = diff.get("data") or diff.get("after") + if row is None: + continue + key = row.get(key_field) + if diff["type"] == "DELETE": + store.pop(key, None) + else: + store[key] = row + self._version += 1 + + async def _load_room_ids(self) -> list[str]: + def _query() -> list[str]: + with psycopg.connect(**PSYCOPG_CONNECTION) as conn: + with conn.cursor() as cur: + cur.execute('SELECT id FROM "Room" ORDER BY id') + return [row[0] for row in cur.fetchall()] + + return await asyncio.get_event_loop().run_in_executor(None, _query) + + # -- readiness ----------------------------------------------------------- + + def wait_ready(self, timeout: float = 180.0) -> None: + """Block until the engine is running, raising if it failed to start.""" + if not self._ready.wait(timeout): + raise TimeoutError("the Drasi engine did not start in time") + if self._error is not None: + raise RuntimeError(f"the Drasi engine failed to start: {self._error}") + + # -- reads (any thread) -------------------------------------------------- + + def snapshot(self) -> dict[str, Any]: + """Return the current result set of every query, plus engine flags.""" + with self._lock: + results = {qid: list(store.values()) for qid, store in self._results.items()} + return { + "version": self._version, + "simulation": self._simulation, + "results": results, + "error": str(self._error) if self._error else None, + } + + # -- writes (any thread -> Postgres, observed via CDC) ------------------- + + def set_room(self, room_id: str, temperature: int, humidity: int, co2: int) -> None: + _update_rooms( + 'UPDATE "Room" SET temperature = %s, humidity = %s, co2 = %s WHERE id = %s', + (temperature, humidity, co2, room_id), + ) + + def reset_room(self, room_id: str | None = None) -> None: + """Reset one room, or every room when ``room_id`` is None.""" + d = COMFORT_DEFAULTS + if room_id is None: + _update_rooms( + 'UPDATE "Room" SET temperature = %s, humidity = %s, co2 = %s', + (d["temperature"], d["humidity"], d["co2"]), + ) + else: + self.set_room(room_id, d["temperature"], d["humidity"], d["co2"]) + + def set_simulation(self, enabled: bool) -> None: + """Turn hands-free simulation on or off.""" + if self._loop is None: + return + future = asyncio.run_coroutine_threadsafe(self._set_simulation(enabled), self._loop) + future.result(timeout=10) + + async def _set_simulation(self, enabled: bool) -> None: + if enabled and self._sim_task is None: + if not self._room_ids: + self._room_ids = await self._load_room_ids() + self._sim_task = asyncio.ensure_future(self._simulate()) + self._simulation = True + elif not enabled and self._sim_task is not None: + self._sim_task.cancel() + self._sim_task = None + self._simulation = False + + async def _simulate(self) -> None: + loop = asyncio.get_event_loop() + try: + while True: + room = random.choice(self._room_ids) + temperature = random.randint(*_SIM_TEMPERATURE) + humidity = random.randint(*_SIM_HUMIDITY) + co2 = random.randint(*_SIM_CO2) + # Run the blocking write off the event loop so the reaction keeps + # delivering while simulation is on. + await loop.run_in_executor(None, self.set_room, room, temperature, humidity, co2) + await asyncio.sleep(SIMULATION_INTERVAL_S) + except asyncio.CancelledError: + pass diff --git a/tutorials/building-comfort/demo/queries.py b/tutorials/building-comfort/demo/queries.py new file mode 100644 index 0000000..4b21c31 --- /dev/null +++ b/tutorials/building-comfort/demo/queries.py @@ -0,0 +1,203 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The six continuous queries, ported from the Drasi Server tutorial. + +Each query computes a comfort level with the same formula. A value between 40 +and 50 is comfortable; the seed values (70F, 40%, 10 ppm) give +``floor(50 + (70-72) + (40-42) + 0) = 46``. + +The Room -> Floor -> Building hierarchy is walked through two *synthetic joins*. +Drasi does not read Postgres foreign keys, so each query declares the +relationships it needs. In ``drasi-lib`` a join is a ``Join`` mapping, passed to +``add_query(..., joins=[...])`` -- the same shape the Drasi Server YAML uses. +""" + +from __future__ import annotations + +from typing import Any + +# The comfort formula, shared by every query. ``r`` is the Room node. +_COMFORT = ( + "floor( 50 + (r.temperature - 72) + (r.humidity - 42)" + " + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END )" +) + +# Synthetic joins: Room.floor_id -> Floor.id, Floor.building_id -> Building.id. +PART_OF_FLOOR: dict[str, Any] = { + "id": "PART_OF_FLOOR", + "keys": [ + {"label": "Room", "property": "floor_id"}, + {"label": "Floor", "property": "id"}, + ], +} +PART_OF_BUILDING: dict[str, Any] = { + "id": "PART_OF_BUILDING", + "keys": [ + {"label": "Floor", "property": "building_id"}, + {"label": "Building", "property": "id"}, + ], +} + +# Query ids, used both to register the queries and to index the reaction's state. +BUILDING_COMFORT_UI = "building-comfort-ui" +BUILDING_COMFORT_LEVEL = "building-comfort-level-calc" +FLOOR_COMFORT_LEVEL = "floor-comfort-level-calc" +ROOM_ALERT = "room-alert" +FLOOR_ALERT = "floor-alert" +BUILDING_ALERT = "building-alert" + + +# Each entry: (id, cypher, joins). ``joins`` is empty for queries that match a +# single label. The engine registers them in order. +QUERIES: list[tuple[str, str, list[dict[str, Any]]]] = [ + # Query 1: per-room comfort level (drives the building view). + ( + BUILDING_COMFORT_UI, + f""" + MATCH + (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH + r, f, b, + {_COMFORT} AS ComfortLevel + RETURN + r.id AS RoomId, + r.name AS RoomName, + f.id AS FloorId, + f.name AS FloorName, + b.id AS BuildingId, + b.name AS BuildingName, + r.temperature AS Temperature, + r.humidity AS Humidity, + r.co2 AS CO2, + ComfortLevel + """, + [PART_OF_FLOOR, PART_OF_BUILDING], + ), + # Query 2: overall building comfort (avg of floor averages). + ( + BUILDING_COMFORT_LEVEL, + f""" + MATCH + (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH + b, + {_COMFORT} AS RoomComfortLevel + WITH + b, + avg(RoomComfortLevel) AS FloorComfortLevel + WITH + b, + avg(FloorComfortLevel) AS ComfortLevel + RETURN + b.id AS BuildingId, + ComfortLevel + """, + [PART_OF_FLOOR, PART_OF_BUILDING], + ), + # Query 3: per-floor comfort (avg of the floor's rooms). + ( + FLOOR_COMFORT_LEVEL, + f""" + MATCH + (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH + f, + {_COMFORT} AS RoomComfortLevel + WITH + f, + avg(RoomComfortLevel) AS ComfortLevel + RETURN + f.id AS FloorId, + ComfortLevel + """, + [PART_OF_FLOOR], + ), + # Query 4: rooms outside the comfortable band (40-50). + ( + ROOM_ALERT, + f""" + MATCH + (r:Room) + WITH + r.id AS RoomId, + r.name AS RoomName, + {_COMFORT} AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN + RoomId, RoomName, ComfortLevel + """, + [], + ), + # Query 5: floors whose average comfort is outside 40-50. + ( + FLOOR_ALERT, + f""" + MATCH + (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH + f, + {_COMFORT} AS RoomComfortLevel + WITH + f, + avg(RoomComfortLevel) AS ComfortLevel + WHERE + ComfortLevel < 40 OR ComfortLevel > 50 + RETURN + f.id AS FloorId, + f.name AS FloorName, + ComfortLevel + """, + [PART_OF_FLOOR], + ), + # Query 6: the building when its overall comfort is outside 40-50. + ( + BUILDING_ALERT, + f""" + MATCH + (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH + f, b, + {_COMFORT} AS RoomComfortLevel + WITH + f, b, + avg(RoomComfortLevel) AS FloorComfortLevel + WITH + b, + avg(FloorComfortLevel) AS ComfortLevel + WHERE + ComfortLevel < 40 OR ComfortLevel > 50 + RETURN + b.id AS BuildingId, + b.name AS BuildingName, + ComfortLevel + """, + [PART_OF_FLOOR, PART_OF_BUILDING], + ), +] + +# The query ids the UI reaction subscribes to (all of them). +ALL_QUERY_IDS = [q[0] for q in QUERIES] + +# The primary-key field of each query's rows. The reaction keeps a snapshot of +# every query keyed by this field, so it can apply ADD/UPDATE/DELETE diffs by row +# identity rather than rescanning. +KEY_FIELDS: dict[str, str] = { + BUILDING_COMFORT_UI: "RoomId", + BUILDING_COMFORT_LEVEL: "BuildingId", + FLOOR_COMFORT_LEVEL: "FloorId", + ROOM_ALERT: "RoomId", + FLOOR_ALERT: "FloorId", + BUILDING_ALERT: "BuildingId", +} diff --git a/tutorials/building-comfort/images/streamlit-building-view.png b/tutorials/building-comfort/images/streamlit-building-view.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5522984c546a296b64daffbd635eda8a67456a GIT binary patch literal 158171 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ΠvJr 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 literal 0 HcmV?d00001 diff --git a/tutorials/building-comfort/requirements.txt b/tutorials/building-comfort/requirements.txt new file mode 100644 index 0000000..9c5b83b --- /dev/null +++ b/tutorials/building-comfort/requirements.txt @@ -0,0 +1,11 @@ +# Building Comfort tutorial dependencies. +# +# pip install -r requirements.txt +# +# drasi-lib is the Python binding for Drasi's embeddable engine. It downloads the +# Postgres source/bootstrap plugins from ghcr.io at runtime, so the first run +# needs network access. +drasi-lib>=0.2,<1 +streamlit>=1.32 +streamlit-autorefresh>=1.0 +psycopg[binary]>=3.1 diff --git a/tutorials/building-comfort/scripts/cleanup.ps1 b/tutorials/building-comfort/scripts/cleanup.ps1 new file mode 100644 index 0000000..a3f7382 --- /dev/null +++ b/tutorials/building-comfort/scripts/cleanup.ps1 @@ -0,0 +1,48 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Cleanup Script (Windows) +# Stops the PostgreSQL container. The app runs in the foreground, so stop it +# first with Ctrl+C. Pass -RemoveVolumes to also delete the database volume. + +param( + [switch]$RemoveVolumes +) + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$DatabaseDir = Join-Path $ScriptDir "..\database" + +Write-Host "=== Building Comfort - Cleanup ===" +Write-Host "" + +Push-Location $DatabaseDir +try { + if ($RemoveVolumes) { + Write-Host "Removing container and volumes..." + docker compose down -v 2>&1 | Write-Host + } else { + Write-Host "Removing container (keeping volumes)..." + docker compose down 2>&1 | Write-Host + } +} +finally { + Pop-Location +} + +Write-Host "" +Write-Host "=== Cleanup complete! ===" +Write-Host "" +Write-Host "Options:" +Write-Host " scripts/cleanup.ps1 # Stop containers, keep data volumes" +Write-Host " scripts/cleanup.ps1 -RemoveVolumes # Stop containers and remove data volumes" diff --git a/tutorials/building-comfort/scripts/cleanup.sh b/tutorials/building-comfort/scripts/cleanup.sh new file mode 100755 index 0000000..852b4a0 --- /dev/null +++ b/tutorials/building-comfort/scripts/cleanup.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Cleanup Script +# Removes the PostgreSQL container. The app runs in the foreground, so stop it +# first with Ctrl+C. Pass --volumes to also delete the database volume. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DATABASE_DIR="$SCRIPT_DIR/../database" +REMOVE_VOLUMES="${1:-}" + +echo "=== Building Comfort - Cleanup ===" +echo + +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "Warning: docker-compose not found, skipping container cleanup" + exit 0 +fi + +echo "Stopping PostgreSQL container..." +cd "$DATABASE_DIR" + +if [ "$REMOVE_VOLUMES" = "--volumes" ] || [ "$REMOVE_VOLUMES" = "-v" ]; then + echo "Removing container and volumes..." + $COMPOSE_CMD down -v +else + echo "Removing container (keeping volumes)..." + $COMPOSE_CMD down +fi + +echo +echo "=== Cleanup complete! ===" +echo +echo "Options:" +echo " $0 # Stop containers, keep data volumes" +echo " $0 --volumes # Stop containers and remove data volumes" diff --git a/tutorials/building-comfort/scripts/setup-database.ps1 b/tutorials/building-comfort/scripts/setup-database.ps1 new file mode 100644 index 0000000..98f02f0 --- /dev/null +++ b/tutorials/building-comfort/scripts/setup-database.ps1 @@ -0,0 +1,105 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Setup Database Script (Windows) +# Starts PostgreSQL with logical replication (WAL) enabled for CDC and seeds the +# building hierarchy (1 Building, 3 Floors, 9 Rooms). + +# Note: we deliberately do not set `$ErrorActionPreference = "Stop"` because the +# docker CLI writes progress to stderr, which PowerShell would otherwise treat +# as a terminating error. We check $LASTEXITCODE explicitly instead. + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$DatabaseDir = Join-Path $ScriptDir "..\database" + +Write-Host "=== Building Comfort - Database Setup ===" +Write-Host "" + +if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { + Write-Host "Error: Docker is not installed or not in PATH" + Write-Host "Please install Docker: https://docs.docker.com/get-docker/" + exit 1 +} + +docker info 2>&1 | Out-Null +if ($LASTEXITCODE -ne 0) { + Write-Host "Error: Docker daemon is not running" + Write-Host "Please start Docker and try again" + exit 1 +} + +Push-Location $DatabaseDir +try { + Write-Host "Stopping any existing PostgreSQL container..." + docker compose down -v 2>&1 | Out-Null + + Write-Host "Starting PostgreSQL with WAL replication..." + docker compose up -d 2>&1 | Write-Host + if ($LASTEXITCODE -ne 0) { + Write-Host "Error: failed to start PostgreSQL container" + exit 1 + } + + Write-Host "Waiting for PostgreSQL to be ready..." + $maxRetries = 30 + $ready = $false + for ($i = 1; $i -le $maxRetries; $i++) { + docker exec building-comfort-postgres pg_isready -U postgres -d building_comfort 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "PostgreSQL is ready!" + $ready = $true + break + } + Write-Host " Waiting... ($i/$maxRetries)" + Start-Sleep -Seconds 2 + } + + if (-not $ready) { + Write-Host "Error: PostgreSQL failed to start within the timeout" + Write-Host "Check logs with: docker logs building-comfort-postgres" + exit 1 + } + + # Apply the schema and seed data over stdin. We pipe init.sql into psql with + # `docker exec -i` instead of bind-mounting it into the container, so the same + # flow works for the dev container, Codespaces, and bare-metal runs. + Write-Host "Applying schema and seed data..." + Get-Content (Join-Path $DatabaseDir "init.sql") | ` + docker exec -i building-comfort-postgres psql -v ON_ERROR_STOP=1 -U postgres -d building_comfort + if ($LASTEXITCODE -ne 0) { + Write-Host "Error: failed to apply schema and seed data" + exit 1 + } + + Write-Host "" + Write-Host "Seeded rooms:" + # Pipe the query over stdin to avoid PowerShell mangling the embedded quotes + # around the case-sensitive "Room" identifier when passing -c to docker. + 'SELECT id, name, temperature, humidity, co2, floor_id FROM "Room" ORDER BY id;' | ` + docker exec -i building-comfort-postgres psql -U drasi_user -d building_comfort + + Write-Host "" + Write-Host "=== Database setup complete! ===" + Write-Host "" + $hostPort = if ($env:POSTGRES_HOST_PORT) { $env:POSTGRES_HOST_PORT } else { "5732" } + Write-Host "Connection details:" + Write-Host " Host: localhost" + Write-Host " Port: $hostPort" + Write-Host " Database: building_comfort" + Write-Host " User: drasi_user" + Write-Host " Password: drasi_password" +} +finally { + Pop-Location +} diff --git a/tutorials/building-comfort/scripts/setup-database.sh b/tutorials/building-comfort/scripts/setup-database.sh new file mode 100755 index 0000000..9b1d14d --- /dev/null +++ b/tutorials/building-comfort/scripts/setup-database.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Setup Database Script +# Starts PostgreSQL with logical replication (WAL) enabled for CDC and seeds the +# building hierarchy (1 Building, 3 Floors, 9 Rooms). + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DATABASE_DIR="$SCRIPT_DIR/../database" + +echo "=== Building Comfort - Database Setup ===" +echo + +if ! command -v docker &> /dev/null; then + echo "Error: Docker is not installed or not in PATH" + echo "Please install Docker: https://docs.docker.com/get-docker/" + exit 1 +fi + +if ! docker info &> /dev/null; then + echo "Error: Docker daemon is not running" + echo "Please start Docker and try again" + exit 1 +fi + +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "Error: docker-compose is not installed" + echo "Please install Docker Compose: https://docs.docker.com/compose/install/" + exit 1 +fi + +echo "Using: $COMPOSE_CMD" +echo + +echo "Stopping any existing PostgreSQL container..." +cd "$DATABASE_DIR" +$COMPOSE_CMD down -v 2>/dev/null || true + +echo "Starting PostgreSQL with WAL replication..." +$COMPOSE_CMD up -d + +echo "Waiting for PostgreSQL to be ready..." +MAX_RETRIES=30 +RETRY_COUNT=0 +while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + if docker exec building-comfort-postgres pg_isready -U postgres -d building_comfort &> /dev/null; then + echo "PostgreSQL is ready!" + break + fi + RETRY_COUNT=$((RETRY_COUNT + 1)) + echo " Waiting... ($RETRY_COUNT/$MAX_RETRIES)" + sleep 2 +done + +if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then + echo "Error: PostgreSQL failed to start within the timeout" + echo "Check logs with: docker logs building-comfort-postgres" + exit 1 +fi + +# Apply the schema and seed data over stdin. We pipe init.sql into psql with +# `docker exec -i` instead of bind-mounting it into the container. A bind mount +# would break under docker-outside-of-docker (the dev container's file path does +# not exist on the host's Docker daemon); piping over stdin works the same way +# for the dev container, Codespaces, and bare-metal runs. +echo "Applying schema and seed data..." +docker exec -i building-comfort-postgres \ + psql -v ON_ERROR_STOP=1 -U postgres -d building_comfort < "$DATABASE_DIR/init.sql" + +echo +echo "Verifying database setup..." + +PUB_EXISTS=$(docker exec building-comfort-postgres psql -U drasi_user -d building_comfort -tAc \ + "SELECT 1 FROM pg_publication WHERE pubname = 'drasi_building_comfort_pub';" 2>/dev/null || echo "0") +if [ "$PUB_EXISTS" = "1" ]; then + echo " Publication: drasi_building_comfort_pub [OK]" +else + echo " Publication: drasi_building_comfort_pub [MISSING]" +fi + +SLOT_EXISTS=$(docker exec building-comfort-postgres psql -U drasi_user -d building_comfort -tAc \ + "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'drasi_building_comfort_slot';" 2>/dev/null || echo "0") +if [ "$SLOT_EXISTS" = "1" ]; then + echo " Replication slot: drasi_building_comfort_slot [OK]" +else + echo " Replication slot: drasi_building_comfort_slot [MISSING]" +fi + +echo +echo "Seeded rooms:" +docker exec building-comfort-postgres psql -U drasi_user -d building_comfort -c \ + "SELECT id, name, temperature, humidity, co2, floor_id FROM \"Room\" ORDER BY id;" + +echo +echo "=== Database setup complete! ===" +echo +echo "Connection details:" +echo " Host: localhost" +echo " Port: ${POSTGRES_HOST_PORT:-5732}" +echo " Database: building_comfort" +echo " User: drasi_user" +echo " Password: drasi_password" diff --git a/tutorials/building-comfort/scripts/start-demo.ps1 b/tutorials/building-comfort/scripts/start-demo.ps1 new file mode 100644 index 0000000..036821f --- /dev/null +++ b/tutorials/building-comfort/scripts/start-demo.ps1 @@ -0,0 +1,42 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Start Demo Script (Windows) +# Starts (and seeds) the database, then launches the Streamlit app in the +# foreground. The app embeds the Drasi engine, so this is the only process you +# need. Open the URL it prints (http://localhost:8501 by default) and drive the +# demo from the sidebar. Press Ctrl+C to stop, then run scripts/cleanup.ps1. + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$TutorialDir = Join-Path $ScriptDir ".." + +& (Join-Path $ScriptDir "setup-database.ps1") +if ($LASTEXITCODE -ne 0) { + Write-Host "Database setup failed; not starting the app." + exit 1 +} + +Write-Host "" +Write-Host "Starting the Streamlit app (embeds the Drasi engine)..." +Write-Host "On first run it downloads the Drasi plugins from ghcr.io - give it a moment." +Write-Host "" + +$port = if ($env:STREAMLIT_PORT) { $env:STREAMLIT_PORT } else { "8501" } +Push-Location $TutorialDir +try { + streamlit run app.py --server.port $port +} +finally { + Pop-Location +} diff --git a/tutorials/building-comfort/scripts/start-demo.sh b/tutorials/building-comfort/scripts/start-demo.sh new file mode 100755 index 0000000..c678364 --- /dev/null +++ b/tutorials/building-comfort/scripts/start-demo.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Start Demo Script +# One command to run the whole Building Comfort demo: start (and seed) the +# database, then launch the Streamlit app in the foreground. The app embeds the +# Drasi engine, so this is the only process you need. Open the URL it prints +# (http://localhost:8501 by default) and drive the demo from the sidebar. Press +# Ctrl+C to stop, then run ./scripts/cleanup.sh. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TUTORIAL_DIR="$SCRIPT_DIR/.." + +bash "$SCRIPT_DIR/setup-database.sh" + +echo +echo "Starting the Streamlit app (embeds the Drasi engine)..." +echo "On first run it downloads the Drasi plugins from ghcr.io โ€” give it a moment." +echo + +cd "$TUTORIAL_DIR" +exec streamlit run app.py --server.port "${STREAMLIT_PORT:-8501}" diff --git a/website/config.toml b/website/config.toml index 9be4846..b9f21cf 100644 --- a/website/config.toml +++ b/website/config.toml @@ -57,6 +57,21 @@ defaultContentLanguageInSubdir = false extended = true min = "0.110.0" + # Tutorials are authored once under ../tutorials/<name>/_index.md (co-located + # with their runnable code) and mounted into the docs content tree, so the doc + # site and the generated GitHub READMEs stay in sync. Adding a content mount + # disables Hugo's implicit "content -> content" default, so it is re-declared + # here; the assets/ and layouts/ defaults are untouched. + [[module.mounts]] + source = "content" + target = "content" + + [[module.mounts]] + source = "../tutorials/building-comfort" + target = "content/docs/tutorials/building-comfort" + # Only the write-up and its images are docs; the app code is not. + includeFiles = ["/_index.md", "/images/**"] + # UI customization [params] # Built-in (offline) lunr.js search โ€” no external service required. diff --git a/website/content/docs/tutorials/_index.md b/website/content/docs/tutorials/_index.md new file mode 100644 index 0000000..ce1b4d5 --- /dev/null +++ b/website/content/docs/tutorials/_index.md @@ -0,0 +1,27 @@ +--- +title: "Tutorials" +linkTitle: "Tutorials" +weight: 45 +description: > + End-to-end, hands-on walkthroughs you can run and drive yourself. +--- + +Longer, hands-on tutorials that build a complete demo with `drasi-lib` โ€” a real +data source, continuous queries, and a reaction you can watch react in real time. + +Each tutorial is authored once under +[`tutorials/`](https://github.com/drasi-project/drasi-python/tree/main/tutorials) +in the repository (runnable code plus its write-up), so you can read it here or +clone it and run it locally. + +<div class="card-grid card-grid--2"> + <a href="building-comfort/"> + <div class="unified-card unified-card--tutorials"> + <div class="unified-card-icon"><i class="fas fa-building"></i></div> + <div class="unified-card-content"> + <h3 class="unified-card-title">Building Comfort</h3> + <p class="unified-card-summary">Monitor a smart building in real time with PostgreSQL CDC, six continuous queries with synthetic joins, and a Python reaction that drives a Streamlit UI.</p> + </div> + </div> + </a> +</div> From 9f4231cd6fcccb43b3ebe8fee82f8c7eed580b84 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Thu, 30 Jul 2026 17:02:24 -0700 Subject: [PATCH 02/19] Make queries.py read as a plain, self-contained list Rewrite each of the six queries as a named `Query` (id, key, cypher, joins) with the Cypher and comfort formula written out in full, instead of splicing a shared formula string into positional tuples. Drop the derived ALL_QUERY_IDS and KEY_FIELDS module globals; the engine reads id/cypher/joins/key off each Query. Behaviour is unchanged (re-verified end-to-end). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../building-comfort/devcontainer-lock.json | 24 ++ tutorials/building-comfort/README.md | 13 +- tutorials/building-comfort/_index.md | 13 +- tutorials/building-comfort/demo/engine.py | 25 +- tutorials/building-comfort/demo/queries.py | 293 ++++++++---------- 5 files changed, 187 insertions(+), 181 deletions(-) create mode 100644 .devcontainer/building-comfort/devcontainer-lock.json diff --git a/.devcontainer/building-comfort/devcontainer-lock.json b/.devcontainer/building-comfort/devcontainer-lock.json new file mode 100644 index 0000000..895e5a2 --- /dev/null +++ b/.devcontainer/building-comfort/devcontainer-lock.json @@ -0,0 +1,24 @@ +{ + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.9", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a", + "integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.17.0", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c", + "integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.8", + "resolved": "ghcr.io/devcontainers/features/git@sha256:fd75977de13a9979000e0e78baf949adb0ca71d2398995fa22e0a36d7e7e7fe2", + "integrity": "sha256:fd75977de13a9979000e0e78baf949adb0ca71d2398995fa22e0a36d7e7e7fe2" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "1.8.0", + "resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511", + "integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511" + } + } +} diff --git a/tutorials/building-comfort/README.md b/tutorials/building-comfort/README.md index 36b8777..61006a4 100644 --- a/tutorials/building-comfort/README.md +++ b/tutorials/building-comfort/README.md @@ -255,22 +255,21 @@ This is where the Python demo differs from a built-in dashboard. Instead of conf ```python def on_results(event): - query_id = event["query_id"] - key = KEY_FIELDS[query_id] # "RoomId", "FloorId" or "BuildingId" + query = QUERIES_BY_ID[event["query_id"]] with lock: - store = results[query_id] # the UI reads this snapshot + store = snapshot[query.id] # this query's rows, keyed by id; the UI reads it for diff in event["results"]: row = diff.get("data") or diff.get("after") if diff["type"] == "DELETE": - store.pop(row[key], None) + store.pop(row[query.key], None) else: # ADD or UPDATE: upsert by row identity - store[row[key]] = row + store[row[query.key]] = row -await drasi.add_python_reaction("ui", ALL_QUERY_IDS, on_results) +await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) ``` -Every diff carries the affected row in `data` (for an `UPDATE` it equals `after`), so the snapshot is kept by row identity โ€” upsert on `ADD`/`UPDATE`, drop on `DELETE`. The `type` is one of `ADD`, `UPDATE`, or `DELETE`. This works the same for the per-room queries and the aggregate floor/building queries, which emit an `UPDATE` as their average changes. See [Python reactions](../../guides/python-reactions/) for the full callback contract. +Each query knows the RETURN column that identifies its rows (`query.key` โ€” `RoomId`, `FloorId` or `BuildingId`). Every diff carries the affected row in `data` (for an `UPDATE` it equals `after`), so the snapshot is kept by row identity โ€” upsert on `ADD`/`UPDATE`, drop on `DELETE`. The `type` is one of `ADD`, `UPDATE`, or `DELETE`. This works the same for the per-room queries and the aggregate floor/building queries, which emit an `UPDATE` as their average changes. See [Python reactions](../../guides/python-reactions/) for the full callback contract. ### Embedding the engine in Streamlit diff --git a/tutorials/building-comfort/_index.md b/tutorials/building-comfort/_index.md index 339e907..74a8f3f 100644 --- a/tutorials/building-comfort/_index.md +++ b/tutorials/building-comfort/_index.md @@ -289,22 +289,21 @@ This is where the Python demo differs from a built-in dashboard. Instead of conf ```python def on_results(event): - query_id = event["query_id"] - key = KEY_FIELDS[query_id] # "RoomId", "FloorId" or "BuildingId" + query = QUERIES_BY_ID[event["query_id"]] with lock: - store = results[query_id] # the UI reads this snapshot + store = snapshot[query.id] # this query's rows, keyed by id; the UI reads it for diff in event["results"]: row = diff.get("data") or diff.get("after") if diff["type"] == "DELETE": - store.pop(row[key], None) + store.pop(row[query.key], None) else: # ADD or UPDATE: upsert by row identity - store[row[key]] = row + store[row[query.key]] = row -await drasi.add_python_reaction("ui", ALL_QUERY_IDS, on_results) +await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) ``` -Every diff carries the affected row in `data` (for an `UPDATE` it equals `after`), so the snapshot is kept by row identity โ€” upsert on `ADD`/`UPDATE`, drop on `DELETE`. The `type` is one of `ADD`, `UPDATE`, or `DELETE`. This works the same for the per-room queries and the aggregate floor/building queries, which emit an `UPDATE` as their average changes. See [Python reactions](../../guides/python-reactions/) for the full callback contract. +Each query knows the RETURN column that identifies its rows (`query.key` โ€” `RoomId`, `FloorId` or `BuildingId`). Every diff carries the affected row in `data` (for an `UPDATE` it equals `after`), so the snapshot is kept by row identity โ€” upsert on `ADD`/`UPDATE`, drop on `DELETE`. The `type` is one of `ADD`, `UPDATE`, or `DELETE`. This works the same for the per-room queries and the aggregate floor/building queries, which emit an `UPDATE` as their average changes. See [Python reactions](../../guides/python-reactions/) for the full callback contract. ### Embedding the engine in Streamlit diff --git a/tutorials/building-comfort/demo/engine.py b/tutorials/building-comfort/demo/engine.py index 5832387..26431a2 100644 --- a/tutorials/building-comfort/demo/engine.py +++ b/tutorials/building-comfort/demo/engine.py @@ -59,7 +59,7 @@ SIMULATION_INTERVAL_S, SOURCE_CONFIG, ) -from .queries import ALL_QUERY_IDS, KEY_FIELDS, QUERIES # noqa: E402 +from .queries import QUERIES # noqa: E402 # Ranges chosen to straddle the comfortable band, so simulation makes alerts # come and go rather than sitting at one extreme. @@ -83,6 +83,8 @@ def __init__(self) -> None: # Per query: a dict keyed by that query's primary-key field. The reaction # applies diffs into it; the UI reads a snapshot of the values. self._results: dict[str, dict[Any, dict[str, Any]]] = {} + # Which RETURN column identifies each query's rows, looked up by query id. + self._key_by_id = {query.id: query.key for query in QUERIES} self._version = 0 self._simulation = False @@ -121,21 +123,22 @@ async def _start(self) -> None: await self._drasi.add_source("postgres", "db", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) - for query_id, cypher, joins in QUERIES: - await self._drasi.add_query(query_id, cypher, ["db"], joins=joins or None) - for query_id, _, _ in QUERIES: - await self._drasi.wait_for_query(query_id) + for query in QUERIES: + await self._drasi.add_query(query.id, query.cypher, ["db"], joins=query.joins or None) + for query in QUERIES: + await self._drasi.wait_for_query(query.id) # Prime the snapshot from the bootstrapped result sets, then let the # reaction keep it current. - for query_id, _, _ in QUERIES: - rows = await self._drasi.get_query_results(query_id) - key_field = KEY_FIELDS[query_id] + for query in QUERIES: + rows = await self._drasi.get_query_results(query.id) with self._lock: - self._results[query_id] = {row[key_field]: row for row in rows} + self._results[query.id] = {row[query.key]: row for row in rows} self._room_ids = await self._load_room_ids() - await self._drasi.add_python_reaction("ui", ALL_QUERY_IDS, self._on_results) + await self._drasi.add_python_reaction( + "ui", [query.id for query in QUERIES], self._on_results + ) def _on_results(self, event: QueryResultEvent) -> None: """Apply a batch of diffs to the snapshot. @@ -146,7 +149,7 @@ def _on_results(self, event: QueryResultEvent) -> None: DELETE. Result sets are tiny, so this stays cheap. """ query_id = event["query_id"] - key_field = KEY_FIELDS[query_id] + key_field = self._key_by_id[query_id] with self._lock: store = self._results.setdefault(query_id, {}) for diff in event["results"]: diff --git a/tutorials/building-comfort/demo/queries.py b/tutorials/building-comfort/demo/queries.py index 4b21c31..b09077d 100644 --- a/tutorials/building-comfort/demo/queries.py +++ b/tutorials/building-comfort/demo/queries.py @@ -12,37 +12,39 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""The six continuous queries, ported from the Drasi Server tutorial. +"""The six continuous queries that power the demo. -Each query computes a comfort level with the same formula. A value between 40 -and 50 is comfortable; the seed values (70F, 40%, 10 ppm) give -``floor(50 + (70-72) + (40-42) + 0) = 46``. +Each query is written out in full below so you can read (or copy) exactly what +Drasi runs. They all compute the same comfort level: + + floor(50 + (temperature - 72) + (humidity - 42) + + CASE WHEN co2 > 500 THEN (co2 - 500) / 25 ELSE 0 END) + +A value between 40 and 50 is comfortable; the seed values (70F, 40%, 10 ppm) +give floor(50 + (70-72) + (40-42) + 0) = 46. The Room -> Floor -> Building hierarchy is walked through two *synthetic joins*. Drasi does not read Postgres foreign keys, so each query declares the -relationships it needs. In ``drasi-lib`` a join is a ``Join`` mapping, passed to -``add_query(..., joins=[...])`` -- the same shape the Drasi Server YAML uses. +relationships it needs. A join is a plain mapping passed to ``add_query``. """ from __future__ import annotations +from dataclasses import dataclass, field from typing import Any -# The comfort formula, shared by every query. ``r`` is the Room node. -_COMFORT = ( - "floor( 50 + (r.temperature - 72) + (r.humidity - 42)" - " + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END )" -) +# --- Synthetic joins --------------------------------------------------------- +# Room.floor_id -> Floor.id, and Floor.building_id -> Building.id. -# Synthetic joins: Room.floor_id -> Floor.id, Floor.building_id -> Building.id. -PART_OF_FLOOR: dict[str, Any] = { +PART_OF_FLOOR = { "id": "PART_OF_FLOOR", "keys": [ {"label": "Room", "property": "floor_id"}, {"label": "Floor", "property": "id"}, ], } -PART_OF_BUILDING: dict[str, Any] = { + +PART_OF_BUILDING = { "id": "PART_OF_BUILDING", "keys": [ {"label": "Floor", "property": "building_id"}, @@ -50,7 +52,9 @@ ], } -# Query ids, used both to register the queries and to index the reaction's state. +# --- Query ids --------------------------------------------------------------- +# The UI reads each query's result set from the snapshot by these ids. + BUILDING_COMFORT_UI = "building-comfort-ui" BUILDING_COMFORT_LEVEL = "building-comfort-level-calc" FLOOR_COMFORT_LEVEL = "floor-comfort-level-calc" @@ -59,145 +63,122 @@ BUILDING_ALERT = "building-alert" -# Each entry: (id, cypher, joins). ``joins`` is empty for queries that match a -# single label. The engine registers them in order. -QUERIES: list[tuple[str, str, list[dict[str, Any]]]] = [ - # Query 1: per-room comfort level (drives the building view). - ( - BUILDING_COMFORT_UI, - f""" - MATCH - (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) - WITH - r, f, b, - {_COMFORT} AS ComfortLevel - RETURN - r.id AS RoomId, - r.name AS RoomName, - f.id AS FloorId, - f.name AS FloorName, - b.id AS BuildingId, - b.name AS BuildingName, - r.temperature AS Temperature, - r.humidity AS Humidity, - r.co2 AS CO2, - ComfortLevel - """, - [PART_OF_FLOOR, PART_OF_BUILDING], - ), - # Query 2: overall building comfort (avg of floor averages). - ( - BUILDING_COMFORT_LEVEL, - f""" - MATCH - (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) - WITH - b, - {_COMFORT} AS RoomComfortLevel - WITH - b, - avg(RoomComfortLevel) AS FloorComfortLevel - WITH - b, - avg(FloorComfortLevel) AS ComfortLevel - RETURN - b.id AS BuildingId, - ComfortLevel - """, - [PART_OF_FLOOR, PART_OF_BUILDING], - ), - # Query 3: per-floor comfort (avg of the floor's rooms). - ( - FLOOR_COMFORT_LEVEL, - f""" - MATCH - (r:Room)-[:PART_OF_FLOOR]->(f:Floor) - WITH - f, - {_COMFORT} AS RoomComfortLevel - WITH - f, - avg(RoomComfortLevel) AS ComfortLevel - RETURN - f.id AS FloorId, - ComfortLevel - """, - [PART_OF_FLOOR], - ), - # Query 4: rooms outside the comfortable band (40-50). - ( - ROOM_ALERT, - f""" - MATCH - (r:Room) - WITH - r.id AS RoomId, - r.name AS RoomName, - {_COMFORT} AS ComfortLevel - WHERE ComfortLevel < 40 OR ComfortLevel > 50 - RETURN - RoomId, RoomName, ComfortLevel - """, - [], - ), - # Query 5: floors whose average comfort is outside 40-50. - ( - FLOOR_ALERT, - f""" - MATCH - (r:Room)-[:PART_OF_FLOOR]->(f:Floor) - WITH - f, - {_COMFORT} AS RoomComfortLevel - WITH - f, - avg(RoomComfortLevel) AS ComfortLevel - WHERE - ComfortLevel < 40 OR ComfortLevel > 50 - RETURN - f.id AS FloorId, - f.name AS FloorName, - ComfortLevel - """, - [PART_OF_FLOOR], - ), - # Query 6: the building when its overall comfort is outside 40-50. - ( - BUILDING_ALERT, - f""" - MATCH - (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) - WITH - f, b, - {_COMFORT} AS RoomComfortLevel - WITH - f, b, - avg(RoomComfortLevel) AS FloorComfortLevel - WITH - b, - avg(FloorComfortLevel) AS ComfortLevel - WHERE - ComfortLevel < 40 OR ComfortLevel > 50 - RETURN - b.id AS BuildingId, - b.name AS BuildingName, - ComfortLevel - """, - [PART_OF_FLOOR, PART_OF_BUILDING], - ), -] +@dataclass(frozen=True) +class Query: + """One continuous query: how to register it and how to index its rows.""" -# The query ids the UI reaction subscribes to (all of them). -ALL_QUERY_IDS = [q[0] for q in QUERIES] - -# The primary-key field of each query's rows. The reaction keeps a snapshot of -# every query keyed by this field, so it can apply ADD/UPDATE/DELETE diffs by row -# identity rather than rescanning. -KEY_FIELDS: dict[str, str] = { - BUILDING_COMFORT_UI: "RoomId", - BUILDING_COMFORT_LEVEL: "BuildingId", - FLOOR_COMFORT_LEVEL: "FloorId", - ROOM_ALERT: "RoomId", - FLOOR_ALERT: "FloorId", - BUILDING_ALERT: "BuildingId", -} + id: str + key: str # the RETURN column that identifies each row (its primary key) + cypher: str + joins: list[dict[str, Any]] = field(default_factory=list) + + +# One row per room, with its comfort level. Drives the building view. +BUILDING_COMFORT_UI_QUERY = Query( + id=BUILDING_COMFORT_UI, + key="RoomId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + RETURN + r.id AS RoomId, + r.name AS RoomName, + f.id AS FloorId, + f.name AS FloorName, + b.id AS BuildingId, + b.name AS BuildingName, + r.temperature AS Temperature, + r.humidity AS Humidity, + r.co2 AS CO2, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + """, +) + +# The building's overall comfort: the average of each floor's average. +BUILDING_COMFORT_LEVEL_QUERY = Query( + id=BUILDING_COMFORT_LEVEL, + key="BuildingId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH b, f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH b, avg(RoomComfortLevel) AS FloorComfortLevel + WITH b, avg(FloorComfortLevel) AS ComfortLevel + RETURN b.id AS BuildingId, ComfortLevel + """, +) + +# Each floor's comfort: the average of the rooms on it. +FLOOR_COMFORT_LEVEL_QUERY = Query( + id=FLOOR_COMFORT_LEVEL, + key="FloorId", + joins=[PART_OF_FLOOR], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH f, avg(RoomComfortLevel) AS ComfortLevel + RETURN f.id AS FloorId, ComfortLevel + """, +) + +# Only the rooms whose comfort is outside the 40-50 band. +ROOM_ALERT_QUERY = Query( + id=ROOM_ALERT, + key="RoomId", + cypher=""" + MATCH (r:Room) + WITH r.id AS RoomId, r.name AS RoomName, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN RoomId, RoomName, ComfortLevel + """, +) + +# Only the floors whose average comfort is outside the 40-50 band. +FLOOR_ALERT_QUERY = Query( + id=FLOOR_ALERT, + key="FloorId", + joins=[PART_OF_FLOOR], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH f, avg(RoomComfortLevel) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN f.id AS FloorId, f.name AS FloorName, ComfortLevel + """, +) + +# The building, only while its overall comfort is outside the 40-50 band. +BUILDING_ALERT_QUERY = Query( + id=BUILDING_ALERT, + key="BuildingId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH b, f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH b, f, avg(RoomComfortLevel) AS FloorComfortLevel + WITH b, avg(FloorComfortLevel) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN b.id AS BuildingId, b.name AS BuildingName, ComfortLevel + """, +) + +# The engine registers these in order and subscribes one reaction to them all. +QUERIES = [ + BUILDING_COMFORT_UI_QUERY, + BUILDING_COMFORT_LEVEL_QUERY, + FLOOR_COMFORT_LEVEL_QUERY, + ROOM_ALERT_QUERY, + FLOOR_ALERT_QUERY, + BUILDING_ALERT_QUERY, +] From 8d019873a13006fcd6a715583d290b2f481c8274 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Thu, 30 Jul 2026 18:06:57 -0700 Subject: [PATCH 03/19] Add Curbside Pickup tutorial (Python, cross-DB, single Streamlit UI) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the upstream Curbside Pickup tutorial to drasi-lib. It joins a PostgreSQL orders store and a MySQL vehicles store by license plate, with six continuous queries โ€” two that join across both databases, and a temporal `delay` query using drasi.trueFor. Upstream used the dashboard reaction plus a separate Node operations console; this collapses both into one integrated Streamlit UI powered by a single Python reaction: the panels render live and their inline buttons (plus a sidebar reset and a color-coded SQL activity log) drive the changes. - tutorials/curbside-pickup/: app.py + demo/ (engine, queries, config), two-DB database assets, env/setup/start/cleanup scripts, requirements (psycopg + PyMySQL). - Authored in _index.md (mounted into the website), README generated. - Website mount + tutorials index card, tutorials/README row, dev container. Verified end-to-end against real PostgreSQL and MySQL: seed, cross-DB delivery match, the drasi.trueFor delay firing at ~10s, and reset; screenshot captured. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../curbside-pickup/devcontainer.json | 68 ++++ .devcontainer/curbside-pickup/post-create.sh | 24 ++ tutorials/README.md | 1 + tutorials/curbside-pickup/.env.example | 30 ++ tutorials/curbside-pickup/.gitignore | 11 + tutorials/curbside-pickup/README.md | 288 ++++++++++++++++ tutorials/curbside-pickup/_index.md | 321 ++++++++++++++++++ tutorials/curbside-pickup/app.py | 190 +++++++++++ .../database/docker-compose.yml | 93 +++++ .../curbside-pickup/database/mysql-init.sql | 56 +++ .../database/postgres-init.sql | 97 ++++++ tutorials/curbside-pickup/demo/__init__.py | 19 ++ tutorials/curbside-pickup/demo/config.py | 109 ++++++ tutorials/curbside-pickup/demo/engine.py | 222 ++++++++++++ tutorials/curbside-pickup/demo/queries.py | 196 +++++++++++ .../curbside-pickup/images/curbside-ui.png | Bin 0 -> 128963 bytes tutorials/curbside-pickup/requirements.txt | 12 + tutorials/curbside-pickup/scripts/cleanup.ps1 | 48 +++ tutorials/curbside-pickup/scripts/cleanup.sh | 54 +++ .../scripts/setup-database.ps1 | 82 +++++ .../curbside-pickup/scripts/setup-database.sh | 103 ++++++ .../curbside-pickup/scripts/start-demo.ps1 | 41 +++ .../curbside-pickup/scripts/start-demo.sh | 35 ++ website/config.toml | 5 + website/content/docs/tutorials/_index.md | 9 + 25 files changed, 2114 insertions(+) create mode 100644 .devcontainer/curbside-pickup/devcontainer.json create mode 100755 .devcontainer/curbside-pickup/post-create.sh create mode 100644 tutorials/curbside-pickup/.env.example create mode 100644 tutorials/curbside-pickup/.gitignore create mode 100644 tutorials/curbside-pickup/README.md create mode 100644 tutorials/curbside-pickup/_index.md create mode 100644 tutorials/curbside-pickup/app.py create mode 100644 tutorials/curbside-pickup/database/docker-compose.yml create mode 100644 tutorials/curbside-pickup/database/mysql-init.sql create mode 100644 tutorials/curbside-pickup/database/postgres-init.sql create mode 100644 tutorials/curbside-pickup/demo/__init__.py create mode 100644 tutorials/curbside-pickup/demo/config.py create mode 100644 tutorials/curbside-pickup/demo/engine.py create mode 100644 tutorials/curbside-pickup/demo/queries.py create mode 100644 tutorials/curbside-pickup/images/curbside-ui.png create mode 100644 tutorials/curbside-pickup/requirements.txt create mode 100644 tutorials/curbside-pickup/scripts/cleanup.ps1 create mode 100755 tutorials/curbside-pickup/scripts/cleanup.sh create mode 100644 tutorials/curbside-pickup/scripts/setup-database.ps1 create mode 100755 tutorials/curbside-pickup/scripts/setup-database.sh create mode 100644 tutorials/curbside-pickup/scripts/start-demo.ps1 create mode 100755 tutorials/curbside-pickup/scripts/start-demo.sh diff --git a/.devcontainer/curbside-pickup/devcontainer.json b/.devcontainer/curbside-pickup/devcontainer.json new file mode 100644 index 0000000..cd95247 --- /dev/null +++ b/.devcontainer/curbside-pickup/devcontainer.json @@ -0,0 +1,68 @@ +{ + "name": "Curbside Pickup Tutorial (Python)", + "image": "mcr.microsoft.com/devcontainers/base:bookworm@sha256:9e35ebf156350ba25b117da6869907034b6e2437b1891e3a43395af0b7d3eda4", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "29", + "moby": false + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12", + "installTools": true + }, + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "configureZshAsDefaultShell": true + } + }, + "postCreateCommand": "bash ../../.devcontainer/curbside-pickup/post-create.sh", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/tutorials/curbside-pickup", + "runArgs": [ + "--privileged", + "--init", + "--ipc=host" + ], + "forwardPorts": [ + 8501, + 5742, + 3309 + ], + "portsAttributes": { + "8501": { + "label": "Streamlit UI", + "onAutoForward": "notify" + }, + "5742": { + "label": "PostgreSQL (orders)", + "onAutoForward": "silent" + }, + "3309": { + "label": "MySQL (vehicles)", + "onAutoForward": "silent" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "editor.formatOnSave": true + } + } + }, + "remoteUser": "vscode", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "containerEnv": { + "POSTGRES_HOST": "localhost", + "POSTGRES_PORT": "5742", + "POSTGRES_HOST_PORT": "5742", + "MYSQL_HOST": "localhost", + "MYSQL_PORT": "3309", + "MYSQL_HOST_PORT": "3309", + "STREAMLIT_PORT": "8501" + } +} diff --git a/.devcontainer/curbside-pickup/post-create.sh b/.devcontainer/curbside-pickup/post-create.sh new file mode 100755 index 0000000..3dac6b6 --- /dev/null +++ b/.devcontainer/curbside-pickup/post-create.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Post-create script for the Curbside Pickup tutorial (Python). + +set -e + +echo "๐Ÿ”ง Initializing the Curbside Pickup (Python) tutorial environment..." + +# Resolve the tutorial directory from this script's location so the script works +# regardless of the current working directory. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +TUTORIAL_DIR="$REPO_ROOT/tutorials/curbside-pickup" + +# Install the tutorial's Python dependencies (drasi-lib, streamlit, psycopg, +# PyMySQL). The devcontainer's Python feature provides a pip that is not +# externally managed, so a plain install works and puts `streamlit` on PATH. +echo "๐Ÿ Installing Python dependencies..." +cd "$TUTORIAL_DIR" +python -m pip install --upgrade pip +python -m pip install -r requirements.txt + +echo "" +echo "โœ… Curbside Pickup (Python) tutorial environment is ready!" +echo " Next: run 'bash scripts/start-demo.sh' (you are already in tutorials/curbside-pickup)" diff --git a/tutorials/README.md b/tutorials/README.md index 826e0fe..47d38b9 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -7,6 +7,7 @@ self-contained project you can clone and run. | Tutorial | What you learn | How to run it | | --- | --- | --- | | [building-comfort](building-comfort) | Smart-building comfort monitoring โ€” PostgreSQL CDC, six continuous queries with synthetic joins and aggregation, and a **Python reaction that drives a Streamlit UI** (with simulation, reset and per-room controls) | Open the **Building Comfort Tutorial (Python)** dev container (or a Codespace) and follow [tutorials/building-comfort](building-comfort) | +| [curbside-pickup](curbside-pickup) | Cross-database joins โ€” a PostgreSQL `orders` store and a MySQL `vehicles` store joined by license plate, six continuous queries (incl. a temporal `drasi.trueFor` delay), rendered and driven from **one integrated Streamlit UI** powered by a Python reaction | Open the **Curbside Pickup Tutorial (Python)** dev container (or a Codespace) and follow [tutorials/curbside-pickup](curbside-pickup) | ## How these tutorials are authored diff --git a/tutorials/curbside-pickup/.env.example b/tutorials/curbside-pickup/.env.example new file mode 100644 index 0000000..13ac113 --- /dev/null +++ b/tutorials/curbside-pickup/.env.example @@ -0,0 +1,30 @@ +# Example environment variables for the curbside-pickup tutorial. +# +# Copy this file to .env and adjust as needed: +# cp .env.example .env +# +# The scripts and the app read these with the same defaults shown here. +# +# IMPORTANT: Never commit .env with real credentials! + +# Retail Operations - PostgreSQL (orders) +POSTGRES_HOST=localhost +POSTGRES_PORT=5742 +POSTGRES_HOST_PORT=5742 +POSTGRES_DATABASE=RetailOperations +POSTGRES_USER=drasi_user +POSTGRES_PASSWORD=drasi_password + +# Physical Operations - MySQL (vehicles) +MYSQL_HOST=localhost +MYSQL_PORT=3309 +MYSQL_HOST_PORT=3309 +MYSQL_DATABASE=PhysicalOperations +MYSQL_USER=drasi_user +MYSQL_PASSWORD=drasi_password +# The root password is used by the setup scripts to seed the database and grant +# the replication privileges the Drasi source needs. +MYSQL_ROOT_PASSWORD=root_admin + +# Streamlit UI port. +STREAMLIT_PORT=8501 diff --git a/tutorials/curbside-pickup/.gitignore b/tutorials/curbside-pickup/.gitignore new file mode 100644 index 0000000..1e58996 --- /dev/null +++ b/tutorials/curbside-pickup/.gitignore @@ -0,0 +1,11 @@ +# Local environment overrides +.env + +# Python +__pycache__/ +*.pyc +.venv/ + +# Drasi engine state / logs +data/ +*.log diff --git a/tutorials/curbside-pickup/README.md b/tutorials/curbside-pickup/README.md new file mode 100644 index 0000000..5ee28fc --- /dev/null +++ b/tutorials/curbside-pickup/README.md @@ -0,0 +1,288 @@ +<!-- DO NOT EDIT. Generated from _index.md by scripts/render-tutorials.py. Edit _index.md and run `python3 scripts/render-tutorials.py`. --> + +Imagine a store running curbside pickup. The **retail team** manages customer orders in one database and marks an order *ready* when it's prepared. Independently, the **physical operations team** tracks pickup vehicles in *another* database, and a driver sets their location to *Curbside* when they arrive. The two systems never talk to each other. + +You want a single live view with six panels: + +- an **Orders ยท Preparing** panel and an **Orders ยท Ready** panel, so an order visibly moves from one to the other the moment it's marked *ready*, +- a **Vehicles ยท Parking** panel and a **Vehicles ยท Curbside** panel, so a car moves between them as the driver pulls up, plus the two situations that matter: +- a **Matched Orders** panel that lights up the instant an order is *ready* **and** its driver is at the curbside, so staff know exactly which order to carry out, and +- a **Delayed Orders** panel that flags drivers who have been waiting at the curbside too long while their order still isn't ready. + +The catch: the orders live in **PostgreSQL** and the vehicles live in **MySQL**, and what you care about โ€” a *ready* order whose driver has *arrived* โ€” only exists when you combine a fact from each. Building this the traditional way means **polling both databases**, and you always end up reactive on one side and blind, between polls, on the other. This tutorial builds it with **`drasi-lib`** instead: two sources, six continuous queries (two of which join across both databases), and a **Python reaction** that renders one [Streamlit](https://streamlit.io/) UI. Drasi watches the change feed of *both* databases at once, so a change on **either** side re-evaluates the join immediately โ€” no polling, no blind side. And rather than a separate dashboard plus a separate console, the **one** Streamlit page both shows the six panels *and* has the buttons that drive the changes. + +**Sources** โ†’ **Continuous Queries** โ†’ **Reactions** + +- **Sources** โ€” Connect to your data sources +- **Continuous Queries** โ€” Define what changes matter +- **Reactions** โ€” Take action automatically + +| Step | What You'll Do | +| ---- | ------------- | +| **[Step 1: Set Up Your Environment](#step-1-of-4-set-up-your-environment)** | Open the dev container (or install Python + Docker locally) | +| **[Step 2: Run the Demo](#step-2-of-4-run-the-demo)** | One command starts both databases and the Streamlit app | +| **[Step 3: Open the UI](#step-3-of-4-open-the-ui)** | Watch all six panels update live | +| **[Step 4: Drive Change](#step-4-of-4-drive-change)** | Use the buttons in the UI to change orders and vehicles, and watch Drasi react | +| **[How It Works](#how-it-works)** | Understand the two sources, the cross-database join, and the six queries | + +> **Before you begin** +> +> - **One process:** the Streamlit app embeds the Drasi engine, so a single command runs everything. It stays in the foreground and you drive changes from your **browser**. +> - **Working directory:** run every command from the tutorial directory (`tutorials/curbside-pickup/`). The dev container opens there automatically; if you're running locally, `cd tutorials/curbside-pickup` first. +> - **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*). Use the one for your shell. The dev container and Codespaces use *bash*. +> - **Ports:** the Streamlit UI is on `8501`, PostgreSQL is published on `5742`, and MySQL on `3309`. + +## Step 1 of 4: Set Up Your Environment +This tutorial needs **Docker** (it runs PostgreSQL and MySQL) and **Python 3.10+**. The easiest way to get everything is the **dev container**. + +### Option A: Dev Container or GitHub Codespaces (recommended) + +1. Open the [`drasi-python`](https://github.com/drasi-project/drasi-python) repository in VS Code and run **Reopen in Container** (or create a **Codespace** from the repo's **Code** menu). +2. When prompted for a configuration, choose **Curbside Pickup Tutorial (Python)**. +3. Wait for the container to finish. Its setup script installs the tutorial's Python dependencies and the Docker tooling for the two databases. + +That's it. Skip ahead to [Step 2](#step-2-of-4-run-the-demo). + +### Option B: Run Locally + +You'll need **Python 3.10+**, **Docker**, and **bash** for the helper scripts (on Windows use Git Bash or WSL, or run the PowerShell scripts). From the repository root, move into the tutorial directory and install the dependencies: + +**bash / zsh** + +```bash +cd tutorials/curbside-pickup +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +``` + +**PowerShell** + +```powershell +cd tutorials/curbside-pickup +python -m venv .venv; .venv\Scripts\Activate.ps1 +pip install -r requirements.txt +``` + +`drasi-lib` ships as a prebuilt wheel, so no Rust toolchain is needed. It downloads the Postgres and MySQL plugins it needs from `ghcr.io/drasi-project` the first time the app runs. + +## Step 2 of 4: Run the Demo +Start the demo: + +**bash / zsh** + +```bash +bash scripts/start-demo.sh +``` + +**PowerShell** + +```powershell +powershell -ExecutionPolicy Bypass -File scripts/start-demo.ps1 +``` + +The `start-demo` script does two things: + +1. **Starts both databases.** PostgreSQL comes up with logical replication enabled and the `orders` table seeded (three orders, all *preparing*). MySQL comes up with **ROW-based binary logging** and GTID mode enabled and the `vehicles` table seeded (three vehicles, all *Parking*) โ€” the plates match the orders. +2. **Launches the Streamlit app**, which embeds the Drasi engine. + +On first start, the app's embedded engine downloads the plugins it needs (`source/postgres`, `bootstrap/postgres`, `source/mysql`, `bootstrap/mysql`) from `ghcr.io/drasi-project`, connects to both databases, bootstraps the existing rows, starts the six continuous queries, and registers the reaction. Streamlit prints a URL like the following when it's ready: + +```text +Local URL: http://localhost:8501 +``` + +Leave this running. Everything else happens in your **browser**. + +> **Stopping and resetting** +> +> Press **Ctrl+C** in the terminal to stop the app. To remove the database containers when you're completely done, run `bash scripts/cleanup.sh` (bash) or `powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1` (PowerShell). Add `--volumes` (bash) or `-RemoveVolumes` (PowerShell) to also delete the data. + +## Step 3 of 4: Open the UI +The app renders everything from the Python reaction โ€” there's no separate dashboard server and no separate console. **Wait until the app has finished starting** (the first run takes a little longer while the plugins download), then open it in your browser: + +```text +http://localhost:8501 +``` + +In the dev container or Codespaces, port `8501` is forwarded automatically. You'll see the **Curbside Pickup** UI with six panels: + +- ๐Ÿ• **Orders ยท Preparing** and ๐Ÿ• **Orders ยท Ready** โ€” every order in PostgreSQL, split by status. Each row has a button to move it. +- ๐Ÿš— **Vehicles ยท Parking** and ๐Ÿš— **Vehicles ยท Curbside** โ€” every vehicle in MySQL, split by location. Each row has a button to move it. +- ๐Ÿ“ฆ **Matched Orders** โ€” orders that are *ready* whose driver is at the *Curbside* (the `delivery` query). +- โš ๏ธ **Delayed Orders** โ€” drivers who have waited at the curbside for more than 10 seconds while their order is still being prepared (the `delay` query). + +At bootstrap all three orders are *preparing* and all three vehicles are in *Parking*, so those two panels list every row while their **Ready** and **Curbside** counterparts start empty. The **Matched** and **Delayed** panels also start **empty** and fill in as you drive changes. The **sidebar** has a **Reset** button and a live, color-coded **activity log** of the SQL each button runs. + +## Step 4 of 4: Drive Change +> **No middle tier โ€” the buttons just write to a database** +> +> Each button on an order or vehicle runs a single SQL `UPDATE` โ€” orders in PostgreSQL (via `psycopg`), vehicles in MySQL (via `PyMySQL`) โ€” exactly what a real retail or physical-operations app would run. There's no API to call and no event to publish. Drasi observes the PostgreSQL change through logical replication and the MySQL change through the binary log, re-evaluates the affected queries (including the cross-database join), and calls the Python reaction, which updates the panels. The sidebar's **activity log** shows each statement and which database it hit. + +### Trigger a delivery + +In the ๐Ÿ• **Orders ยท Preparing** panel, press **Mark ready โ†’** on order **1** (Sophia Carter). It immediately leaves *Preparing* and appears in ๐Ÿ• **Orders ยท Ready**. Now, in the ๐Ÿš— **Vehicles ยท Parking** panel, press **To Curbside โ†’** on vehicle **A1234**. + +The vehicle jumps to ๐Ÿš— **Vehicles ยท Curbside**, and within about a second the ๐Ÿ“ฆ **Matched Orders** panel reacts: order **1** appears with its driver and vehicle. Nothing polled anything โ€” Drasi saw the PostgreSQL change and the MySQL change and re-evaluated the cross-database join. Send the vehicle back with **โ†ฉ Parking** and the row disappears from **Matched Orders**. + +### Trigger a delay + +Now reproduce the *other* scenario. Pick a vehicle whose order is **not** ready โ€” say **B5678** (Mason Rivera) โ€” and press **To Curbside โ†’**, but **leave order 2 as *preparing***. Nothing happens immediately. After **10 seconds** the โš ๏ธ **Delayed Orders** panel lights up: order **2** appears, flagging that the driver has been waiting too long. + +This is the interesting one. Drasi doesn't poll to find slow orders โ€” the **continuous query schedules its own future re-evaluation** for the moment the 10-second threshold is crossed, and fires exactly then. If you mark the order *ready* (or send the driver back to *Parking*) before the 10 seconds elapse, the alert never appears. + +### Reset + +Press **Reset everything** in the sidebar to return to the starting state (all orders *preparing*, all vehicles *Parking*). + +## How It Works +The whole demo is a single Streamlit program in `tutorials/curbside-pickup/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state and hosts the controls. + +### Two Sources + +The queries join data from two different databases, so the engine adds two sources. Installing the plugins and adding the sources is a few lines of Python: + +```python +for plugin in ("source/postgres", "bootstrap/postgres", "source/mysql", "bootstrap/mysql"): + await drasi.install_plugin(plugin) +await drasi.start() + +# PostgreSQL holds the orders and streams changes via logical replication (CDC). +await drasi.add_source( + "postgres", + "retail-ops", + { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.orders"], + "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], + "slotName": "drasi_curbside_slot", + "publicationName": "drasi_curbside_pub", + }, + bootstrap={"kind": "postgres", **POSTGRES}, +) + +# MySQL holds the vehicles and streams changes via its binary log (binlog). +await drasi.add_source( + "mysql", + "physical-ops", + { + **MYSQL, + "sslMode": "disabled", + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], + }, + bootstrap={ + "kind": "mysql", + **MYSQL, + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], + }, +) +``` + +The MySQL container is started with ROW-based logging, full row images/metadata and GTID mode (see `database/docker-compose.yml`), and `database/mysql-init.sql` grants the Drasi user the replication privileges it needs to read the binlog. Each table row becomes a graph node: `orders` rows become `(o:orders)` and `vehicles` rows become `(v:vehicles)`. + +### The Synthetic Join + +There is no foreign key between the two databases โ€” they're completely separate systems. Drasi creates the relationship in the query with a **synthetic join**, matching a vehicle to an order whenever their `plate` values are equal: + +```python +PICKUP_BY = { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], +} +``` + +The `delivery` and `delay` queries read from **both** sources and pass `joins=[PICKUP_BY]` to `add_query`, then walk `(o:orders)-[:PICKUP_BY]->(v:vehicles)` as if it were a real graph edge โ€” across two different databases. + +### The Six Continuous Queries + +Four are simple single-source lists, filtered by state, that feed the split **Orders** and **Vehicles** panels โ€” because each matches only part of a table, a row leaves one query's result and joins the other's the instant its status or location changes: + +```cypher +MATCH (o:orders) +WHERE o.status <> 'ready' // orders-ready uses: o.status = 'ready' +RETURN o.id AS id, o.customer_name AS customerName, o.plate AS plate +``` + +The other two join across both databases. **`delivery`** returns an order whenever it is *ready* and its driver's vehicle is at the *Curbside*: + +```cypher +MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) +WHERE o.status = 'ready' AND v.location = 'Curbside' +RETURN o.id AS id, o.customer_name AS customerName, o.plate AS vehicleId, v.make AS vehicleMake +``` + +**`delay`** returns an order whose driver has been at the *Curbside* for more than 10 seconds while the order still isn't *ready*. It uses **`drasi.trueFor`**, which schedules a future re-evaluation and fires the moment the condition has held for the given duration, so the order appears exactly when the 10-second threshold is crossed: + +```cypher +MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) +WHERE o.status <> 'ready' + AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) +RETURN o.id AS orderId, o.customer_name AS customerName, + drasi.changeDateTime(v) AS waitingSinceTimestamp +``` + +> **How the timing works** +> +> Both the PostgreSQL and MySQL sources stamp every change with the wall-clock time it happened, which `drasi.changeDateTime()` exposes. `drasi.trueFor` uses that timestamp to anchor its 10-second timer โ€” so the delay query fires exactly when a curbside vehicle has waited long enough โ€” with no extra timestamp columns or application bookkeeping. The `delivery` query uses `drasi.listMax([drasi.changeDateTime(o), drasi.changeDateTime(v)])` to report whichever of the two changes happened later. + +### The Python Reaction + +A single **Python reaction** subscribes to all six queries. Each time a query's result set changes โ€” including the delayed re-evaluation the `delay` query schedules โ€” Drasi calls it with the query id and the diffs, and the callback updates a thread-safe snapshot the UI reads: + +```python +def on_results(event): + query = QUERIES_BY_ID[event["query_id"]] + with lock: + store = snapshot[query.id] # this query's rows, keyed by id + for diff in event["results"]: + row = diff.get("data") or diff.get("after") + if diff["type"] == "DELETE": + store.pop(row[query.key], None) + else: # ADD or UPDATE: upsert by row identity + store[row[query.key]] = row + + +await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) +``` + +### One integrated Streamlit UI + +The engine runs on its own asyncio event loop in a background daemon thread, created once with `@st.cache_resource`, and `app.py` reads `engine.snapshot()` each run and renders the six panels with a ~1-second auto-refresh. Because it's the same page, the panels can *also* drive the demo: each order and vehicle row has a button that runs the matching `UPDATE`, the sidebar has a **Reset** button, and every write is appended to the activity log the sidebar shows. A `delay` row appearing 10 seconds after you move a vehicle to the curbside is the reaction receiving `drasi.trueFor`'s scheduled re-evaluation โ€” live, with no polling. + +<img src="images/curbside-ui.png" width="960" alt="The Curbside Pickup Streamlit UI: four panels of orders and vehicles with inline buttons across the top, Matched and Delayed panels below, and a sidebar with a reset button and a color-coded SQL activity log"> + +## Clean Up +When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database containers: + +**bash / zsh** + +```bash +# Stop containers, keep data +bash scripts/cleanup.sh + +# Stop containers and delete the data volumes +bash scripts/cleanup.sh --volumes +``` + +**PowerShell** + +```powershell +# Stop containers, keep data +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 + +# Stop containers and delete the data volumes +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 -RemoveVolumes +``` + +## What You Learned +- **Sources** connect Drasi to live data โ€” here, **two** databases at once, PostgreSQL via logical replication and MySQL via the binary log. +- **Continuous Queries** with a **synthetic join** relate rows across those two databases (order โ†” vehicle by plate), and a **temporal** query (`drasi.trueFor`) fires exactly when a condition has held long enough โ€” no polling, no bookkeeping. +- A **Python reaction** turned all six queries into one live UI, and because it's a Streamlit page, the same UI hosts the controls that drive the changes โ€” no dashboard reaction and no separate console. +- Because Drasi emits only what *changed*, every panel updates the instant the data does, whichever database changed. + +From here, try changing the delay threshold, adding an order/vehicle, or pointing the sources at your own databases. diff --git a/tutorials/curbside-pickup/_index.md b/tutorials/curbside-pickup/_index.md new file mode 100644 index 0000000..ea6751d --- /dev/null +++ b/tutorials/curbside-pickup/_index.md @@ -0,0 +1,321 @@ +--- +type: "docs" +title: "Curbside Pickup" +linkTitle: "Curbside Pickup" +weight: 40 +description: > + Join a PostgreSQL orders store and a MySQL vehicles store by license plate, and render the result live in one Streamlit UI driven by a Python reaction โ€” with the controls that drive the changes built right in. +--- + +Imagine a store running curbside pickup. The **retail team** manages customer orders in one database and marks an order *ready* when it's prepared. Independently, the **physical operations team** tracks pickup vehicles in *another* database, and a driver sets their location to *Curbside* when they arrive. The two systems never talk to each other. + +You want a single live view with six panels: + +- an **Orders ยท Preparing** panel and an **Orders ยท Ready** panel, so an order visibly moves from one to the other the moment it's marked *ready*, +- a **Vehicles ยท Parking** panel and a **Vehicles ยท Curbside** panel, so a car moves between them as the driver pulls up, plus the two situations that matter: +- a **Matched Orders** panel that lights up the instant an order is *ready* **and** its driver is at the curbside, so staff know exactly which order to carry out, and +- a **Delayed Orders** panel that flags drivers who have been waiting at the curbside too long while their order still isn't ready. + +The catch: the orders live in **PostgreSQL** and the vehicles live in **MySQL**, and what you care about โ€” a *ready* order whose driver has *arrived* โ€” only exists when you combine a fact from each. Building this the traditional way means **polling both databases**, and you always end up reactive on one side and blind, between polls, on the other. This tutorial builds it with **`drasi-lib`** instead: two sources, six continuous queries (two of which join across both databases), and a **Python reaction** that renders one [Streamlit](https://streamlit.io/) UI. Drasi watches the change feed of *both* databases at once, so a change on **either** side re-evaluates the join immediately โ€” no polling, no blind side. And rather than a separate dashboard plus a separate console, the **one** Streamlit page both shows the six panels *and* has the buttons that drive the changes. + +<div class="flow-diagram"> + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-database"></i> + </div> + <div class="flow-step__label">Sources</div> + <div class="flow-step__description">Connect to your data sources</div> + </div> + + <div class="flow-arrow"> + <i class="fas fa-arrow-right"></i> + </div> + + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-filter"></i> + </div> + <div class="flow-step__label">Continuous Queries</div> + <div class="flow-step__description">Define what changes matter</div> + </div> + + <div class="flow-arrow"> + <i class="fas fa-arrow-right"></i> + </div> + + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-bolt"></i> + </div> + <div class="flow-step__label">Reactions</div> + <div class="flow-step__description">Take action automatically</div> + </div> +</div> + +| Step | What You'll Do | +| ---- | ------------- | +| **[Step 1: Set Up Your Environment](#setup)** | Open the dev container (or install Python + Docker locally) | +| **[Step 2: Run the Demo](#run)** | One command starts both databases and the Streamlit app | +| **[Step 3: Open the UI](#ui)** | Watch all six panels update live | +| **[Step 4: Drive Change](#drive)** | Use the buttons in the UI to change orders and vehicles, and watch Drasi react | +| **[How It Works](#how)** | Understand the two sources, the cross-database join, and the six queries | + +{{% alert title="Before you begin" color="info" %}} +- **One process:** the Streamlit app embeds the Drasi engine, so a single command runs everything. It stays in the foreground and you drive changes from your **browser**. +- **Working directory:** run every command from the tutorial directory (`tutorials/curbside-pickup/`). The dev container opens there automatically; if you're running locally, `cd tutorials/curbside-pickup` first. +- **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*). Use the one for your shell. The dev container and Codespaces use *bash*. +- **Ports:** the Streamlit UI is on `8501`, PostgreSQL is published on `5742`, and MySQL on `3309`. +{{% /alert %}} + +## Step 1 of 4: Set Up Your Environment {#setup} + +This tutorial needs **Docker** (it runs PostgreSQL and MySQL) and **Python 3.10+**. The easiest way to get everything is the **dev container**. + +### Option A: Dev Container or GitHub Codespaces (recommended) + +1. Open the [`drasi-python`](https://github.com/drasi-project/drasi-python) repository in VS Code and run **Reopen in Container** (or create a **Codespace** from the repo's **Code** menu). +2. When prompted for a configuration, choose **Curbside Pickup Tutorial (Python)**. +3. Wait for the container to finish. Its setup script installs the tutorial's Python dependencies and the Docker tooling for the two databases. + +That's it. Skip ahead to [Step 2](#run). + +### Option B: Run Locally + +You'll need **Python 3.10+**, **Docker**, and **bash** for the helper scripts (on Windows use Git Bash or WSL, or run the PowerShell scripts). From the repository root, move into the tutorial directory and install the dependencies: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +cd tutorials/curbside-pickup +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +cd tutorials/curbside-pickup +python -m venv .venv; .venv\Scripts\Activate.ps1 +pip install -r requirements.txt +{{< /tab >}} +{{< /tabpane >}} + +`drasi-lib` ships as a prebuilt wheel, so no Rust toolchain is needed. It downloads the Postgres and MySQL plugins it needs from `ghcr.io/drasi-project` the first time the app runs. + +## Step 2 of 4: Run the Demo {#run} + +Start the demo: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +bash scripts/start-demo.sh +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +powershell -ExecutionPolicy Bypass -File scripts/start-demo.ps1 +{{< /tab >}} +{{< /tabpane >}} + +The `start-demo` script does two things: + +1. **Starts both databases.** PostgreSQL comes up with logical replication enabled and the `orders` table seeded (three orders, all *preparing*). MySQL comes up with **ROW-based binary logging** and GTID mode enabled and the `vehicles` table seeded (three vehicles, all *Parking*) โ€” the plates match the orders. +2. **Launches the Streamlit app**, which embeds the Drasi engine. + +On first start, the app's embedded engine downloads the plugins it needs (`source/postgres`, `bootstrap/postgres`, `source/mysql`, `bootstrap/mysql`) from `ghcr.io/drasi-project`, connects to both databases, bootstraps the existing rows, starts the six continuous queries, and registers the reaction. Streamlit prints a URL like the following when it's ready: + +```text +Local URL: http://localhost:8501 +``` + +Leave this running. Everything else happens in your **browser**. + +{{% alert title="Stopping and resetting" color="info" %}} +Press **Ctrl+C** in the terminal to stop the app. To remove the database containers when you're completely done, run `bash scripts/cleanup.sh` (bash) or `powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1` (PowerShell). Add `--volumes` (bash) or `-RemoveVolumes` (PowerShell) to also delete the data. +{{% /alert %}} + +## Step 3 of 4: Open the UI {#ui} + +The app renders everything from the Python reaction โ€” there's no separate dashboard server and no separate console. **Wait until the app has finished starting** (the first run takes a little longer while the plugins download), then open it in your browser: + +```text +http://localhost:8501 +``` + +In the dev container or Codespaces, port `8501` is forwarded automatically. You'll see the **Curbside Pickup** UI with six panels: + +- ๐Ÿ• **Orders ยท Preparing** and ๐Ÿ• **Orders ยท Ready** โ€” every order in PostgreSQL, split by status. Each row has a button to move it. +- ๐Ÿš— **Vehicles ยท Parking** and ๐Ÿš— **Vehicles ยท Curbside** โ€” every vehicle in MySQL, split by location. Each row has a button to move it. +- ๐Ÿ“ฆ **Matched Orders** โ€” orders that are *ready* whose driver is at the *Curbside* (the `delivery` query). +- โš ๏ธ **Delayed Orders** โ€” drivers who have waited at the curbside for more than 10 seconds while their order is still being prepared (the `delay` query). + +At bootstrap all three orders are *preparing* and all three vehicles are in *Parking*, so those two panels list every row while their **Ready** and **Curbside** counterparts start empty. The **Matched** and **Delayed** panels also start **empty** and fill in as you drive changes. The **sidebar** has a **Reset** button and a live, color-coded **activity log** of the SQL each button runs. + +## Step 4 of 4: Drive Change {#drive} + +{{% alert title="No middle tier โ€” the buttons just write to a database" color="info" %}} +Each button on an order or vehicle runs a single SQL `UPDATE` โ€” orders in PostgreSQL (via `psycopg`), vehicles in MySQL (via `PyMySQL`) โ€” exactly what a real retail or physical-operations app would run. There's no API to call and no event to publish. Drasi observes the PostgreSQL change through logical replication and the MySQL change through the binary log, re-evaluates the affected queries (including the cross-database join), and calls the Python reaction, which updates the panels. The sidebar's **activity log** shows each statement and which database it hit. +{{% /alert %}} + +### Trigger a delivery + +In the ๐Ÿ• **Orders ยท Preparing** panel, press **Mark ready โ†’** on order **1** (Sophia Carter). It immediately leaves *Preparing* and appears in ๐Ÿ• **Orders ยท Ready**. Now, in the ๐Ÿš— **Vehicles ยท Parking** panel, press **To Curbside โ†’** on vehicle **A1234**. + +The vehicle jumps to ๐Ÿš— **Vehicles ยท Curbside**, and within about a second the ๐Ÿ“ฆ **Matched Orders** panel reacts: order **1** appears with its driver and vehicle. Nothing polled anything โ€” Drasi saw the PostgreSQL change and the MySQL change and re-evaluated the cross-database join. Send the vehicle back with **โ†ฉ Parking** and the row disappears from **Matched Orders**. + +### Trigger a delay + +Now reproduce the *other* scenario. Pick a vehicle whose order is **not** ready โ€” say **B5678** (Mason Rivera) โ€” and press **To Curbside โ†’**, but **leave order 2 as *preparing***. Nothing happens immediately. After **10 seconds** the โš ๏ธ **Delayed Orders** panel lights up: order **2** appears, flagging that the driver has been waiting too long. + +This is the interesting one. Drasi doesn't poll to find slow orders โ€” the **continuous query schedules its own future re-evaluation** for the moment the 10-second threshold is crossed, and fires exactly then. If you mark the order *ready* (or send the driver back to *Parking*) before the 10 seconds elapse, the alert never appears. + +### Reset + +Press **Reset everything** in the sidebar to return to the starting state (all orders *preparing*, all vehicles *Parking*). + +## How It Works {#how} + +The whole demo is a single Streamlit program in `tutorials/curbside-pickup/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state and hosts the controls. + +### Two Sources + +The queries join data from two different databases, so the engine adds two sources. Installing the plugins and adding the sources is a few lines of Python: + +```python +for plugin in ("source/postgres", "bootstrap/postgres", "source/mysql", "bootstrap/mysql"): + await drasi.install_plugin(plugin) +await drasi.start() + +# PostgreSQL holds the orders and streams changes via logical replication (CDC). +await drasi.add_source( + "postgres", + "retail-ops", + { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.orders"], + "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], + "slotName": "drasi_curbside_slot", + "publicationName": "drasi_curbside_pub", + }, + bootstrap={"kind": "postgres", **POSTGRES}, +) + +# MySQL holds the vehicles and streams changes via its binary log (binlog). +await drasi.add_source( + "mysql", + "physical-ops", + { + **MYSQL, + "sslMode": "disabled", + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], + }, + bootstrap={ + "kind": "mysql", + **MYSQL, + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], + }, +) +``` + +The MySQL container is started with ROW-based logging, full row images/metadata and GTID mode (see `database/docker-compose.yml`), and `database/mysql-init.sql` grants the Drasi user the replication privileges it needs to read the binlog. Each table row becomes a graph node: `orders` rows become `(o:orders)` and `vehicles` rows become `(v:vehicles)`. + +### The Synthetic Join + +There is no foreign key between the two databases โ€” they're completely separate systems. Drasi creates the relationship in the query with a **synthetic join**, matching a vehicle to an order whenever their `plate` values are equal: + +```python +PICKUP_BY = { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], +} +``` + +The `delivery` and `delay` queries read from **both** sources and pass `joins=[PICKUP_BY]` to `add_query`, then walk `(o:orders)-[:PICKUP_BY]->(v:vehicles)` as if it were a real graph edge โ€” across two different databases. + +### The Six Continuous Queries + +Four are simple single-source lists, filtered by state, that feed the split **Orders** and **Vehicles** panels โ€” because each matches only part of a table, a row leaves one query's result and joins the other's the instant its status or location changes: + +```cypher +MATCH (o:orders) +WHERE o.status <> 'ready' // orders-ready uses: o.status = 'ready' +RETURN o.id AS id, o.customer_name AS customerName, o.plate AS plate +``` + +The other two join across both databases. **`delivery`** returns an order whenever it is *ready* and its driver's vehicle is at the *Curbside*: + +```cypher +MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) +WHERE o.status = 'ready' AND v.location = 'Curbside' +RETURN o.id AS id, o.customer_name AS customerName, o.plate AS vehicleId, v.make AS vehicleMake +``` + +**`delay`** returns an order whose driver has been at the *Curbside* for more than 10 seconds while the order still isn't *ready*. It uses **`drasi.trueFor`**, which schedules a future re-evaluation and fires the moment the condition has held for the given duration, so the order appears exactly when the 10-second threshold is crossed: + +```cypher +MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) +WHERE o.status <> 'ready' + AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) +RETURN o.id AS orderId, o.customer_name AS customerName, + drasi.changeDateTime(v) AS waitingSinceTimestamp +``` + +{{% alert title="How the timing works" color="info" %}} +Both the PostgreSQL and MySQL sources stamp every change with the wall-clock time it happened, which `drasi.changeDateTime()` exposes. `drasi.trueFor` uses that timestamp to anchor its 10-second timer โ€” so the delay query fires exactly when a curbside vehicle has waited long enough โ€” with no extra timestamp columns or application bookkeeping. The `delivery` query uses `drasi.listMax([drasi.changeDateTime(o), drasi.changeDateTime(v)])` to report whichever of the two changes happened later. +{{% /alert %}} + +### The Python Reaction + +A single **Python reaction** subscribes to all six queries. Each time a query's result set changes โ€” including the delayed re-evaluation the `delay` query schedules โ€” Drasi calls it with the query id and the diffs, and the callback updates a thread-safe snapshot the UI reads: + +```python +def on_results(event): + query = QUERIES_BY_ID[event["query_id"]] + with lock: + store = snapshot[query.id] # this query's rows, keyed by id + for diff in event["results"]: + row = diff.get("data") or diff.get("after") + if diff["type"] == "DELETE": + store.pop(row[query.key], None) + else: # ADD or UPDATE: upsert by row identity + store[row[query.key]] = row + + +await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) +``` + +### One integrated Streamlit UI + +The engine runs on its own asyncio event loop in a background daemon thread, created once with `@st.cache_resource`, and `app.py` reads `engine.snapshot()` each run and renders the six panels with a ~1-second auto-refresh. Because it's the same page, the panels can *also* drive the demo: each order and vehicle row has a button that runs the matching `UPDATE`, the sidebar has a **Reset** button, and every write is appended to the activity log the sidebar shows. A `delay` row appearing 10 seconds after you move a vehicle to the curbside is the reaction receiving `drasi.trueFor`'s scheduled re-evaluation โ€” live, with no polling. + +<img src="images/curbside-ui.png" width="960" alt="The Curbside Pickup Streamlit UI: four panels of orders and vehicles with inline buttons across the top, Matched and Delayed panels below, and a sidebar with a reset button and a color-coded SQL activity log"> + +## Clean Up {#cleanup} + +When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database containers: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +# Stop containers, keep data +bash scripts/cleanup.sh + +# Stop containers and delete the data volumes +bash scripts/cleanup.sh --volumes +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +# Stop containers, keep data +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 + +# Stop containers and delete the data volumes +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 -RemoveVolumes +{{< /tab >}} +{{< /tabpane >}} + +## What You Learned {#summary} + +- **Sources** connect Drasi to live data โ€” here, **two** databases at once, PostgreSQL via logical replication and MySQL via the binary log. +- **Continuous Queries** with a **synthetic join** relate rows across those two databases (order โ†” vehicle by plate), and a **temporal** query (`drasi.trueFor`) fires exactly when a condition has held long enough โ€” no polling, no bookkeeping. +- A **Python reaction** turned all six queries into one live UI, and because it's a Streamlit page, the same UI hosts the controls that drive the changes โ€” no dashboard reaction and no separate console. +- Because Drasi emits only what *changed*, every panel updates the instant the data does, whichever database changed. + +From here, try changing the delay threshold, adding an order/vehicle, or pointing the sources at your own databases. diff --git a/tutorials/curbside-pickup/app.py b/tutorials/curbside-pickup/app.py new file mode 100644 index 0000000..2c5d9b5 --- /dev/null +++ b/tutorials/curbside-pickup/app.py @@ -0,0 +1,190 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Curbside Pickup -- one integrated Streamlit UI driven by a Drasi reaction. + +Run it with: + + streamlit run app.py + +The six panels are rendered live from a Python reaction over two databases, and +the same page drives the changes: the Orders and Vehicles panels have inline +buttons that write to Postgres / MySQL, and the sidebar has a reset button and a +color-coded log of the SQL each button ran. No dashboard reaction, no separate +console -- the reaction *is* the application. +""" + +from __future__ import annotations + +from typing import Any + +import streamlit as st +from demo.config import DELAY_SECONDS +from demo.engine import CurbsideEngine +from demo.queries import ( + DELAY, + DELIVERY, + ORDERS_PREPARING, + ORDERS_READY, + VEHICLES_CURBSIDE, + VEHICLES_PARKING, +) +from streamlit_autorefresh import st_autorefresh + +st.set_page_config(page_title="Curbside Pickup", page_icon="๐Ÿ›’", layout="wide") + + +@st.cache_resource +def get_engine() -> CurbsideEngine: + """Create (once) and return the shared Drasi engine.""" + engine = CurbsideEngine() + with st.spinner("Starting Drasi: installing plugins and connecting to Postgres + MySQL..."): + engine.wait_ready() + return engine + + +def order_panel(rows: list[dict[str, Any]], *, ready: bool, engine: CurbsideEngine) -> None: + title = "๐Ÿ• Orders ยท Ready" if ready else "๐Ÿ• Orders ยท Preparing" + st.subheader(title) + if not rows: + st.caption("_No orders ready._" if ready else "_No orders being prepared._") + return + for row in sorted(rows, key=lambda r: r["orderId"]): + st.markdown(f"**Order {row['orderId']}** โ€” {row['customerName']} โ€” plate `{row['plate']}`") + if ready: + if st.button("โ†ฉ Preparing", key=f"order-prep-{row['id']}", use_container_width=True): + engine.set_order_status(int(row["id"]), "preparing") + st.rerun() + else: + if st.button("Mark ready โ†’", key=f"order-ready-{row['id']}", use_container_width=True): + engine.set_order_status(int(row["id"]), "ready") + st.rerun() + + +def vehicle_panel(rows: list[dict[str, Any]], *, curbside: bool, engine: CurbsideEngine) -> None: + title = "๐Ÿš— Vehicles ยท Curbside" if curbside else "๐Ÿš— Vehicles ยท Parking" + st.subheader(title) + if not rows: + st.caption("_No vehicles at the curb._" if curbside else "_No vehicles parked._") + return + for row in sorted(rows, key=lambda r: r["plate"]): + st.markdown(f"**`{row['plate']}`** โ€” {row['color']} {row['make']} {row['model']}") + if curbside: + if st.button("โ†ฉ Parking", key=f"veh-park-{row['plate']}", use_container_width=True): + engine.set_vehicle_location(row["plate"], "Parking") + st.rerun() + else: + if st.button("To Curbside โ†’", key=f"veh-curb-{row['plate']}", use_container_width=True): + engine.set_vehicle_location(row["plate"], "Curbside") + st.rerun() + + +def matched_panel(rows: list[dict[str, Any]]) -> None: + st.subheader("๐Ÿ“ฆ Matched Orders") + st.caption("Ready **and** the driver is at the curbside โ€” carry it out.") + if not rows: + st.info("No matched orders yet. Mark an order ready and bring its vehicle to the curbside.") + return + for row in sorted(rows, key=lambda r: r["orderId"]): + st.success( + f"**Order {row['orderId']}** โ€” {row['customerName']} โ€” " + f"{row['vehicleColor']} {row['vehicleMake']} {row['vehicleModel']} " + f"(`{row['vehicleId']}`) is at the curbside." + ) + + +def _short_time(value: Any) -> str: + """Show just the clock time from an ISO timestamp like 2026-07-31T00:58:53+00:00.""" + text = str(value) + if "T" in text: + return text.split("T", 1)[1][:8] + return text + + +def delayed_panel(rows: list[dict[str, Any]]) -> None: + st.subheader("โš ๏ธ Delayed Orders") + st.caption(f"At the curbside over {DELAY_SECONDS}s while the order is still not ready.") + if not rows: + st.info("No delayed orders.") + return + for row in sorted(rows, key=lambda r: r["orderId"]): + st.warning( + f"**Order {row['orderId']}** โ€” {row['customerName']} โ€” " + f"waiting since {_short_time(row.get('waitingSinceTimestamp', ''))}" + ) + + +def render_sidebar(engine: CurbsideEngine, activity: list[dict[str, Any]]) -> None: + st.sidebar.header("Drive the demo") + st.sidebar.caption( + "The buttons on each order and vehicle run a real SQL UPDATE โ€” orders in " + "PostgreSQL, vehicles in MySQL. Drasi observes both via CDC and the panels " + "update on their own." + ) + if st.sidebar.button("Reset everything", use_container_width=True): + engine.reset() + st.toast("Reset: all orders preparing, all vehicles parking.") + st.rerun() + + st.sidebar.divider() + st.sidebar.subheader("Activity log") + if not activity: + st.sidebar.caption("SQL you run will appear here, colored by database.") + return + for entry in reversed(activity): + color = "blue" if entry["db"] == "PostgreSQL" else "orange" + st.sidebar.markdown( + f":gray[{entry['at']}] :{color}[**[{entry['db']}]**] \n`{entry['sql']}`" + ) + + +def main() -> None: + engine = get_engine() + st_autorefresh(interval=1000, key="curbside-refresh") + + snapshot = engine.snapshot() + if snapshot["error"]: + st.error(f"The Drasi engine failed: {snapshot['error']}") + return + results = snapshot["results"] + + st.title("๐Ÿ›’ Curbside Pickup") + st.caption( + "Orders live in **PostgreSQL**, vehicles live in **MySQL**. Drasi joins them " + "by license plate and reacts to changes on either side โ€” no polling." + ) + + render_sidebar(engine, snapshot["activity"]) + + top = st.columns(4) + with top[0]: + order_panel(results.get(ORDERS_PREPARING, []), ready=False, engine=engine) + with top[1]: + order_panel(results.get(ORDERS_READY, []), ready=True, engine=engine) + with top[2]: + vehicle_panel(results.get(VEHICLES_PARKING, []), curbside=False, engine=engine) + with top[3]: + vehicle_panel(results.get(VEHICLES_CURBSIDE, []), curbside=True, engine=engine) + + st.divider() + + bottom = st.columns(2) + with bottom[0]: + matched_panel(results.get(DELIVERY, [])) + with bottom[1]: + delayed_panel(results.get(DELAY, [])) + + +if __name__ == "__main__": + main() diff --git a/tutorials/curbside-pickup/database/docker-compose.yml b/tutorials/curbside-pickup/database/docker-compose.yml new file mode 100644 index 0000000..20322d5 --- /dev/null +++ b/tutorials/curbside-pickup/database/docker-compose.yml @@ -0,0 +1,93 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Two independent databases for the Curbside Pickup tutorial: +# - postgres : the Retail Operations store (orders) +# - mysql : the Physical Operations store (vehicles) +# Both are configured for change data capture (CDC) so Drasi can stream their +# changes in real time. + +name: curbside-pickup + +services: + # --------------------------------------------------------------------------- + # Retail Operations - PostgreSQL (orders) + # --------------------------------------------------------------------------- + postgres: + image: postgres:15-alpine + container_name: curbside-pickup-postgres + environment: + POSTGRES_DB: RetailOperations + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres_admin + command: + # wal_level=logical is required for logical replication (CDC). + - "postgres" + - "-c" + - "wal_level=logical" + - "-c" + - "max_replication_slots=10" + - "-c" + - "max_wal_senders=10" + ports: + - "${POSTGRES_HOST_PORT:-5742}:5432" + volumes: + - curbside_postgres_data:/var/lib/postgresql/data + healthcheck: + # Probe over TCP (-h localhost), not the Unix socket. On first init the + # postgres image runs a temporary socket-only server to create POSTGRES_DB; + # a socket probe can report "ready" before RetailOperations exists. + test: ["CMD-SHELL", "pg_isready -h localhost -U postgres -d RetailOperations"] + interval: 5s + timeout: 5s + retries: 5 + + # --------------------------------------------------------------------------- + # Physical Operations - MySQL (vehicles) + # --------------------------------------------------------------------------- + # The Drasi MySQL source captures changes from the binary log (binlog), so the + # server is started with ROW-based binary logging, full row images, full row + # metadata (column names) and GTID mode - all required by the source. The + # drasi_user is granted REPLICATION SLAVE / REPLICATION CLIENT in + # database/mysql-init.sql so it can stream the binlog. + mysql: + image: mysql:8.0 + container_name: curbside-pickup-mysql + environment: + MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:-root_admin}" + MYSQL_DATABASE: "${MYSQL_DATABASE:-PhysicalOperations}" + MYSQL_USER: "${MYSQL_USER:-drasi_user}" + MYSQL_PASSWORD: "${MYSQL_PASSWORD:-drasi_password}" + command: + - "--server-id=1" + - "--log-bin=mysql-bin" + - "--binlog-format=ROW" + - "--binlog-row-image=FULL" + - "--binlog-row-metadata=FULL" + - "--gtid-mode=ON" + - "--enforce-gtid-consistency=ON" + ports: + - "${MYSQL_HOST_PORT:-3309}:3306" + volumes: + - curbside_mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p\"$$MYSQL_ROOT_PASSWORD\" || exit 1"] + interval: 5s + timeout: 5s + retries: 12 + start_period: 20s + +volumes: + curbside_postgres_data: + curbside_mysql_data: diff --git a/tutorials/curbside-pickup/database/mysql-init.sql b/tutorials/curbside-pickup/database/mysql-init.sql new file mode 100644 index 0000000..4678d46 --- /dev/null +++ b/tutorials/curbside-pickup/database/mysql-init.sql @@ -0,0 +1,56 @@ +-- Copyright 2026 The Drasi Authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +-- Curbside Pickup Tutorial - Physical Operations (MySQL) +-- +-- Tracks the pickup vehicles. A driver updates their `location` to 'Curbside' +-- when they arrive at the pickup zone. The Drasi MySQL source captures changes +-- by streaming the binary log (binlog), which the container enables with +-- ROW-based logging, full row images/metadata and GTID mode (see +-- docker-compose.yml). The `drasi_user` (created from MYSQL_USER) is granted +-- replication privileges below so it can read the binlog. +-- +-- The table is `vehicles`; the Drasi source is configured with +-- `tables: [vehicles]`, which yields the node label `vehicles`, matching +-- (v:vehicles) in the Cypher continuous queries. + +USE PhysicalOperations; + +-- vehicles table: one row per pickup vehicle. `plate` is the natural key and the +-- join key to the orders table in PostgreSQL. +DROP TABLE IF EXISTS vehicles; + +CREATE TABLE vehicles ( + plate VARCHAR(10) NOT NULL PRIMARY KEY, + driver_name VARCHAR(50) NOT NULL, + customer_name VARCHAR(50) NOT NULL, + make VARCHAR(50) NOT NULL, + model VARCHAR(50) NOT NULL, + color VARCHAR(30) NOT NULL, + location VARCHAR(20) NOT NULL DEFAULT 'Parking' +); + +-- Seed three vehicles, all parked. The plates match the seeded orders in the +-- PostgreSQL database so the PICKUP_BY join lines up. +INSERT INTO vehicles (plate, driver_name, customer_name, make, model, color, location) VALUES + ('A1234', 'Elijah Brooks', 'Sophia Carter', 'Toyota', 'Camry', 'Blue', 'Parking'), + ('B5678', 'Isabella Reed', 'Mason Rivera', 'Ford', 'F-150', 'Red', 'Parking'), + ('C9876', 'Liam Bennett', 'Ava Mitchell', 'Honda', 'Civic', 'Black', 'Parking'); + +-- Grant the Drasi user the privileges it needs to stream the binlog. MYSQL_USER +-- (drasi_user) already exists with full access to the PhysicalOperations +-- database; it additionally needs the server-wide replication privileges to open +-- a binlog stream. +GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'drasi_user'@'%'; +FLUSH PRIVILEGES; diff --git a/tutorials/curbside-pickup/database/postgres-init.sql b/tutorials/curbside-pickup/database/postgres-init.sql new file mode 100644 index 0000000..b4a9528 --- /dev/null +++ b/tutorials/curbside-pickup/database/postgres-init.sql @@ -0,0 +1,97 @@ +-- Copyright 2026 The Drasi Authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +-- Curbside Pickup Tutorial - Retail Operations (PostgreSQL) +-- +-- Holds the customer orders. The retail team marks an order 'ready' when it is +-- prepared. The `orders` table is lower-case and unquoted so the node label +-- Drasi sees matches the Cypher continuous queries, which use (o:orders) and +-- o.status, o.plate, o.driver_name, o.customer_name without any change. + +-- Suppress noisy output during setup. +\set QUIET on +SET client_min_messages = ERROR; + +-- Create a user with replication privileges for CDC. +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'drasi_user') THEN + CREATE USER drasi_user WITH REPLICATION LOGIN PASSWORD 'drasi_password'; + END IF; +END +$$; + +-- Grant permissions on the database. +GRANT CREATE ON DATABASE "RetailOperations" TO drasi_user; +GRANT ALL PRIVILEGES ON DATABASE "RetailOperations" TO drasi_user; + +-- Drop existing table if it exists. +DROP TABLE IF EXISTS orders CASCADE; + +-- orders table: one row per customer order awaiting curbside pickup. +CREATE TABLE orders ( + id SERIAL PRIMARY KEY, + customer_name VARCHAR(255) NOT NULL, + driver_name VARCHAR(255) NOT NULL, + plate VARCHAR(50) NOT NULL, + status VARCHAR(50) NOT NULL DEFAULT 'preparing' +); + +-- Enforce the order status enum. +ALTER TABLE orders ADD CONSTRAINT chk_status CHECK (status IN ('preparing', 'ready')); + +-- Set REPLICA IDENTITY to FULL so change events include every column. +ALTER TABLE orders REPLICA IDENTITY FULL; + +-- Ensure drasi_user owns the table. +ALTER TABLE orders OWNER TO drasi_user; + +-- Grant permissions to drasi_user. +GRANT USAGE ON SCHEMA public TO drasi_user; +GRANT SELECT ON ALL TABLES IN SCHEMA public TO drasi_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO drasi_user; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO drasi_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO drasi_user; + +-- Create the publication for logical replication. The replication slot itself is +-- NOT created here: the Drasi PostgreSQL source creates it on startup and takes a +-- consistent snapshot, so the rows seeded below are loaded once via bootstrap and +-- are not also replayed as change events (which would double-count them). +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'drasi_curbside_pub') THEN + CREATE PUBLICATION drasi_curbside_pub FOR TABLE orders; + END IF; +END +$$; + +-- Seed three orders, all 'preparing'. The plates match the seeded vehicles in the +-- MySQL database so the PICKUP_BY join lines up. +INSERT INTO orders (customer_name, driver_name, plate, status) +SELECT * FROM (VALUES + ('Sophia Carter', 'Elijah Brooks', 'A1234', 'preparing'), + ('Mason Rivera', 'Isabella Reed', 'B5678', 'preparing'), + ('Ava Mitchell', 'Liam Bennett', 'C9876', 'preparing') +) AS d(customer_name, driver_name, plate, status) +WHERE NOT EXISTS (SELECT 1 FROM orders); + +-- Summary. +SET client_min_messages = NOTICE; +DO $$ +BEGIN + RAISE NOTICE 'Retail Operations database initialized successfully!'; + RAISE NOTICE 'Table: orders'; + RAISE NOTICE 'Publication: drasi_curbside_pub'; +END +$$; diff --git a/tutorials/curbside-pickup/demo/__init__.py b/tutorials/curbside-pickup/demo/__init__.py new file mode 100644 index 0000000..28c1c7a --- /dev/null +++ b/tutorials/curbside-pickup/demo/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The Curbside Pickup demo package: an embedded Drasi engine and its queries.""" + +from .engine import CurbsideEngine + +__all__ = ["CurbsideEngine"] diff --git a/tutorials/curbside-pickup/demo/config.py b/tutorials/curbside-pickup/demo/config.py new file mode 100644 index 0000000..a5246c1 --- /dev/null +++ b/tutorials/curbside-pickup/demo/config.py @@ -0,0 +1,109 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Configuration for the Curbside Pickup tutorial. + +Two independent databases: PostgreSQL holds the orders (Retail Operations) and +MySQL holds the vehicles (Physical Operations). This module has the connection +settings for both -- for Drasi's source plugins and for the direct SQL writes the +UI makes (psycopg for Postgres, PyMySQL for MySQL) -- read from the environment +with the same defaults the tutorial's databases use. +""" + +from __future__ import annotations + +import os + +# --- PostgreSQL (orders) ----------------------------------------------------- + +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5742")), + "database": os.environ.get("POSTGRES_DATABASE", "RetailOperations"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} + +# The Postgres source plugin creates its own replication slot on startup and +# reuses the publication created by database/postgres-init.sql. +POSTGRES_SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.orders"], + "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], + "slotName": "drasi_curbside_slot", + "publicationName": "drasi_curbside_pub", +} +POSTGRES_BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} + +# psycopg spells the database key ``dbname``; the UI writes order status changes +# through this connection so Drasi observes them via logical replication. +PSYCOPG_CONNECTION = { + "host": POSTGRES["host"], + "port": POSTGRES["port"], + "dbname": POSTGRES["database"], + "user": POSTGRES["user"], + "password": POSTGRES["password"], +} + +# --- MySQL (vehicles) -------------------------------------------------------- + +MYSQL = { + "host": os.environ.get("MYSQL_HOST", "localhost"), + "port": int(os.environ.get("MYSQL_PORT", "3309")), + "database": os.environ.get("MYSQL_DATABASE", "PhysicalOperations"), + "user": os.environ.get("MYSQL_USER", "drasi_user"), + "password": os.environ.get("MYSQL_PASSWORD", "drasi_password"), +} + +# The MySQL source streams the binlog. TLS is disabled on the tutorial container, +# so the source must connect in plaintext. Unlike Postgres, the MySQL bootstrap +# provider takes its own connection settings (and does not accept sslMode). +MYSQL_SOURCE_CONFIG = { + **MYSQL, + "sslMode": "disabled", + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], +} +MYSQL_BOOTSTRAP_CONFIG = { + "kind": "mysql", + **MYSQL, + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], +} + +# PyMySQL spells the database key ``database``; the UI writes vehicle location +# changes through this connection so Drasi observes them via the binlog. +PYMYSQL_CONNECTION = { + "host": MYSQL["host"], + "port": MYSQL["port"], + "database": MYSQL["database"], + "user": MYSQL["user"], + "password": MYSQL["password"], + "autocommit": True, +} + +# --- Demo constants ---------------------------------------------------------- + +ORDER_PREPARING = "preparing" +ORDER_READY = "ready" +VEHICLE_PARKING = "Parking" +VEHICLE_CURBSIDE = "Curbside" + +# How long (matching the delay query) a curbside vehicle waits on an unready order +# before it is flagged as delayed. Shown in the UI copy. +DELAY_SECONDS = 10 + +# How many recent SQL writes to keep in the activity log. +ACTIVITY_LOG_SIZE = 20 diff --git a/tutorials/curbside-pickup/demo/engine.py b/tutorials/curbside-pickup/demo/engine.py new file mode 100644 index 0000000..93ce71f --- /dev/null +++ b/tutorials/curbside-pickup/demo/engine.py @@ -0,0 +1,222 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The embedded Drasi engine that powers the Streamlit UI. + +The same pattern as the Building Comfort tutorial: the Drasi engine runs on its +own asyncio event loop in a background daemon thread (created once, cached by +Streamlit), and a single **Python reaction** over all six queries keeps a +thread-safe snapshot the UI renders. + +What is new here is that there are **two** databases. Drasi reads changes from a +PostgreSQL `orders` table and a MySQL `vehicles` table and joins them by license +plate. The UI's controls write order changes to Postgres (via ``psycopg``) and +vehicle changes to MySQL (via ``PyMySQL``); Drasi observes both through CDC. Each +write is also appended to an in-memory activity log the UI shows. +""" + +from __future__ import annotations + +import os + +# Quiet the engine's default INFO logging. Must run before ``drasi`` is imported. +os.environ.setdefault("RUST_LOG", "warn") + +import asyncio # noqa: E402 +import threading # noqa: E402 +import time # noqa: E402 +from collections import deque # noqa: E402 +from typing import Any # noqa: E402 + +import psycopg # noqa: E402 +import pymysql # noqa: E402 + +from drasi import Drasi # noqa: E402 +from drasi.types import QueryResultEvent # noqa: E402 + +from .config import ( # noqa: E402 + ACTIVITY_LOG_SIZE, + MYSQL_BOOTSTRAP_CONFIG, + MYSQL_SOURCE_CONFIG, + ORDER_PREPARING, + POSTGRES_BOOTSTRAP_CONFIG, + POSTGRES_SOURCE_CONFIG, + PSYCOPG_CONNECTION, + PYMYSQL_CONNECTION, + VEHICLE_PARKING, +) +from .queries import PHYSICAL_OPS, QUERIES, RETAIL_OPS # noqa: E402 + + +class CurbsideEngine: + """Runs Drasi over two databases and exposes a thread-safe snapshot.""" + + def __init__(self) -> None: + self._lock = threading.Lock() + # Per query: a dict keyed by that query's primary-key field. The reaction + # applies diffs into it; the UI reads a snapshot of the values. + self._results: dict[str, dict[Any, dict[str, Any]]] = {} + # Which RETURN column identifies each query's rows, looked up by query id. + self._key_by_id = {query.id: query.key for query in QUERIES} + self._version = 0 + # Recent SQL writes, newest last: {"db": ..., "sql": ..., "at": ...}. + self._activity: deque[dict[str, Any]] = deque(maxlen=ACTIVITY_LOG_SIZE) + + self._loop: asyncio.AbstractEventLoop | None = None + self._drasi: Drasi | None = None + + self._ready = threading.Event() + self._error: BaseException | None = None + + self._thread = threading.Thread(target=self._run, name="drasi-curbside-engine", daemon=True) + self._thread.start() + + # -- lifecycle (background thread) --------------------------------------- + + def _run(self) -> None: + loop = asyncio.new_event_loop() + self._loop = loop + asyncio.set_event_loop(loop) + try: + loop.run_until_complete(self._start()) + except BaseException as exc: # noqa: BLE001 - surfaced to the UI thread + self._error = exc + self._ready.set() + return + self._ready.set() + loop.run_forever() + + async def _start(self) -> None: + self._drasi = await Drasi.create("curbside-pickup") + + for plugin in ( + "source/postgres", + "bootstrap/postgres", + "source/mysql", + "bootstrap/mysql", + ): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + # Two sources: PostgreSQL orders and MySQL vehicles. + await self._drasi.add_source( + "postgres", RETAIL_OPS, POSTGRES_SOURCE_CONFIG, bootstrap=POSTGRES_BOOTSTRAP_CONFIG + ) + await self._drasi.add_source( + "mysql", PHYSICAL_OPS, MYSQL_SOURCE_CONFIG, bootstrap=MYSQL_BOOTSTRAP_CONFIG + ) + + for query in QUERIES: + await self._drasi.add_query( + query.id, query.cypher, query.sources, joins=query.joins or None + ) + for query in QUERIES: + await self._drasi.wait_for_query(query.id, timeout=60) + + # Prime the snapshot from the bootstrapped result sets, then let the + # reaction keep it current. + for query in QUERIES: + rows = await self._drasi.get_query_results(query.id) + with self._lock: + self._results[query.id] = {row[query.key]: row for row in rows} + + await self._drasi.add_python_reaction( + "ui", [query.id for query in QUERIES], self._on_results + ) + + def _on_results(self, event: QueryResultEvent) -> None: + """Apply a batch of diffs to the snapshot (called synchronously). + + Every diff carries the affected row in ``data`` (for an UPDATE it equals + ``after``), so the snapshot is kept by row identity: upsert on ADD/UPDATE, + drop on DELETE. + """ + query_id = event["query_id"] + key_field = self._key_by_id[query_id] + with self._lock: + store = self._results.setdefault(query_id, {}) + for diff in event["results"]: + row = diff.get("data") or diff.get("after") + if row is None: + continue + key = row.get(key_field) + if diff["type"] == "DELETE": + store.pop(key, None) + else: + store[key] = row + self._version += 1 + + # -- readiness ----------------------------------------------------------- + + def wait_ready(self, timeout: float = 240.0) -> None: + """Block until the engine is running, raising if it failed to start.""" + if not self._ready.wait(timeout): + raise TimeoutError("the Drasi engine did not start in time") + if self._error is not None: + raise RuntimeError(f"the Drasi engine failed to start: {self._error}") + + # -- reads (any thread) -------------------------------------------------- + + def snapshot(self) -> dict[str, Any]: + """Return the current result set of every query, plus the activity log.""" + with self._lock: + results = {qid: list(store.values()) for qid, store in self._results.items()} + activity = list(self._activity) + return { + "version": self._version, + "results": results, + "activity": activity, + "error": str(self._error) if self._error else None, + } + + # -- writes (any thread -> a database, observed via CDC) ----------------- + + def set_order_status(self, order_id: int, status: str) -> None: + """Update one order's status in PostgreSQL.""" + with psycopg.connect(**PSYCOPG_CONNECTION) as conn: + conn.execute("UPDATE orders SET status = %s WHERE id = %s", (status, order_id)) + self._log("PostgreSQL", f"UPDATE orders SET status='{status}' WHERE id={order_id};") + + def set_vehicle_location(self, plate: str, location: str) -> None: + """Update one vehicle's location in MySQL.""" + conn = pymysql.connect(**PYMYSQL_CONNECTION) + try: + with conn.cursor() as cur: + cur.execute("UPDATE vehicles SET location = %s WHERE plate = %s", (location, plate)) + finally: + conn.close() + self._log("MySQL", f"UPDATE vehicles SET location='{location}' WHERE plate='{plate}';") + + def reset(self) -> None: + """Return everything to the start: all orders preparing, all parked.""" + with psycopg.connect(**PSYCOPG_CONNECTION) as conn: + conn.execute( + "UPDATE orders SET status = %s WHERE status <> %s", + (ORDER_PREPARING, ORDER_PREPARING), + ) + conn2 = pymysql.connect(**PYMYSQL_CONNECTION) + try: + with conn2.cursor() as cur: + cur.execute( + "UPDATE vehicles SET location = %s WHERE location <> %s", + (VEHICLE_PARKING, VEHICLE_PARKING), + ) + finally: + conn2.close() + self._log("PostgreSQL", f"UPDATE orders SET status='{ORDER_PREPARING}';") + self._log("MySQL", f"UPDATE vehicles SET location='{VEHICLE_PARKING}';") + + def _log(self, db: str, sql: str) -> None: + with self._lock: + self._activity.append({"db": db, "sql": sql, "at": time.strftime("%H:%M:%S")}) diff --git a/tutorials/curbside-pickup/demo/queries.py b/tutorials/curbside-pickup/demo/queries.py new file mode 100644 index 0000000..ed43e2f --- /dev/null +++ b/tutorials/curbside-pickup/demo/queries.py @@ -0,0 +1,196 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The six continuous queries that power the demo. + +Four are single-source filtered lists that split the orders and vehicles by +state, so a row hops from one panel to another the instant it changes. Two join +across *both* databases through a synthetic join on license plate: + + delivery : an order is 'ready' AND its driver's vehicle is at the 'Curbside' + delay : a driver has waited at the 'Curbside' for over 10s while the order + is still being prepared (uses the temporal drasi.trueFor) + +Each query is written out in full so you can read exactly what Drasi runs. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any + +# Source ids (one per database). +RETAIL_OPS = "retail-ops" # PostgreSQL: orders +PHYSICAL_OPS = "physical-ops" # MySQL: vehicles + +# --- Synthetic join ---------------------------------------------------------- +# There is no foreign key between the two databases. Drasi creates the +# relationship in the query, matching a vehicle to an order by equal plate. +PICKUP_BY = { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], +} + +# --- Query ids --------------------------------------------------------------- +# The UI reads each query's result set from the snapshot by these ids. + +ORDERS_PREPARING = "orders-preparing" +ORDERS_READY = "orders-ready" +VEHICLES_PARKING = "vehicles-parking" +VEHICLES_CURBSIDE = "vehicles-curbside" +DELIVERY = "delivery" +DELAY = "delay" + + +@dataclass(frozen=True) +class Query: + """One continuous query: how to register it and how to index its rows.""" + + id: str + key: str # the RETURN column that identifies each row (its primary key) + sources: list[str] # the source ids this query reads from + cypher: str + joins: list[dict[str, Any]] = field(default_factory=list) + + +# Orders still being prepared (status != 'ready'). +ORDERS_PREPARING_QUERY = Query( + id=ORDERS_PREPARING, + key="id", + sources=[RETAIL_OPS], + cypher=""" + MATCH (o:orders) + WHERE o.status <> 'ready' + RETURN + o.id AS id, + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS plate, + o.status AS status + """, +) + +# Orders that are ready for pickup (status = 'ready'). +ORDERS_READY_QUERY = Query( + id=ORDERS_READY, + key="id", + sources=[RETAIL_OPS], + cypher=""" + MATCH (o:orders) + WHERE o.status = 'ready' + RETURN + o.id AS id, + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS plate, + o.status AS status + """, +) + +# Vehicles still in the parking lot (location = 'Parking'). +VEHICLES_PARKING_QUERY = Query( + id=VEHICLES_PARKING, + key="id", + sources=[PHYSICAL_OPS], + cypher=""" + MATCH (v:vehicles) + WHERE v.location = 'Parking' + RETURN + v.plate AS id, + v.plate AS plate, + v.make AS make, + v.model AS model, + v.color AS color, + v.location AS location + """, +) + +# Vehicles waiting at the curb (location = 'Curbside'). +VEHICLES_CURBSIDE_QUERY = Query( + id=VEHICLES_CURBSIDE, + key="id", + sources=[PHYSICAL_OPS], + cypher=""" + MATCH (v:vehicles) + WHERE v.location = 'Curbside' + RETURN + v.plate AS id, + v.plate AS plate, + v.make AS make, + v.model AS model, + v.color AS color, + v.location AS location + """, +) + +# Matched orders: ready AND the driver's vehicle is at the curbside. Joins the +# PostgreSQL order to the MySQL vehicle by plate. drasi.changeDateTime exposes the +# wall-clock time of each change; drasi.listMax picks the later of the two. +DELIVERY_QUERY = Query( + id=DELIVERY, + key="id", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status = 'ready' AND v.location = 'Curbside' + RETURN + o.id AS id, + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS vehicleId, + v.make AS vehicleMake, + v.model AS vehicleModel, + v.color AS vehicleColor, + v.location AS vehicleLocation, + drasi.listMax([drasi.changeDateTime(o), drasi.changeDateTime(v)]) AS readyTimestamp + """, +) + +# Delayed orders: a driver has been at the curbside for over 10 seconds while the +# order is still not ready. drasi.trueFor schedules a future re-evaluation and +# fires the instant the condition has held for the given duration. +DELAY_QUERY = Query( + id=DELAY, + key="orderId", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status <> 'ready' + AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) + RETURN + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS plate, + drasi.changeDateTime(v) AS waitingSinceTimestamp + """, +) + +# The engine registers these in order and subscribes one reaction to them all. +QUERIES = [ + ORDERS_PREPARING_QUERY, + ORDERS_READY_QUERY, + VEHICLES_PARKING_QUERY, + VEHICLES_CURBSIDE_QUERY, + DELIVERY_QUERY, + DELAY_QUERY, +] diff --git a/tutorials/curbside-pickup/images/curbside-ui.png b/tutorials/curbside-pickup/images/curbside-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..bd817df3e9c576d90953ef2219240abd06f94c03 GIT binary patch literal 128963 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 literal 0 HcmV?d00001 diff --git a/tutorials/curbside-pickup/requirements.txt b/tutorials/curbside-pickup/requirements.txt new file mode 100644 index 0000000..6a60bca --- /dev/null +++ b/tutorials/curbside-pickup/requirements.txt @@ -0,0 +1,12 @@ +# Curbside Pickup tutorial dependencies. +# +# pip install -r requirements.txt +# +# drasi-lib is the Python binding for Drasi's embeddable engine. It downloads the +# Postgres and MySQL source/bootstrap plugins from ghcr.io at runtime, so the +# first run needs network access. +drasi-lib>=0.2,<1 +streamlit>=1.32 +streamlit-autorefresh>=1.0 +psycopg[binary]>=3.1 +PyMySQL>=1.1 diff --git a/tutorials/curbside-pickup/scripts/cleanup.ps1 b/tutorials/curbside-pickup/scripts/cleanup.ps1 new file mode 100644 index 0000000..8c42f2e --- /dev/null +++ b/tutorials/curbside-pickup/scripts/cleanup.ps1 @@ -0,0 +1,48 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Cleanup Script (Windows) +# Stops the PostgreSQL and MySQL containers. The app runs in the foreground, so +# stop it first with Ctrl+C. Pass -RemoveVolumes to also delete the data volumes. + +param( + [switch]$RemoveVolumes +) + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$DatabaseDir = Join-Path $ScriptDir "..\database" + +Write-Host "=== Curbside Pickup - Cleanup ===" +Write-Host "" + +Push-Location $DatabaseDir +try { + if ($RemoveVolumes) { + Write-Host "Removing containers and volumes..." + docker compose down -v 2>&1 | Write-Host + } else { + Write-Host "Removing containers (keeping volumes)..." + docker compose down 2>&1 | Write-Host + } +} +finally { + Pop-Location +} + +Write-Host "" +Write-Host "=== Cleanup complete! ===" +Write-Host "" +Write-Host "Options:" +Write-Host " scripts/cleanup.ps1 # Stop containers, keep data volumes" +Write-Host " scripts/cleanup.ps1 -RemoveVolumes # Stop containers and remove data volumes" diff --git a/tutorials/curbside-pickup/scripts/cleanup.sh b/tutorials/curbside-pickup/scripts/cleanup.sh new file mode 100755 index 0000000..2c1e2e0 --- /dev/null +++ b/tutorials/curbside-pickup/scripts/cleanup.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Cleanup Script +# Removes the PostgreSQL and MySQL containers. The app runs in the foreground, so +# stop it first with Ctrl+C. Pass --volumes to also delete the database volumes. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DATABASE_DIR="$SCRIPT_DIR/../database" +REMOVE_VOLUMES="${1:-}" + +echo "=== Curbside Pickup - Cleanup ===" +echo + +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "Warning: docker-compose not found, skipping container cleanup" + exit 0 +fi + +echo "Stopping containers..." +cd "$DATABASE_DIR" + +if [ "$REMOVE_VOLUMES" = "--volumes" ] || [ "$REMOVE_VOLUMES" = "-v" ]; then + echo "Removing containers and volumes..." + $COMPOSE_CMD down -v +else + echo "Removing containers (keeping volumes)..." + $COMPOSE_CMD down +fi + +echo +echo "=== Cleanup complete! ===" +echo +echo "Options:" +echo " $0 # Stop containers, keep data volumes" +echo " $0 --volumes # Stop containers and remove data volumes" diff --git a/tutorials/curbside-pickup/scripts/setup-database.ps1 b/tutorials/curbside-pickup/scripts/setup-database.ps1 new file mode 100644 index 0000000..9d2917c --- /dev/null +++ b/tutorials/curbside-pickup/scripts/setup-database.ps1 @@ -0,0 +1,82 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Setup Database Script (Windows) +# Starts PostgreSQL (orders) and MySQL (vehicles) configured for CDC and seeds +# both databases. + +# We check $LASTEXITCODE explicitly rather than setting a terminating error +# preference, because the docker CLI writes progress to stderr. + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$DatabaseDir = Join-Path $ScriptDir "..\database" +$MysqlRootPassword = if ($env:MYSQL_ROOT_PASSWORD) { $env:MYSQL_ROOT_PASSWORD } else { "root_admin" } + +Write-Host "=== Curbside Pickup - Database Setup ===" +Write-Host "" + +if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { + Write-Host "Error: Docker is not installed or not in PATH" + exit 1 +} + +docker info 2>&1 | Out-Null +if ($LASTEXITCODE -ne 0) { + Write-Host "Error: Docker daemon is not running" + exit 1 +} + +Push-Location $DatabaseDir +try { + Write-Host "Stopping any existing containers..." + docker compose down -v 2>&1 | Out-Null + + Write-Host "Starting PostgreSQL and MySQL..." + docker compose up -d 2>&1 | Write-Host + if ($LASTEXITCODE -ne 0) { + Write-Host "Error: failed to start containers" + exit 1 + } + + Write-Host "Waiting for PostgreSQL to be ready..." + for ($i = 1; $i -le 30; $i++) { + docker exec curbside-pickup-postgres pg_isready -h localhost -U postgres -d RetailOperations 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { Write-Host "PostgreSQL is ready!"; break } + Write-Host " Waiting... ($i/30)"; Start-Sleep -Seconds 2 + } + + Write-Host "Waiting for MySQL to be ready..." + for ($i = 1; $i -le 45; $i++) { + docker exec curbside-pickup-mysql mysqladmin ping -h localhost -uroot -p"$MysqlRootPassword" 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { Write-Host "MySQL is ready!"; break } + Write-Host " Waiting... ($i/45)"; Start-Sleep -Seconds 2 + } + + Write-Host "" + Write-Host "Seeding PostgreSQL (orders)..." + Get-Content (Join-Path $DatabaseDir "postgres-init.sql") | ` + docker exec -i curbside-pickup-postgres psql -v ON_ERROR_STOP=1 -U postgres -d RetailOperations + if ($LASTEXITCODE -ne 0) { Write-Host "Error: failed to seed PostgreSQL"; exit 1 } + + Write-Host "Seeding MySQL (vehicles)..." + Get-Content (Join-Path $DatabaseDir "mysql-init.sql") | ` + docker exec -i curbside-pickup-mysql mysql -uroot -p"$MysqlRootPassword" + if ($LASTEXITCODE -ne 0) { Write-Host "Error: failed to seed MySQL"; exit 1 } + + Write-Host "" + Write-Host "=== Database setup complete! ===" +} +finally { + Pop-Location +} diff --git a/tutorials/curbside-pickup/scripts/setup-database.sh b/tutorials/curbside-pickup/scripts/setup-database.sh new file mode 100755 index 0000000..8e2361e --- /dev/null +++ b/tutorials/curbside-pickup/scripts/setup-database.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Setup Database Script +# Starts PostgreSQL (orders, logical replication) and MySQL (vehicles, ROW-based +# binlog + GTID) and seeds both, so Drasi can stream their changes via CDC. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DATABASE_DIR="$SCRIPT_DIR/../database" +MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-root_admin}" + +echo "=== Curbside Pickup - Database Setup ===" +echo + +if ! command -v docker &> /dev/null; then + echo "Error: Docker is not installed or not in PATH" + echo "Please install Docker: https://docs.docker.com/get-docker/" + exit 1 +fi + +if ! docker info &> /dev/null; then + echo "Error: Docker daemon is not running" + echo "Please start Docker and try again" + exit 1 +fi + +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "Error: docker-compose is not installed" + echo "Please install Docker Compose: https://docs.docker.com/compose/install/" + exit 1 +fi + +echo "Using: $COMPOSE_CMD" +echo + +echo "Stopping any existing containers..." +cd "$DATABASE_DIR" +$COMPOSE_CMD down -v 2>/dev/null || true + +echo "Starting PostgreSQL and MySQL..." +$COMPOSE_CMD up -d + +echo "Waiting for PostgreSQL to be ready..." +for i in $(seq 1 30); do + if docker exec curbside-pickup-postgres pg_isready -h localhost -U postgres -d RetailOperations &> /dev/null; then + echo "PostgreSQL is ready!" + break + fi + echo " Waiting... ($i/30)" + sleep 2 +done + +echo "Waiting for MySQL to be ready..." +for i in $(seq 1 45); do + if docker exec curbside-pickup-mysql mysqladmin ping -h localhost -uroot -p"$MYSQL_ROOT_PASSWORD" &> /dev/null; then + echo "MySQL is ready!" + break + fi + echo " Waiting... ($i/45)" + sleep 2 +done + +echo +echo "Seeding PostgreSQL (orders)..." +docker exec -i curbside-pickup-postgres \ + psql -v ON_ERROR_STOP=1 -U postgres -d RetailOperations < "$DATABASE_DIR/postgres-init.sql" + +echo "Seeding MySQL (vehicles)..." +docker exec -i curbside-pickup-mysql \ + mysql -uroot -p"$MYSQL_ROOT_PASSWORD" < "$DATABASE_DIR/mysql-init.sql" + +echo +echo "Seeded orders (PostgreSQL):" +docker exec curbside-pickup-postgres psql -U drasi_user -d RetailOperations -c \ + "SELECT id, customer_name, plate, status FROM orders ORDER BY id;" + +echo "Seeded vehicles (MySQL):" +docker exec curbside-pickup-mysql \ + mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -D PhysicalOperations -e \ + "SELECT plate, make, model, color, location FROM vehicles ORDER BY plate;" 2>/dev/null + +echo +echo "=== Database setup complete! ===" +echo " PostgreSQL: localhost:${POSTGRES_HOST_PORT:-5742} / RetailOperations" +echo " MySQL: localhost:${MYSQL_HOST_PORT:-3309} / PhysicalOperations" diff --git a/tutorials/curbside-pickup/scripts/start-demo.ps1 b/tutorials/curbside-pickup/scripts/start-demo.ps1 new file mode 100644 index 0000000..be661c3 --- /dev/null +++ b/tutorials/curbside-pickup/scripts/start-demo.ps1 @@ -0,0 +1,41 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Start Demo Script (Windows) +# Starts (and seeds) both databases, then launches the Streamlit app in the +# foreground. Open the URL it prints (http://localhost:8501 by default) and drive +# the demo from the panels. Press Ctrl+C to stop, then run scripts/cleanup.ps1. + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$TutorialDir = Join-Path $ScriptDir ".." + +& (Join-Path $ScriptDir "setup-database.ps1") +if ($LASTEXITCODE -ne 0) { + Write-Host "Database setup failed; not starting the app." + exit 1 +} + +Write-Host "" +Write-Host "Starting the Streamlit app (embeds the Drasi engine)..." +Write-Host "On first run it downloads the Drasi plugins from ghcr.io - give it a moment." +Write-Host "" + +$port = if ($env:STREAMLIT_PORT) { $env:STREAMLIT_PORT } else { "8501" } +Push-Location $TutorialDir +try { + streamlit run app.py --server.port $port +} +finally { + Pop-Location +} diff --git a/tutorials/curbside-pickup/scripts/start-demo.sh b/tutorials/curbside-pickup/scripts/start-demo.sh new file mode 100755 index 0000000..885f9df --- /dev/null +++ b/tutorials/curbside-pickup/scripts/start-demo.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Start Demo Script +# Starts (and seeds) both databases, then launches the Streamlit app in the +# foreground. The app embeds the Drasi engine, so this is the only process you +# need. Open the URL it prints (http://localhost:8501 by default) and drive the +# demo from the panels. Press Ctrl+C to stop, then run ./scripts/cleanup.sh. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TUTORIAL_DIR="$SCRIPT_DIR/.." + +bash "$SCRIPT_DIR/setup-database.sh" + +echo +echo "Starting the Streamlit app (embeds the Drasi engine)..." +echo "On first run it downloads the Drasi plugins from ghcr.io โ€” give it a moment." +echo + +cd "$TUTORIAL_DIR" +exec streamlit run app.py --server.port "${STREAMLIT_PORT:-8501}" diff --git a/website/config.toml b/website/config.toml index b9f21cf..b37ec4b 100644 --- a/website/config.toml +++ b/website/config.toml @@ -72,6 +72,11 @@ defaultContentLanguageInSubdir = false # Only the write-up and its images are docs; the app code is not. includeFiles = ["/_index.md", "/images/**"] + [[module.mounts]] + source = "../tutorials/curbside-pickup" + target = "content/docs/tutorials/curbside-pickup" + includeFiles = ["/_index.md", "/images/**"] + # UI customization [params] # Built-in (offline) lunr.js search โ€” no external service required. diff --git a/website/content/docs/tutorials/_index.md b/website/content/docs/tutorials/_index.md index ce1b4d5..3a6e9fc 100644 --- a/website/content/docs/tutorials/_index.md +++ b/website/content/docs/tutorials/_index.md @@ -24,4 +24,13 @@ clone it and run it locally. </div> </div> </a> + <a href="curbside-pickup/"> + <div class="unified-card unified-card--tutorials"> + <div class="unified-card-icon"><i class="fas fa-car"></i></div> + <div class="unified-card-content"> + <h3 class="unified-card-title">Curbside Pickup</h3> + <p class="unified-card-summary">Join a PostgreSQL orders store and a MySQL vehicles store by license plate โ€” including a temporal delay query โ€” in one integrated Streamlit UI driven by a Python reaction.</p> + </div> + </div> + </a> </div> From a782c0b1304e6754d20f072d339aabb3bd8b9872 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Thu, 30 Jul 2026 19:36:39 -0700 Subject: [PATCH 04/19] Fix Streamlit startup in a fresh container (headless mode) Without `--server.headless=true`, Streamlit stops at its interactive first-run email prompt on a fresh machine and never binds its port, so the tutorial app is unreachable (ERR_CONNECTION_REFUSED) in a dev container or Codespace. Add a `.streamlit/config.toml` (headless = true, address = 0.0.0.0) to each tutorial so any launch is non-interactive and reachable through a forwarded port, and pass the same flags explicitly in the start-demo scripts. Verified with a clean HOME that the app now starts with no prompt and serves on 8501. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../curbside-pickup/devcontainer-lock.json | 24 +++++++++++++++++++ .../building-comfort/.streamlit/config.toml | 8 +++++++ .../building-comfort/scripts/start-demo.ps1 | 2 +- .../building-comfort/scripts/start-demo.sh | 5 +++- .../curbside-pickup/.streamlit/config.toml | 8 +++++++ .../curbside-pickup/scripts/start-demo.ps1 | 2 +- .../curbside-pickup/scripts/start-demo.sh | 5 +++- 7 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 .devcontainer/curbside-pickup/devcontainer-lock.json create mode 100644 tutorials/building-comfort/.streamlit/config.toml create mode 100644 tutorials/curbside-pickup/.streamlit/config.toml diff --git a/.devcontainer/curbside-pickup/devcontainer-lock.json b/.devcontainer/curbside-pickup/devcontainer-lock.json new file mode 100644 index 0000000..895e5a2 --- /dev/null +++ b/.devcontainer/curbside-pickup/devcontainer-lock.json @@ -0,0 +1,24 @@ +{ + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.9", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a", + "integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.17.0", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c", + "integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.8", + "resolved": "ghcr.io/devcontainers/features/git@sha256:fd75977de13a9979000e0e78baf949adb0ca71d2398995fa22e0a36d7e7e7fe2", + "integrity": "sha256:fd75977de13a9979000e0e78baf949adb0ca71d2398995fa22e0a36d7e7e7fe2" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "1.8.0", + "resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511", + "integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511" + } + } +} diff --git a/tutorials/building-comfort/.streamlit/config.toml b/tutorials/building-comfort/.streamlit/config.toml new file mode 100644 index 0000000..b1b7854 --- /dev/null +++ b/tutorials/building-comfort/.streamlit/config.toml @@ -0,0 +1,8 @@ +# Streamlit settings for the tutorial. `headless = true` skips the interactive +# first-run email prompt (which would otherwise block startup and leave the port +# unbound), and `address = 0.0.0.0` makes the app reachable through a dev +# container's / Codespaces' forwarded port. +[server] +headless = true +address = "0.0.0.0" +port = 8501 diff --git a/tutorials/building-comfort/scripts/start-demo.ps1 b/tutorials/building-comfort/scripts/start-demo.ps1 index 036821f..0434fe9 100644 --- a/tutorials/building-comfort/scripts/start-demo.ps1 +++ b/tutorials/building-comfort/scripts/start-demo.ps1 @@ -35,7 +35,7 @@ Write-Host "" $port = if ($env:STREAMLIT_PORT) { $env:STREAMLIT_PORT } else { "8501" } Push-Location $TutorialDir try { - streamlit run app.py --server.port $port + streamlit run app.py --server.port $port --server.address=0.0.0.0 --server.headless=true } finally { Pop-Location diff --git a/tutorials/building-comfort/scripts/start-demo.sh b/tutorials/building-comfort/scripts/start-demo.sh index c678364..9c707b0 100755 --- a/tutorials/building-comfort/scripts/start-demo.sh +++ b/tutorials/building-comfort/scripts/start-demo.sh @@ -33,4 +33,7 @@ echo "On first run it downloads the Drasi plugins from ghcr.io โ€” give it a mom echo cd "$TUTORIAL_DIR" -exec streamlit run app.py --server.port "${STREAMLIT_PORT:-8501}" +exec streamlit run app.py \ + --server.port "${STREAMLIT_PORT:-8501}" \ + --server.address=0.0.0.0 \ + --server.headless=true diff --git a/tutorials/curbside-pickup/.streamlit/config.toml b/tutorials/curbside-pickup/.streamlit/config.toml new file mode 100644 index 0000000..b1b7854 --- /dev/null +++ b/tutorials/curbside-pickup/.streamlit/config.toml @@ -0,0 +1,8 @@ +# Streamlit settings for the tutorial. `headless = true` skips the interactive +# first-run email prompt (which would otherwise block startup and leave the port +# unbound), and `address = 0.0.0.0` makes the app reachable through a dev +# container's / Codespaces' forwarded port. +[server] +headless = true +address = "0.0.0.0" +port = 8501 diff --git a/tutorials/curbside-pickup/scripts/start-demo.ps1 b/tutorials/curbside-pickup/scripts/start-demo.ps1 index be661c3..febbaae 100644 --- a/tutorials/curbside-pickup/scripts/start-demo.ps1 +++ b/tutorials/curbside-pickup/scripts/start-demo.ps1 @@ -34,7 +34,7 @@ Write-Host "" $port = if ($env:STREAMLIT_PORT) { $env:STREAMLIT_PORT } else { "8501" } Push-Location $TutorialDir try { - streamlit run app.py --server.port $port + streamlit run app.py --server.port $port --server.address=0.0.0.0 --server.headless=true } finally { Pop-Location diff --git a/tutorials/curbside-pickup/scripts/start-demo.sh b/tutorials/curbside-pickup/scripts/start-demo.sh index 885f9df..4a579f1 100755 --- a/tutorials/curbside-pickup/scripts/start-demo.sh +++ b/tutorials/curbside-pickup/scripts/start-demo.sh @@ -32,4 +32,7 @@ echo "On first run it downloads the Drasi plugins from ghcr.io โ€” give it a mom echo cd "$TUTORIAL_DIR" -exec streamlit run app.py --server.port "${STREAMLIT_PORT:-8501}" +exec streamlit run app.py \ + --server.port "${STREAMLIT_PORT:-8501}" \ + --server.address=0.0.0.0 \ + --server.headless=true From 837124b6e8dbfb5df8b62f93125dc4f7975dae56 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 09:20:51 -0700 Subject: [PATCH 05/19] Give each Curbside panel its own bordered box Wrap the six panels in st.container(border=True) so each order/vehicle/matched/ delayed box is visually distinct, and refresh the screenshot. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/curbside-pickup/app.py | 14 ++++++-------- .../curbside-pickup/images/curbside-ui.png | Bin 128963 -> 171529 bytes 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tutorials/curbside-pickup/app.py b/tutorials/curbside-pickup/app.py index 2c5d9b5..4212958 100644 --- a/tutorials/curbside-pickup/app.py +++ b/tutorials/curbside-pickup/app.py @@ -168,21 +168,19 @@ def main() -> None: render_sidebar(engine, snapshot["activity"]) top = st.columns(4) - with top[0]: + with top[0], st.container(border=True): order_panel(results.get(ORDERS_PREPARING, []), ready=False, engine=engine) - with top[1]: + with top[1], st.container(border=True): order_panel(results.get(ORDERS_READY, []), ready=True, engine=engine) - with top[2]: + with top[2], st.container(border=True): vehicle_panel(results.get(VEHICLES_PARKING, []), curbside=False, engine=engine) - with top[3]: + with top[3], st.container(border=True): vehicle_panel(results.get(VEHICLES_CURBSIDE, []), curbside=True, engine=engine) - st.divider() - bottom = st.columns(2) - with bottom[0]: + with bottom[0], st.container(border=True): matched_panel(results.get(DELIVERY, [])) - with bottom[1]: + with bottom[1], st.container(border=True): delayed_panel(results.get(DELAY, [])) diff --git a/tutorials/curbside-pickup/images/curbside-ui.png b/tutorials/curbside-pickup/images/curbside-ui.png index bd817df3e9c576d90953ef2219240abd06f94c03..d3062968f1f7fc62f414cadff33cd38f787c8d6c 100644 GIT binary patch literal 171529 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⁡-Y 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 cgOEIS3#M$Q!S!%L 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©^_s=o!u2WW5iuGY}@XX#=ME;FrNhW;kzF?Z>!f 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 literal 128963 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 From 82efaa926bb08e4a72414fe814496511d4f7d898 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 09:31:38 -0700 Subject: [PATCH 06/19] Highlight Cypher code blocks in the docs Chroma has no Cypher lexer, so ```cypher fences rendered unhighlighted (language-fallback) on the site. Add a code-block render hook that applies a lightweight, Cypher-aware highlight using the same Chroma CSS classes the theme already styles (keywords, functions incl. Drasi's drasi.* extensions, labels, numbers, comments). The generated GitHub READMEs keep their ```cypher fences. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../_markup/render-codeblock-cypher.html | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 website/layouts/_default/_markup/render-codeblock-cypher.html diff --git a/website/layouts/_default/_markup/render-codeblock-cypher.html b/website/layouts/_default/_markup/render-codeblock-cypher.html new file mode 100644 index 0000000..f217fb1 --- /dev/null +++ b/website/layouts/_default/_markup/render-codeblock-cypher.html @@ -0,0 +1,30 @@ +{{- /* + Code-block render hook for Cypher. + + Chroma (the highlighter Hugo bundles) has no Cypher lexer, so ```cypher blocks + would otherwise render unhighlighted on the doc site. This hook applies a + lightweight, Cypher-aware highlight using the same Chroma CSS classes + (k, nf, nl, m, c1) the rest of the site already styles. The GitHub READMEs are + generated separately and keep their ```cypher fences, where GitHub's own + Linguist grammar highlights them. +*/ -}} +{{- $h := trim .Inner "\n" | htmlEscape -}} +{{- /* htmlEscape turns ' into the entity '; its digits would otherwise be + caught by the number rule below (producing &#<span...>39</span>; which + browsers can't decode, showing a literal '). Park it on a placeholder + and restore it after all the highlighting regexes have run. */ -}} +{{- $sq := "\ue000" -}} +{{- $h = replace $h "'" $sq -}} +{{- /* line comments */ -}} +{{- $h = $h | replaceRE `(//[^\n]*)` `<span class="c1">$1</span>` -}} +{{- /* keywords */ -}} +{{- $h = $h | replaceRE `(?i)\b(MATCH|OPTIONAL|WITH|WHERE|RETURN|MERGE|CREATE|DELETE|SET|REMOVE|UNWIND|ORDER|BY|SKIP|LIMIT|AS|CASE|WHEN|THEN|ELSE|END|AND|OR|XOR|NOT|IN|STARTS|ENDS|CONTAINS|IS|NULL|TRUE|FALSE|DISTINCT)\b` `<span class="k">$1</span>` -}} +{{- /* functions called as name( โ€” includes Drasi's Cypher extension functions */ -}} +{{- $h = $h | replaceRE `\b(avg|count|sum|min|max|collect|floor|ceil|round|abs|coalesce|toInteger|toFloat|size|length|duration|trueFor|changeDateTime|getVersionByTimestamp|listMax|listMin)(\s*\()` `<span class="nf">$1</span>$2` -}} +{{- /* node / relationship labels after a colon */ -}} +{{- $h = $h | replaceRE `:([A-Za-z_][A-Za-z0-9_]*)` `:<span class="nl">$1</span>` -}} +{{- /* numbers */ -}} +{{- $h = $h | replaceRE `\b(\d+(?:\.\d+)?)\b` `<span class="m">$1</span>` -}} +{{- /* restore the single-quote entity */ -}} +{{- $h = replace $h $sq "'" -}} +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cypher" data-lang="cypher">{{ $h | safeHTML }}</code></pre></div> From 299cf01a9de654ea9c99eb3b437def67a272ab53 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 13:11:01 -0700 Subject: [PATCH 07/19] Add Getting Started tutorial (Python console app) Port the Drasi Server Getting Started tutorial to drasi-lib as a no-UI console app. Drasi's PostgreSQL source watches a `message` table and three continuous queries build up in complexity -- a Hello World filter, a count aggregation, and a temporal `inactive-people` (drasi.trueLater + datetime.realtime). Instead of the log + SSE reactions, a single Python reaction prints each query change to the console; you drive changes by running SQL directly (add/delete-message scripts and raw psql), in the same two-terminal model as the other tutorials. - tutorials/getting-started/: app.py + demo/ (queries, config), database assets, setup/start/add-message/delete-message/cleanup scripts, requirements (drasi-lib). - Authored in _index.md (mounted into the website) with a real console transcript; README generated. Website mount + tutorials index card (weight 10) + tutorials/README row + dev container. - Extend the Cypher render hook to also highlight drasi.trueLater / realtime. Verified end-to-end against a real PostgreSQL: the filter and aggregation react to inserts/deletes, and inactive-people fires ~20s after a sender's last message. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../getting-started/devcontainer.json | 54 ++++ .devcontainer/getting-started/post-create.sh | 23 ++ tutorials/README.md | 1 + tutorials/getting-started/.env.example | 16 + tutorials/getting-started/.gitignore | 11 + tutorials/getting-started/README.md | 273 ++++++++++++++++ tutorials/getting-started/_index.md | 300 ++++++++++++++++++ tutorials/getting-started/app.py | 119 +++++++ .../database/docker-compose.yml | 44 +++ tutorials/getting-started/database/init.sql | 92 ++++++ tutorials/getting-started/demo/__init__.py | 15 + tutorials/getting-started/demo/config.py | 56 ++++ tutorials/getting-started/demo/queries.py | 85 +++++ tutorials/getting-started/requirements.txt | 8 + .../getting-started/scripts/add-message.ps1 | 34 ++ .../getting-started/scripts/add-message.sh | 51 +++ tutorials/getting-started/scripts/cleanup.ps1 | 48 +++ tutorials/getting-started/scripts/cleanup.sh | 54 ++++ .../scripts/delete-message.ps1 | 29 ++ .../getting-started/scripts/delete-message.sh | 48 +++ .../scripts/setup-database.ps1 | 71 +++++ .../getting-started/scripts/setup-database.sh | 82 +++++ .../getting-started/scripts/start-demo.ps1 | 41 +++ .../getting-started/scripts/start-demo.sh | 36 +++ website/config.toml | 5 + website/content/docs/tutorials/_index.md | 9 + .../_markup/render-codeblock-cypher.html | 2 +- 27 files changed, 1606 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/getting-started/devcontainer.json create mode 100755 .devcontainer/getting-started/post-create.sh create mode 100644 tutorials/getting-started/.env.example create mode 100644 tutorials/getting-started/.gitignore create mode 100644 tutorials/getting-started/README.md create mode 100644 tutorials/getting-started/_index.md create mode 100644 tutorials/getting-started/app.py create mode 100644 tutorials/getting-started/database/docker-compose.yml create mode 100644 tutorials/getting-started/database/init.sql create mode 100644 tutorials/getting-started/demo/__init__.py create mode 100644 tutorials/getting-started/demo/config.py create mode 100644 tutorials/getting-started/demo/queries.py create mode 100644 tutorials/getting-started/requirements.txt create mode 100644 tutorials/getting-started/scripts/add-message.ps1 create mode 100755 tutorials/getting-started/scripts/add-message.sh create mode 100644 tutorials/getting-started/scripts/cleanup.ps1 create mode 100755 tutorials/getting-started/scripts/cleanup.sh create mode 100644 tutorials/getting-started/scripts/delete-message.ps1 create mode 100755 tutorials/getting-started/scripts/delete-message.sh create mode 100644 tutorials/getting-started/scripts/setup-database.ps1 create mode 100755 tutorials/getting-started/scripts/setup-database.sh create mode 100644 tutorials/getting-started/scripts/start-demo.ps1 create mode 100755 tutorials/getting-started/scripts/start-demo.sh diff --git a/.devcontainer/getting-started/devcontainer.json b/.devcontainer/getting-started/devcontainer.json new file mode 100644 index 0000000..801b278 --- /dev/null +++ b/.devcontainer/getting-started/devcontainer.json @@ -0,0 +1,54 @@ +{ + "name": "Getting Started Tutorial (Python)", + "image": "mcr.microsoft.com/devcontainers/base:bookworm@sha256:9e35ebf156350ba25b117da6869907034b6e2437b1891e3a43395af0b7d3eda4", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "29", + "moby": false + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12", + "installTools": true + }, + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "configureZshAsDefaultShell": true + } + }, + "postCreateCommand": "bash ../../.devcontainer/getting-started/post-create.sh", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/tutorials/getting-started", + "runArgs": [ + "--privileged", + "--init", + "--ipc=host" + ], + "forwardPorts": [ + 5752 + ], + "portsAttributes": { + "5752": { + "label": "PostgreSQL", + "onAutoForward": "silent" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "editor.formatOnSave": true + } + } + }, + "remoteUser": "vscode", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "containerEnv": { + "POSTGRES_HOST": "localhost", + "POSTGRES_PORT": "5752", + "POSTGRES_HOST_PORT": "5752" + } +} diff --git a/.devcontainer/getting-started/post-create.sh b/.devcontainer/getting-started/post-create.sh new file mode 100755 index 0000000..701dc00 --- /dev/null +++ b/.devcontainer/getting-started/post-create.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Post-create script for the Getting Started tutorial (Python). + +set -e + +echo "๐Ÿ”ง Initializing the Getting Started (Python) tutorial environment..." + +# Resolve the tutorial directory from this script's location so the script works +# regardless of the current working directory. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +TUTORIAL_DIR="$REPO_ROOT/tutorials/getting-started" + +# Install the tutorial's Python dependency (drasi-lib). The devcontainer's Python +# feature provides a pip that is not externally managed, so a plain install works. +echo "๐Ÿ Installing Python dependencies..." +cd "$TUTORIAL_DIR" +python -m pip install --upgrade pip +python -m pip install -r requirements.txt + +echo "" +echo "โœ… Getting Started (Python) tutorial environment is ready!" +echo " Next: run 'bash scripts/start-demo.sh' (you are already in tutorials/getting-started)" diff --git a/tutorials/README.md b/tutorials/README.md index 47d38b9..2b917a3 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -6,6 +6,7 @@ self-contained project you can clone and run. | Tutorial | What you learn | How to run it | | --- | --- | --- | +| [getting-started](getting-started) | The change-driven basics โ€” PostgreSQL CDC and three continuous queries (a filter, a `count` aggregation, and a temporal `inactive-people`), printed live by a **Python reaction** in a no-UI console app you drive with SQL | Open the **Getting Started Tutorial (Python)** dev container (or a Codespace) and follow [tutorials/getting-started](getting-started) | | [building-comfort](building-comfort) | Smart-building comfort monitoring โ€” PostgreSQL CDC, six continuous queries with synthetic joins and aggregation, and a **Python reaction that drives a Streamlit UI** (with simulation, reset and per-room controls) | Open the **Building Comfort Tutorial (Python)** dev container (or a Codespace) and follow [tutorials/building-comfort](building-comfort) | | [curbside-pickup](curbside-pickup) | Cross-database joins โ€” a PostgreSQL `orders` store and a MySQL `vehicles` store joined by license plate, six continuous queries (incl. a temporal `drasi.trueFor` delay), rendered and driven from **one integrated Streamlit UI** powered by a Python reaction | Open the **Curbside Pickup Tutorial (Python)** dev container (or a Codespace) and follow [tutorials/curbside-pickup](curbside-pickup) | diff --git a/tutorials/getting-started/.env.example b/tutorials/getting-started/.env.example new file mode 100644 index 0000000..db51a0d --- /dev/null +++ b/tutorials/getting-started/.env.example @@ -0,0 +1,16 @@ +# Example environment variables for the getting-started tutorial. +# +# Copy this file to .env and adjust as needed: +# cp .env.example .env +# +# The scripts and the app read these with the same defaults shown here. +# +# IMPORTANT: Never commit .env with real credentials! + +# PostgreSQL connection settings. +POSTGRES_HOST=localhost +POSTGRES_PORT=5752 +POSTGRES_HOST_PORT=5752 +POSTGRES_DATABASE=getting_started +POSTGRES_USER=drasi_user +POSTGRES_PASSWORD=drasi_password diff --git a/tutorials/getting-started/.gitignore b/tutorials/getting-started/.gitignore new file mode 100644 index 0000000..1e58996 --- /dev/null +++ b/tutorials/getting-started/.gitignore @@ -0,0 +1,11 @@ +# Local environment overrides +.env + +# Python +__pycache__/ +*.pyc +.venv/ + +# Drasi engine state / logs +data/ +*.log diff --git a/tutorials/getting-started/README.md b/tutorials/getting-started/README.md new file mode 100644 index 0000000..20786b0 --- /dev/null +++ b/tutorials/getting-started/README.md @@ -0,0 +1,273 @@ +<!-- DO NOT EDIT. Generated from _index.md by scripts/render-tutorials.py. Edit _index.md and run `python3 scripts/render-tutorials.py`. --> + +Imagine you want to react the instant data changes โ€” a new row in a database, a value crossing a threshold, or something that *should* have changed but didn't. Drasi lets you express these as **continuous queries** that stay constantly up to date, with no polling. + +This is the getting-started tutorial for **`drasi-lib`**, the Python binding that embeds Drasi's engine directly in your app. You'll connect Drasi to a live PostgreSQL table of messages โ€” imagine a simple live message feed โ€” and build three continuous queries that **filter** changes, **aggregate** them, and detect the **absence** of change. A single **Python reaction** prints each query's changes to the console, so there's no UI to build: you run one small console app, drive changes with plain SQL, and watch the queries react in your terminal. + +**Sources** โ†’ **Continuous Queries** โ†’ **Reactions** + +- **Sources** โ€” Connect to your data sources +- **Continuous Queries** โ€” Define what changes matter +- **Reactions** โ€” Take action automatically + +| Step | What You'll Do | +| ---- | ------------- | +| **[Step 1: Set Up Your Environment](#step-1-of-4-set-up-your-environment)** | Open the dev container (or install Python + Docker locally) | +| **[Step 2: Run the Demo](#step-2-of-4-run-the-demo)** | One command starts PostgreSQL and the console app | +| **[Step 3: Read the Console](#step-3-of-4-read-the-console)** | See the three queries' initial results | +| **[Step 4: Drive Change](#step-4-of-4-drive-change)** | Add and delete messages with SQL, and watch the queries react | +| **[How It Works](#how-it-works)** | Understand the source, the three queries, and the Python reaction | + +> **Before you begin** +> +> - **Two terminals:** **Terminal 1** runs the console app (it stays in the foreground and prints changes). Use **Terminal 2** for the helper scripts that add and delete messages. +> - **Working directory:** run every command from the tutorial directory (`tutorials/getting-started/`). The dev container opens there automatically; if you're running locally, `cd tutorials/getting-started` first. +> - **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*). Use the one for your shell. +> - **Ports:** PostgreSQL is published on `5752`. There is no web UI. + +## Step 1 of 4: Set Up Your Environment +This tutorial needs **Python 3.10+** and **Docker** (for PostgreSQL). The easiest way to get everything is the **dev container**. + +### Option A: Dev Container or GitHub Codespaces (recommended) + +1. Open the [`drasi-python`](https://github.com/drasi-project/drasi-python) repository in VS Code and run **Reopen in Container** (or create a **Codespace** from the repo's **Code** menu). +2. When prompted for a configuration, choose **Getting Started Tutorial (Python)**. +3. Wait for the container to finish. Its setup script installs the tutorial's Python dependency (`drasi-lib`) and the Docker tooling for PostgreSQL. + +That's it. Skip ahead to [Step 2](#step-2-of-4-run-the-demo). + +### Option B: Run Locally + +You'll need **Python 3.10+**, **Docker**, and **bash** for the helper scripts. From the repository root, move into the tutorial directory and install the dependency: + +**bash / zsh** + +```bash +cd tutorials/getting-started +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +``` + +**PowerShell** + +```powershell +cd tutorials/getting-started +python -m venv .venv; .venv\Scripts\Activate.ps1 +pip install -r requirements.txt +``` + +`drasi-lib` ships as a prebuilt wheel, so no Rust toolchain is needed. It downloads the Postgres plugins it needs from `ghcr.io/drasi-project` the first time the app runs. + +## Step 2 of 4: Run the Demo +In **Terminal 1**, start the demo: + +**bash / zsh** + +```bash +bash scripts/start-demo.sh +``` + +**PowerShell** + +```powershell +powershell -ExecutionPolicy Bypass -File scripts/start-demo.ps1 +``` + +The `start-demo` script starts PostgreSQL (seeding three messages, one of them exactly `Hello World`) and then runs the console app, which embeds the Drasi engine. On first start it downloads the plugins it needs (`source/postgres`, `bootstrap/postgres`) from `ghcr.io/drasi-project`, connects to the database, bootstraps the existing rows, and starts the three queries and the reaction. + +Leave this running. Everything else happens in **Terminal 2**. + +> **Stopping and resetting** +> +> Press **Ctrl+C** in Terminal 1 to stop the app. To remove the database container when you're completely done, run `bash scripts/cleanup.sh` (bash) or `powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1` (PowerShell). Add `--volumes` (bash) or `-RemoveVolumes` (PowerShell) to also delete the data. + +## Step 3 of 4: Read the Console +Once it's ready, the app prints each query's initial results and then waits for changes: + +```text +=== Initial results === + +[hello-world-from] 1 row(s): + MessageFrom='Brian Kernighan' MessageId=2 + +[message-count] 3 row(s): + Frequency=1 Message='Hello World' + Frequency=1 Message='To infinity and beyond!' + Frequency=1 Message='The Analytical Engine weaves algebraic patterns.' + +[inactive-people] 0 row(s): + +=== Watching for changes (Ctrl+C to stop) === +``` + +`hello-world-from` already found the seeded `Hello World`; `message-count` shows each message once; `inactive-people` is empty. Because the seed messages then sit untouched, after about 20 seconds each sender crosses the inactivity threshold and the temporal query fires on its own: + +```text +[13:06:09] [inactive-people] + LastMessageTimestamp='...T20:05:49+00:00' MessageFrom='Buzz Lightyear' +[13:06:09] [inactive-people] + LastMessageTimestamp='...T20:05:49+00:00' MessageFrom='Ada Lovelace' +[13:06:09] [inactive-people] + LastMessageTimestamp='...T20:05:49+00:00' MessageFrom='Brian Kernighan' +``` + +## Step 4 of 4: Drive Change +With Terminal 1 running the app, use **Terminal 2** to change the data and watch it react. + +> **No middle tier โ€” the scripts just write to the database** +> +> The helper scripts run a plain SQL `INSERT` or `DELETE` against PostgreSQL โ€” exactly what an existing app would already do. There's no API to call and no event to publish. Drasi observes the row change through logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which prints the change. The **PowerShell** tabs make this obvious โ€” they're the raw SQL statements. + +### Add a message + +Send a new `Hello World`, from someone new: + +**bash / zsh** + +```bash +bash scripts/add-message.sh "Grace Hopper" "Hello World" +``` + +**PowerShell** + +```powershell +docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "INSERT INTO message (""from"", message) VALUES ('Grace Hopper', 'Hello World');" +``` + +Terminal 1 reacts immediately โ€” the filter picks up the new sender and the aggregate ticks up: + +```text +[13:06:26] [hello-world-from] + MessageFrom='Grace Hopper' MessageId=4 +[13:06:26] [message-count] ~ Frequency=1 Message='Hello World' -> Frequency=2 Message='Hello World' +``` + +Try a message that *isn't* `Hello World` (say `bash scripts/add-message.sh "Bob" "Goodbye World"`) and only `message-count` changes โ€” the filter query ignores it. + +### Delete a message + +Remove a message by its id (the add script prints the `messageid`; message `2` is the seeded `Hello World`): + +**bash / zsh** + +```bash +bash scripts/delete-message.sh 2 +``` + +**PowerShell** + +```powershell +docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "DELETE FROM message WHERE messageid=2;" +``` + +`hello-world-from` drops that row and `message-count` decrements โ€” a row leaving a result set is a change Drasi reports too. + +## How It Works +The whole demo is a small Python program in `tutorials/getting-started/`: `app.py` runs the engine and prints, and `demo/` holds the queries and configuration. + +### The Source + +The app installs the Postgres plugins and adds the source, pointing it at the tutorial database: + +```python +await drasi.install_plugin("source/postgres") +await drasi.install_plugin("bootstrap/postgres") +await drasi.start() + +await drasi.add_source( + "postgres", + "messages", + { + **POSTGRES, # host, port, user, password, database + "sslMode": "prefer", + "tables": ["public.message"], + "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], + "slotName": "drasi_getting_started_slot", + "publicationName": "drasi_getting_started_pub", + }, + bootstrap={"kind": "postgres", **POSTGRES}, +) +``` + +The source uses **logical replication (CDC)** to stream changes from the `message` table; the `bootstrap` provider loads the rows that already exist when the query starts. The table name is lower-case so the node label Drasi sees matches the queries: `(m:message)`. + +### The Three Queries + +They build up in complexity. + +**Filter** โ€” `hello-world-from` returns only messages whose text is exactly `Hello World`, with who sent them: + +```cypher +MATCH (m:message) +WHERE m.message = 'Hello World' +RETURN m.messageid AS MessageId, m.from AS MessageFrom +``` + +**Aggregation** โ€” `message-count` groups by message text and counts. Like SQL's `GROUP BY`, the non-aggregated `Message` travels alongside `count(m)`, and Drasi maintains the counts **incrementally** โ€” one insert bumps exactly one row: + +```cypher +MATCH (m:message) +RETURN m.message AS Message, count(m) AS Frequency +``` + +**Absence of change** โ€” `inactive-people` finds senders who have gone quiet. It takes each sender's most recent message time and flags them once it is more than 20 seconds ago. `drasi.trueLater` **schedules a future re-evaluation** so a sender appears the instant they cross the threshold, not only when some other change happens: + +```cypher +MATCH (m:message) +WITH m.from AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp +WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) +RETURN MessageFrom, LastMessageTimestamp +``` + +`drasi.changeDateTime(m)` exposes the wall-clock time of each change and `datetime.realtime()` is the current time, so this needs no extra timestamp bookkeeping in your app. + +### The Python Reaction + +Instead of a dashboard or an SSE stream, a single **Python reaction** subscribed to all three queries prints each change to the console: + +```python +def on_change(event): + query_id = event["query_id"] + for diff in event["results"]: + if diff["type"] == "ADD": + print(f"[{query_id}] + {diff['data']}") + elif diff["type"] == "DELETE": + print(f"[{query_id}] - {diff['data']}") + else: # UPDATE + print(f"[{query_id}] ~ {diff['before']} -> {diff['after']}") + + +await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) +``` + +`type` tells you which of `data`, or `before` and `after`, a diff carries. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. + +## Clean Up +When you're finished, stop the app with **Ctrl+C** in Terminal 1, then remove the database container: + +**bash / zsh** + +```bash +# Stop the container, keep data +bash scripts/cleanup.sh + +# Stop the container and delete the data volume +bash scripts/cleanup.sh --volumes +``` + +**PowerShell** + +```powershell +# Stop the container, keep data +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 + +# Stop the container and delete the data volume +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 -RemoveVolumes +``` + +## What You Learned +- **Sources** connect Drasi to live data โ€” here, PostgreSQL via Change Data Capture, in a few lines of Python. +- **Continuous Queries** let you **filter**, **aggregate**, and even detect the **absence** of change (with the temporal `drasi.trueLater`) โ€” all kept current automatically, with no polling. +- A **Python reaction** turned every query change into console output; the same callback could call an API, write a file, or push a notification instead. + +From here, try the [Building Comfort](../building-comfort/) and [Curbside Pickup](../curbside-pickup/) tutorials, which add synthetic joins, cross-database joins, and a live Streamlit UI. diff --git a/tutorials/getting-started/_index.md b/tutorials/getting-started/_index.md new file mode 100644 index 0000000..5403a3f --- /dev/null +++ b/tutorials/getting-started/_index.md @@ -0,0 +1,300 @@ +--- +type: "docs" +title: "Getting Started" +linkTitle: "Getting Started" +weight: 10 +description: > + Connect Drasi to a live PostgreSQL database and build three continuous queries, step by step, in a small Python console app. +--- + +Imagine you want to react the instant data changes โ€” a new row in a database, a value crossing a threshold, or something that *should* have changed but didn't. Drasi lets you express these as **continuous queries** that stay constantly up to date, with no polling. + +This is the getting-started tutorial for **`drasi-lib`**, the Python binding that embeds Drasi's engine directly in your app. You'll connect Drasi to a live PostgreSQL table of messages โ€” imagine a simple live message feed โ€” and build three continuous queries that **filter** changes, **aggregate** them, and detect the **absence** of change. A single **Python reaction** prints each query's changes to the console, so there's no UI to build: you run one small console app, drive changes with plain SQL, and watch the queries react in your terminal. + +<div class="flow-diagram"> + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-database"></i> + </div> + <div class="flow-step__label">Sources</div> + <div class="flow-step__description">Connect to your data sources</div> + </div> + + <div class="flow-arrow"> + <i class="fas fa-arrow-right"></i> + </div> + + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-filter"></i> + </div> + <div class="flow-step__label">Continuous Queries</div> + <div class="flow-step__description">Define what changes matter</div> + </div> + + <div class="flow-arrow"> + <i class="fas fa-arrow-right"></i> + </div> + + <div class="flow-step"> + <div class="flow-step__icon"> + <i class="fas fa-bolt"></i> + </div> + <div class="flow-step__label">Reactions</div> + <div class="flow-step__description">Take action automatically</div> + </div> +</div> + +| Step | What You'll Do | +| ---- | ------------- | +| **[Step 1: Set Up Your Environment](#setup)** | Open the dev container (or install Python + Docker locally) | +| **[Step 2: Run the Demo](#run)** | One command starts PostgreSQL and the console app | +| **[Step 3: Read the Console](#console)** | See the three queries' initial results | +| **[Step 4: Drive Change](#drive)** | Add and delete messages with SQL, and watch the queries react | +| **[How It Works](#how)** | Understand the source, the three queries, and the Python reaction | + +{{% alert title="Before you begin" color="info" %}} +- **Two terminals:** **Terminal 1** runs the console app (it stays in the foreground and prints changes). Use **Terminal 2** for the helper scripts that add and delete messages. +- **Working directory:** run every command from the tutorial directory (`tutorials/getting-started/`). The dev container opens there automatically; if you're running locally, `cd tutorials/getting-started` first. +- **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*). Use the one for your shell. +- **Ports:** PostgreSQL is published on `5752`. There is no web UI. +{{% /alert %}} + +## Step 1 of 4: Set Up Your Environment {#setup} + +This tutorial needs **Python 3.10+** and **Docker** (for PostgreSQL). The easiest way to get everything is the **dev container**. + +### Option A: Dev Container or GitHub Codespaces (recommended) + +1. Open the [`drasi-python`](https://github.com/drasi-project/drasi-python) repository in VS Code and run **Reopen in Container** (or create a **Codespace** from the repo's **Code** menu). +2. When prompted for a configuration, choose **Getting Started Tutorial (Python)**. +3. Wait for the container to finish. Its setup script installs the tutorial's Python dependency (`drasi-lib`) and the Docker tooling for PostgreSQL. + +That's it. Skip ahead to [Step 2](#run). + +### Option B: Run Locally + +You'll need **Python 3.10+**, **Docker**, and **bash** for the helper scripts. From the repository root, move into the tutorial directory and install the dependency: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +cd tutorials/getting-started +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +cd tutorials/getting-started +python -m venv .venv; .venv\Scripts\Activate.ps1 +pip install -r requirements.txt +{{< /tab >}} +{{< /tabpane >}} + +`drasi-lib` ships as a prebuilt wheel, so no Rust toolchain is needed. It downloads the Postgres plugins it needs from `ghcr.io/drasi-project` the first time the app runs. + +## Step 2 of 4: Run the Demo {#run} + +In **Terminal 1**, start the demo: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +bash scripts/start-demo.sh +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +powershell -ExecutionPolicy Bypass -File scripts/start-demo.ps1 +{{< /tab >}} +{{< /tabpane >}} + +The `start-demo` script starts PostgreSQL (seeding three messages, one of them exactly `Hello World`) and then runs the console app, which embeds the Drasi engine. On first start it downloads the plugins it needs (`source/postgres`, `bootstrap/postgres`) from `ghcr.io/drasi-project`, connects to the database, bootstraps the existing rows, and starts the three queries and the reaction. + +Leave this running. Everything else happens in **Terminal 2**. + +{{% alert title="Stopping and resetting" color="info" %}} +Press **Ctrl+C** in Terminal 1 to stop the app. To remove the database container when you're completely done, run `bash scripts/cleanup.sh` (bash) or `powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1` (PowerShell). Add `--volumes` (bash) or `-RemoveVolumes` (PowerShell) to also delete the data. +{{% /alert %}} + +## Step 3 of 4: Read the Console {#console} + +Once it's ready, the app prints each query's initial results and then waits for changes: + +```text +=== Initial results === + +[hello-world-from] 1 row(s): + MessageFrom='Brian Kernighan' MessageId=2 + +[message-count] 3 row(s): + Frequency=1 Message='Hello World' + Frequency=1 Message='To infinity and beyond!' + Frequency=1 Message='The Analytical Engine weaves algebraic patterns.' + +[inactive-people] 0 row(s): + +=== Watching for changes (Ctrl+C to stop) === +``` + +`hello-world-from` already found the seeded `Hello World`; `message-count` shows each message once; `inactive-people` is empty. Because the seed messages then sit untouched, after about 20 seconds each sender crosses the inactivity threshold and the temporal query fires on its own: + +```text +[13:06:09] [inactive-people] + LastMessageTimestamp='...T20:05:49+00:00' MessageFrom='Buzz Lightyear' +[13:06:09] [inactive-people] + LastMessageTimestamp='...T20:05:49+00:00' MessageFrom='Ada Lovelace' +[13:06:09] [inactive-people] + LastMessageTimestamp='...T20:05:49+00:00' MessageFrom='Brian Kernighan' +``` + +## Step 4 of 4: Drive Change {#drive} + +With Terminal 1 running the app, use **Terminal 2** to change the data and watch it react. + +{{% alert title="No middle tier โ€” the scripts just write to the database" color="info" %}} +The helper scripts run a plain SQL `INSERT` or `DELETE` against PostgreSQL โ€” exactly what an existing app would already do. There's no API to call and no event to publish. Drasi observes the row change through logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which prints the change. The **PowerShell** tabs make this obvious โ€” they're the raw SQL statements. +{{% /alert %}} + +### Add a message + +Send a new `Hello World`, from someone new: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +bash scripts/add-message.sh "Grace Hopper" "Hello World" +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "INSERT INTO message (""from"", message) VALUES ('Grace Hopper', 'Hello World');" +{{< /tab >}} +{{< /tabpane >}} + +Terminal 1 reacts immediately โ€” the filter picks up the new sender and the aggregate ticks up: + +```text +[13:06:26] [hello-world-from] + MessageFrom='Grace Hopper' MessageId=4 +[13:06:26] [message-count] ~ Frequency=1 Message='Hello World' -> Frequency=2 Message='Hello World' +``` + +Try a message that *isn't* `Hello World` (say `bash scripts/add-message.sh "Bob" "Goodbye World"`) and only `message-count` changes โ€” the filter query ignores it. + +### Delete a message + +Remove a message by its id (the add script prints the `messageid`; message `2` is the seeded `Hello World`): + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +bash scripts/delete-message.sh 2 +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "DELETE FROM message WHERE messageid=2;" +{{< /tab >}} +{{< /tabpane >}} + +`hello-world-from` drops that row and `message-count` decrements โ€” a row leaving a result set is a change Drasi reports too. + +## How It Works {#how} + +The whole demo is a small Python program in `tutorials/getting-started/`: `app.py` runs the engine and prints, and `demo/` holds the queries and configuration. + +### The Source + +The app installs the Postgres plugins and adds the source, pointing it at the tutorial database: + +```python +await drasi.install_plugin("source/postgres") +await drasi.install_plugin("bootstrap/postgres") +await drasi.start() + +await drasi.add_source( + "postgres", + "messages", + { + **POSTGRES, # host, port, user, password, database + "sslMode": "prefer", + "tables": ["public.message"], + "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], + "slotName": "drasi_getting_started_slot", + "publicationName": "drasi_getting_started_pub", + }, + bootstrap={"kind": "postgres", **POSTGRES}, +) +``` + +The source uses **logical replication (CDC)** to stream changes from the `message` table; the `bootstrap` provider loads the rows that already exist when the query starts. The table name is lower-case so the node label Drasi sees matches the queries: `(m:message)`. + +### The Three Queries + +They build up in complexity. + +**Filter** โ€” `hello-world-from` returns only messages whose text is exactly `Hello World`, with who sent them: + +```cypher +MATCH (m:message) +WHERE m.message = 'Hello World' +RETURN m.messageid AS MessageId, m.from AS MessageFrom +``` + +**Aggregation** โ€” `message-count` groups by message text and counts. Like SQL's `GROUP BY`, the non-aggregated `Message` travels alongside `count(m)`, and Drasi maintains the counts **incrementally** โ€” one insert bumps exactly one row: + +```cypher +MATCH (m:message) +RETURN m.message AS Message, count(m) AS Frequency +``` + +**Absence of change** โ€” `inactive-people` finds senders who have gone quiet. It takes each sender's most recent message time and flags them once it is more than 20 seconds ago. `drasi.trueLater` **schedules a future re-evaluation** so a sender appears the instant they cross the threshold, not only when some other change happens: + +```cypher +MATCH (m:message) +WITH m.from AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp +WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) +RETURN MessageFrom, LastMessageTimestamp +``` + +`drasi.changeDateTime(m)` exposes the wall-clock time of each change and `datetime.realtime()` is the current time, so this needs no extra timestamp bookkeeping in your app. + +### The Python Reaction + +Instead of a dashboard or an SSE stream, a single **Python reaction** subscribed to all three queries prints each change to the console: + +```python +def on_change(event): + query_id = event["query_id"] + for diff in event["results"]: + if diff["type"] == "ADD": + print(f"[{query_id}] + {diff['data']}") + elif diff["type"] == "DELETE": + print(f"[{query_id}] - {diff['data']}") + else: # UPDATE + print(f"[{query_id}] ~ {diff['before']} -> {diff['after']}") + + +await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) +``` + +`type` tells you which of `data`, or `before` and `after`, a diff carries. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. + +## Clean Up {#cleanup} + +When you're finished, stop the app with **Ctrl+C** in Terminal 1, then remove the database container: + +{{< tabpane persist="header" >}} +{{< tab header="bash / zsh" lang="bash" >}} +# Stop the container, keep data +bash scripts/cleanup.sh + +# Stop the container and delete the data volume +bash scripts/cleanup.sh --volumes +{{< /tab >}} +{{< tab header="PowerShell" lang="powershell" >}} +# Stop the container, keep data +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 + +# Stop the container and delete the data volume +powershell -ExecutionPolicy Bypass -File scripts/cleanup.ps1 -RemoveVolumes +{{< /tab >}} +{{< /tabpane >}} + +## What You Learned {#summary} + +- **Sources** connect Drasi to live data โ€” here, PostgreSQL via Change Data Capture, in a few lines of Python. +- **Continuous Queries** let you **filter**, **aggregate**, and even detect the **absence** of change (with the temporal `drasi.trueLater`) โ€” all kept current automatically, with no polling. +- A **Python reaction** turned every query change into console output; the same callback could call an API, write a file, or push a notification instead. + +From here, try the [Building Comfort](../building-comfort/) and [Curbside Pickup](../curbside-pickup/) tutorials, which add synthetic joins, cross-database joins, and a live Streamlit UI. diff --git a/tutorials/getting-started/app.py b/tutorials/getting-started/app.py new file mode 100644 index 0000000..a69334a --- /dev/null +++ b/tutorials/getting-started/app.py @@ -0,0 +1,119 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Getting Started -- a console app driven by a Drasi Python reaction. + +Run it with: + + python app.py + +It connects Drasi's PostgreSQL source to the tutorial's `message` table, starts +three continuous queries, and registers a single **Python reaction** that prints +each result-set change to the console. Then it stays running and watches. There +is no UI and no prompt: you drive changes by running SQL directly against the +database (see scripts/add-message.sh and scripts/delete-message.sh) and watch the +queries react here in real time. Press Ctrl+C to stop. +""" + +from __future__ import annotations + +import os + +# Quiet the engine's default INFO logging so it doesn't drown out the reaction's +# output. Set RUST_LOG=info (or debug) to see what the engine is doing. Must run +# before ``drasi`` is imported. +os.environ.setdefault("RUST_LOG", "warn") + +import asyncio # noqa: E402 +import sys # noqa: E402 +import time # noqa: E402 +from typing import Any # noqa: E402 + +from demo.config import ( # noqa: E402 + BOOTSTRAP_CONFIG, + INACTIVITY_SECONDS, + SOURCE_CONFIG, + SOURCE_ID, +) +from demo.queries import QUERIES # noqa: E402 + +from drasi import Drasi # noqa: E402 +from drasi.types import QueryResultEvent # noqa: E402 + + +def _row(row: dict[str, Any]) -> str: + """Render a result row as a compact, readable `key=value` string.""" + return " ".join(f"{key}={value!r}" for key, value in row.items()) + + +def _stamp() -> str: + return time.strftime("%H:%M:%S") + + +def on_change(event: QueryResultEvent) -> None: + """Print each way a query's result set changed (the console 'reaction').""" + query_id = event["query_id"] + for diff in event["results"]: + if diff["type"] == "ADD": + print(f"[{_stamp()}] [{query_id}] + {_row(diff['data'])}") + elif diff["type"] == "DELETE": + print(f"[{_stamp()}] [{query_id}] - {_row(diff['data'])}") + else: # UPDATE + print(f"[{_stamp()}] [{query_id}] ~ {_row(diff['before'])} -> {_row(diff['after'])}") + + +async def main() -> None: + # Flush each line as it is printed, so the reaction's output appears live + # even when the app's output is piped to a file rather than a terminal. + sys.stdout.reconfigure(line_buffering=True) + + async with await Drasi.create("getting-started") as drasi: + print("Starting Drasi: installing plugins and connecting to PostgreSQL...") + await drasi.install_plugin("source/postgres") + await drasi.install_plugin("bootstrap/postgres") + await drasi.start() + + await drasi.add_source("postgres", SOURCE_ID, SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + + for query in QUERIES: + await drasi.add_query(query.id, query.cypher, [SOURCE_ID]) + for query in QUERIES: + await drasi.wait_for_query(query.id, timeout=60) + + # Show the state each query bootstrapped from the existing rows. + print("\n=== Initial results ===") + for query in QUERIES: + rows = await drasi.get_query_results(query.id) + print(f"\n[{query.id}] {len(rows)} row(s):") + for row in rows: + print(f" {_row(row)}") + + # From here on, print changes as they arrive. + await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) + + print( + f"\n=== Watching for changes (Ctrl+C to stop) ===\n" + f"Add or delete messages with SQL (scripts/add-message.sh, " + f"scripts/delete-message.sh) and watch the queries react. A sender " + f"appears in inactive-people once they have been quiet for " + f"{INACTIVITY_SECONDS}s.\n" + ) + await asyncio.Event().wait() + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + print("\nStopped.") diff --git a/tutorials/getting-started/database/docker-compose.yml b/tutorials/getting-started/database/docker-compose.yml new file mode 100644 index 0000000..f79805b --- /dev/null +++ b/tutorials/getting-started/database/docker-compose.yml @@ -0,0 +1,44 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: getting-started + +services: + postgres: + image: postgres:16-alpine + container_name: getting-started-postgres + environment: + POSTGRES_DB: getting_started + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres_admin + command: + - "postgres" + - "-c" + - "wal_level=logical" + - "-c" + - "max_replication_slots=10" + - "-c" + - "max_wal_senders=10" + ports: + - "${POSTGRES_HOST_PORT:-5752}:5432" + volumes: + - getting_started_postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -h localhost -U postgres -d getting_started"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + getting_started_postgres_data: diff --git a/tutorials/getting-started/database/init.sql b/tutorials/getting-started/database/init.sql new file mode 100644 index 0000000..3e8ae44 --- /dev/null +++ b/tutorials/getting-started/database/init.sql @@ -0,0 +1,92 @@ +-- Copyright 2026 The Drasi Authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +-- Getting Started Tutorial Database Schema +-- +-- A single `message` table -- imagine a simple live message feed. The table and +-- columns are lower-case and unquoted so the node label and property names Drasi +-- sees match the Cypher continuous queries, which use (m:message) and m.message, +-- m.from, m.messageid without any change. + +-- Suppress noisy output during setup. +\set QUIET on +SET client_min_messages = ERROR; + +-- Create a user with replication privileges for CDC. +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'drasi_user') THEN + CREATE USER drasi_user WITH REPLICATION LOGIN PASSWORD 'drasi_password'; + END IF; +END +$$; + +-- Grant permissions on the database. +GRANT CREATE ON DATABASE getting_started TO drasi_user; +GRANT ALL PRIVILEGES ON DATABASE getting_started TO drasi_user; + +-- Drop existing table if it exists. +DROP TABLE IF EXISTS message CASCADE; + +-- message table: one row per message in the feed. +CREATE TABLE message ( + messageid SERIAL PRIMARY KEY, + "from" VARCHAR(50) NOT NULL, + message VARCHAR(200) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Set REPLICA IDENTITY to FULL so change events include every column. +ALTER TABLE message REPLICA IDENTITY FULL; + +-- Ensure drasi_user owns the table. +ALTER TABLE message OWNER TO drasi_user; + +-- Grant permissions to drasi_user. +GRANT USAGE ON SCHEMA public TO drasi_user; +GRANT SELECT ON ALL TABLES IN SCHEMA public TO drasi_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO drasi_user; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO drasi_user; + +-- Create the publication for logical replication. The replication slot itself is +-- created by the Drasi PostgreSQL source on startup (with a consistent +-- snapshot), so the rows seeded below are loaded once via bootstrap and not also +-- replayed as change events. +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'drasi_getting_started_pub') THEN + CREATE PUBLICATION drasi_getting_started_pub FOR TABLE message; + END IF; +END +$$; + +-- Seed a few messages. One of them is exactly 'Hello World', which the +-- hello-world-from query matches. +INSERT INTO message ("from", message) +SELECT * FROM (VALUES + ('Buzz Lightyear', 'To infinity and beyond!'), + ('Brian Kernighan', 'Hello World'), + ('Ada Lovelace', 'The Analytical Engine weaves algebraic patterns.') +) AS d("from", message) +WHERE NOT EXISTS (SELECT 1 FROM message); + +-- Summary. +SET client_min_messages = NOTICE; +DO $$ +BEGIN + RAISE NOTICE 'Getting Started database initialized successfully!'; + RAISE NOTICE 'Table: message'; + RAISE NOTICE 'Publication: drasi_getting_started_pub'; +END +$$; diff --git a/tutorials/getting-started/demo/__init__.py b/tutorials/getting-started/demo/__init__.py new file mode 100644 index 0000000..c305d38 --- /dev/null +++ b/tutorials/getting-started/demo/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The Getting Started demo package: the tutorial's queries and configuration.""" diff --git a/tutorials/getting-started/demo/config.py b/tutorials/getting-started/demo/config.py new file mode 100644 index 0000000..8900a00 --- /dev/null +++ b/tutorials/getting-started/demo/config.py @@ -0,0 +1,56 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Configuration for the Getting Started tutorial. + +Just enough to point Drasi's PostgreSQL source at the tutorial database, read +from the environment with the same defaults the database uses. The app only +reads and reacts -- it never writes -- so there is no database client here; you +drive changes with SQL run directly against the database. +""" + +from __future__ import annotations + +import os + +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5752")), + "database": os.environ.get("POSTGRES_DATABASE", "getting_started"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} + +# The Postgres source creates its own replication slot on startup and reuses the +# publication created by database/init.sql. The table is schema-qualified here +# (public.message) while tableKeys uses the bare name; the node label Drasi sees +# is the bare name, so the Cypher matches (m:message). +SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.message"], + "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], + "slotName": "drasi_getting_started_slot", + "publicationName": "drasi_getting_started_pub", +} + +# The bootstrap provider loads the rows that already exist when the query starts. +BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} + +# The id of the single source the queries read from. +SOURCE_ID = "messages" + +# How long a sender can be quiet before inactive-people flags them (matches the +# duration in the query). Shown in the app's startup banner. +INACTIVITY_SECONDS = 20 diff --git a/tutorials/getting-started/demo/queries.py b/tutorials/getting-started/demo/queries.py new file mode 100644 index 0000000..b49cd6c --- /dev/null +++ b/tutorials/getting-started/demo/queries.py @@ -0,0 +1,85 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The three continuous queries that power the demo. + +They build up in complexity: a filter, an aggregation, and a temporal query that +detects the *absence* of change. Each is written out in full so you can read +exactly what Drasi runs. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +# Query ids, used to register the queries and to label their console output. +HELLO_WORLD_FROM = "hello-world-from" +MESSAGE_COUNT = "message-count" +INACTIVE_PEOPLE = "inactive-people" + + +@dataclass(frozen=True) +class Query: + """One continuous query: how to register it and how to index its rows.""" + + id: str + key: str # the RETURN column that identifies each row (its primary key) + cypher: str + + +# Filter: messages whose text is exactly "Hello World", with who sent them. +HELLO_WORLD_FROM_QUERY = Query( + id=HELLO_WORLD_FROM, + key="MessageId", + cypher=""" + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.from AS MessageFrom + """, +) + +# Aggregation: how many times each distinct message has been sent. +MESSAGE_COUNT_QUERY = Query( + id=MESSAGE_COUNT, + key="Message", + cypher=""" + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, +) + +# Temporal: senders who have not sent a message in the last 20 seconds. +# drasi.trueLater schedules a future re-evaluation so a sender appears the instant +# they cross the 20-second threshold, not only when some other change happens. +INACTIVE_PEOPLE_QUERY = Query( + id=INACTIVE_PEOPLE, + key="MessageFrom", + cypher=""" + MATCH (m:message) + WITH m.from AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, +) + +# The engine registers these in order and subscribes one reaction to them all. +QUERIES = [ + HELLO_WORLD_FROM_QUERY, + MESSAGE_COUNT_QUERY, + INACTIVE_PEOPLE_QUERY, +] diff --git a/tutorials/getting-started/requirements.txt b/tutorials/getting-started/requirements.txt new file mode 100644 index 0000000..52faded --- /dev/null +++ b/tutorials/getting-started/requirements.txt @@ -0,0 +1,8 @@ +# Getting Started tutorial dependencies. +# +# pip install -r requirements.txt +# +# drasi-lib is the Python binding for Drasi's embeddable engine. It downloads the +# Postgres source/bootstrap plugins from ghcr.io at runtime, so the first run +# needs network access. The app only reads and reacts, so it needs nothing else. +drasi-lib>=0.2,<1 diff --git a/tutorials/getting-started/scripts/add-message.ps1 b/tutorials/getting-started/scripts/add-message.ps1 new file mode 100644 index 0000000..4332b18 --- /dev/null +++ b/tutorials/getting-started/scripts/add-message.ps1 @@ -0,0 +1,34 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Add Message Script (Windows) +# Inserts a message into the feed with a plain SQL INSERT. Drasi captures the +# change via CDC and the console app prints how the queries react. + +param( + [Parameter(Mandatory = $true)][string]$From, + [Parameter(Mandatory = $true)][string]$Message +) + +$Container = if ($env:POSTGRES_CONTAINER) { $env:POSTGRES_CONTAINER } else { "getting-started-postgres" } +$Db = if ($env:POSTGRES_DATABASE) { $env:POSTGRES_DATABASE } else { "getting_started" } +$DbUser = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "drasi_user" } + +# Escape single quotes for safe SQL string literals ('' is an escaped quote). +$FromSql = $From -replace "'", "''" +$MessageSql = $Message -replace "'", "''" + +Write-Host "Adding message from '$From': $Message" +$sql = "INSERT INTO message (""from"", message) VALUES ('$FromSql', '$MessageSql') RETURNING messageid, ""from"", message;" +$sql | docker exec -i $Container psql -U $DbUser -d $Db diff --git a/tutorials/getting-started/scripts/add-message.sh b/tutorials/getting-started/scripts/add-message.sh new file mode 100755 index 0000000..8081c03 --- /dev/null +++ b/tutorials/getting-started/scripts/add-message.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Add Message Script +# Inserts a message into the feed with a plain SQL INSERT. Drasi's PostgreSQL +# source captures the change via CDC and the console app prints how the queries +# react. There is no application code in the loop -- just SQL. + +set -e + +CONTAINER="${POSTGRES_CONTAINER:-getting-started-postgres}" +DB="${POSTGRES_DATABASE:-getting_started}" +DB_USER="${POSTGRES_USER:-drasi_user}" + +FROM_NAME="${1:-}" +MESSAGE_TEXT="${2:-}" + +if [ -z "$FROM_NAME" ] || [ -z "$MESSAGE_TEXT" ]; then + echo "Usage: $0 <from> <message>" + echo + echo "Examples:" + echo " $0 'Alice' 'Hello World' # matches the hello-world-from query" + echo " $0 'Bob' 'Goodbye World' # updates message-count" + exit 1 +fi + +# Escape single quotes for safe SQL string literals ('' is an escaped quote). +FROM_SQL="${FROM_NAME//\'/\'\'}" +MESSAGE_SQL="${MESSAGE_TEXT//\'/\'\'}" + +if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then + echo "Error: the ${CONTAINER} container is not running." + echo "Run ./scripts/setup-database.sh first." + exit 1 +fi + +echo "Adding message from '$FROM_NAME': $MESSAGE_TEXT" +docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB" -c \ + "INSERT INTO message (\"from\", message) VALUES ('$FROM_SQL', '$MESSAGE_SQL') RETURNING messageid, \"from\", message;" diff --git a/tutorials/getting-started/scripts/cleanup.ps1 b/tutorials/getting-started/scripts/cleanup.ps1 new file mode 100644 index 0000000..63f6362 --- /dev/null +++ b/tutorials/getting-started/scripts/cleanup.ps1 @@ -0,0 +1,48 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Cleanup Script (Windows) +# Stops the PostgreSQL container. The app runs in the foreground, so stop it +# first with Ctrl+C. Pass -RemoveVolumes to also delete the database volume. + +param( + [switch]$RemoveVolumes +) + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$DatabaseDir = Join-Path $ScriptDir "..\database" + +Write-Host "=== Getting Started - Cleanup ===" +Write-Host "" + +Push-Location $DatabaseDir +try { + if ($RemoveVolumes) { + Write-Host "Removing container and volumes..." + docker compose down -v 2>&1 | Write-Host + } else { + Write-Host "Removing container (keeping volumes)..." + docker compose down 2>&1 | Write-Host + } +} +finally { + Pop-Location +} + +Write-Host "" +Write-Host "=== Cleanup complete! ===" +Write-Host "" +Write-Host "Options:" +Write-Host " scripts/cleanup.ps1 # Stop containers, keep data volumes" +Write-Host " scripts/cleanup.ps1 -RemoveVolumes # Stop containers and remove data volumes" diff --git a/tutorials/getting-started/scripts/cleanup.sh b/tutorials/getting-started/scripts/cleanup.sh new file mode 100755 index 0000000..b6a41bb --- /dev/null +++ b/tutorials/getting-started/scripts/cleanup.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Cleanup Script +# Removes the PostgreSQL container. The app runs in the foreground, so stop it +# first with Ctrl+C. Pass --volumes to also delete the database volume. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DATABASE_DIR="$SCRIPT_DIR/../database" +REMOVE_VOLUMES="${1:-}" + +echo "=== Getting Started - Cleanup ===" +echo + +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "Warning: docker-compose not found, skipping container cleanup" + exit 0 +fi + +echo "Stopping PostgreSQL container..." +cd "$DATABASE_DIR" + +if [ "$REMOVE_VOLUMES" = "--volumes" ] || [ "$REMOVE_VOLUMES" = "-v" ]; then + echo "Removing container and volumes..." + $COMPOSE_CMD down -v +else + echo "Removing container (keeping volumes)..." + $COMPOSE_CMD down +fi + +echo +echo "=== Cleanup complete! ===" +echo +echo "Options:" +echo " $0 # Stop containers, keep data volumes" +echo " $0 --volumes # Stop containers and remove data volumes" diff --git a/tutorials/getting-started/scripts/delete-message.ps1 b/tutorials/getting-started/scripts/delete-message.ps1 new file mode 100644 index 0000000..f93a696 --- /dev/null +++ b/tutorials/getting-started/scripts/delete-message.ps1 @@ -0,0 +1,29 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Delete Message Script (Windows) +# Deletes a message by id with a plain SQL DELETE. Drasi captures the change via +# CDC and the console app prints how the queries react. + +param( + [Parameter(Mandatory = $true)][int]$MessageId +) + +$Container = if ($env:POSTGRES_CONTAINER) { $env:POSTGRES_CONTAINER } else { "getting-started-postgres" } +$Db = if ($env:POSTGRES_DATABASE) { $env:POSTGRES_DATABASE } else { "getting_started" } +$DbUser = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "drasi_user" } + +Write-Host "Deleting message $MessageId" +$sql = "DELETE FROM message WHERE messageid=$MessageId RETURNING messageid, ""from"", message;" +$sql | docker exec -i $Container psql -U $DbUser -d $Db diff --git a/tutorials/getting-started/scripts/delete-message.sh b/tutorials/getting-started/scripts/delete-message.sh new file mode 100755 index 0000000..a25a620 --- /dev/null +++ b/tutorials/getting-started/scripts/delete-message.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Delete Message Script +# Deletes a message by id with a plain SQL DELETE. Drasi captures the change via +# CDC and the console app prints how the queries react. + +set -e + +CONTAINER="${POSTGRES_CONTAINER:-getting-started-postgres}" +DB="${POSTGRES_DATABASE:-getting_started}" +DB_USER="${POSTGRES_USER:-drasi_user}" + +MESSAGE_ID="${1:-}" + +if [ -z "$MESSAGE_ID" ]; then + echo "Usage: $0 <messageid>" + echo + echo "Tip: run 'docker exec $CONTAINER psql -U $DB_USER -d $DB -c \"SELECT messageid, \\\"from\\\", message FROM message;\"' to list ids." + exit 1 +fi + +if ! printf '%s' "$MESSAGE_ID" | grep -Eq '^[0-9]+$'; then + echo "Error: messageid must be an integer (got '$MESSAGE_ID')." + exit 1 +fi + +if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then + echo "Error: the ${CONTAINER} container is not running." + echo "Run ./scripts/setup-database.sh first." + exit 1 +fi + +echo "Deleting message $MESSAGE_ID" +docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB" -c \ + "DELETE FROM message WHERE messageid=$MESSAGE_ID RETURNING messageid, \"from\", message;" diff --git a/tutorials/getting-started/scripts/setup-database.ps1 b/tutorials/getting-started/scripts/setup-database.ps1 new file mode 100644 index 0000000..673fa00 --- /dev/null +++ b/tutorials/getting-started/scripts/setup-database.ps1 @@ -0,0 +1,71 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Setup Database Script (Windows) +# Starts PostgreSQL with logical replication (WAL) enabled for CDC and seeds the +# message feed. + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$DatabaseDir = Join-Path $ScriptDir "..\database" + +Write-Host "=== Getting Started - Database Setup ===" +Write-Host "" + +if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { + Write-Host "Error: Docker is not installed or not in PATH" + exit 1 +} + +docker info 2>&1 | Out-Null +if ($LASTEXITCODE -ne 0) { + Write-Host "Error: Docker daemon is not running" + exit 1 +} + +Push-Location $DatabaseDir +try { + Write-Host "Stopping any existing PostgreSQL container..." + docker compose down -v 2>&1 | Out-Null + + Write-Host "Starting PostgreSQL with WAL replication..." + docker compose up -d 2>&1 | Write-Host + if ($LASTEXITCODE -ne 0) { + Write-Host "Error: failed to start PostgreSQL container" + exit 1 + } + + Write-Host "Waiting for PostgreSQL to be ready..." + for ($i = 1; $i -le 30; $i++) { + docker exec getting-started-postgres pg_isready -h localhost -U postgres -d getting_started 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { Write-Host "PostgreSQL is ready!"; break } + Write-Host " Waiting... ($i/30)"; Start-Sleep -Seconds 2 + } + + Write-Host "" + Write-Host "Applying schema and seed data..." + Get-Content (Join-Path $DatabaseDir "init.sql") | ` + docker exec -i getting-started-postgres psql -v ON_ERROR_STOP=1 -U postgres -d getting_started + if ($LASTEXITCODE -ne 0) { Write-Host "Error: failed to apply schema and seed data"; exit 1 } + + Write-Host "" + Write-Host "Seeded messages:" + 'SELECT messageid, "from", message FROM message ORDER BY messageid;' | ` + docker exec -i getting-started-postgres psql -U drasi_user -d getting_started + + Write-Host "" + Write-Host "=== Database setup complete! ===" +} +finally { + Pop-Location +} diff --git a/tutorials/getting-started/scripts/setup-database.sh b/tutorials/getting-started/scripts/setup-database.sh new file mode 100755 index 0000000..4dd6b1e --- /dev/null +++ b/tutorials/getting-started/scripts/setup-database.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Setup Database Script +# Starts PostgreSQL with logical replication (WAL) enabled for CDC and seeds the +# message feed. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DATABASE_DIR="$SCRIPT_DIR/../database" + +echo "=== Getting Started - Database Setup ===" +echo + +if ! command -v docker &> /dev/null; then + echo "Error: Docker is not installed or not in PATH" + echo "Please install Docker: https://docs.docker.com/get-docker/" + exit 1 +fi + +if ! docker info &> /dev/null; then + echo "Error: Docker daemon is not running" + echo "Please start Docker and try again" + exit 1 +fi + +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "Error: docker-compose is not installed" + echo "Please install Docker Compose: https://docs.docker.com/compose/install/" + exit 1 +fi + +echo "Using: $COMPOSE_CMD" +echo + +echo "Stopping any existing PostgreSQL container..." +cd "$DATABASE_DIR" +$COMPOSE_CMD down -v 2>/dev/null || true + +echo "Starting PostgreSQL with WAL replication..." +$COMPOSE_CMD up -d + +echo "Waiting for PostgreSQL to be ready..." +for i in $(seq 1 30); do + if docker exec getting-started-postgres pg_isready -h localhost -U postgres -d getting_started &> /dev/null; then + echo "PostgreSQL is ready!" + break + fi + echo " Waiting... ($i/30)" + sleep 2 +done + +echo +echo "Applying schema and seed data..." +docker exec -i getting-started-postgres \ + psql -v ON_ERROR_STOP=1 -U postgres -d getting_started < "$DATABASE_DIR/init.sql" + +echo +echo "Seeded messages:" +docker exec getting-started-postgres psql -U drasi_user -d getting_started -c \ + "SELECT messageid, \"from\", message FROM message ORDER BY messageid;" + +echo +echo "=== Database setup complete! ===" +echo " PostgreSQL: localhost:${POSTGRES_HOST_PORT:-5752} / getting_started" diff --git a/tutorials/getting-started/scripts/start-demo.ps1 b/tutorials/getting-started/scripts/start-demo.ps1 new file mode 100644 index 0000000..46c648f --- /dev/null +++ b/tutorials/getting-started/scripts/start-demo.ps1 @@ -0,0 +1,41 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Start Demo Script (Windows) +# Starts (and seeds) the database, then runs the console app in the foreground. +# Leave it running in this terminal; use a second terminal to add and delete +# messages with scripts/add-message.ps1 and scripts/delete-message.ps1. Press +# Ctrl+C to stop, then run scripts/cleanup.ps1. + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$TutorialDir = Join-Path $ScriptDir ".." + +& (Join-Path $ScriptDir "setup-database.ps1") +if ($LASTEXITCODE -ne 0) { + Write-Host "Database setup failed; not starting the app." + exit 1 +} + +Write-Host "" +Write-Host "Starting the console app (embeds the Drasi engine)..." +Write-Host "On first run it downloads the Drasi plugins from ghcr.io - give it a moment." +Write-Host "" + +Push-Location $TutorialDir +try { + python app.py +} +finally { + Pop-Location +} diff --git a/tutorials/getting-started/scripts/start-demo.sh b/tutorials/getting-started/scripts/start-demo.sh new file mode 100755 index 0000000..0ae4c0c --- /dev/null +++ b/tutorials/getting-started/scripts/start-demo.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Start Demo Script +# Starts (and seeds) the database, then runs the console app in the foreground. +# The app embeds the Drasi engine and prints query changes. Leave it running in +# this terminal (Terminal 1); use a second terminal to add and delete messages +# with scripts/add-message.sh and scripts/delete-message.sh. Press Ctrl+C to +# stop, then run ./scripts/cleanup.sh. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TUTORIAL_DIR="$SCRIPT_DIR/.." + +bash "$SCRIPT_DIR/setup-database.sh" + +echo +echo "Starting the console app (embeds the Drasi engine)..." +echo "On first run it downloads the Drasi plugins from ghcr.io โ€” give it a moment." +echo + +cd "$TUTORIAL_DIR" +exec python app.py diff --git a/website/config.toml b/website/config.toml index b37ec4b..0a8da6a 100644 --- a/website/config.toml +++ b/website/config.toml @@ -66,6 +66,11 @@ defaultContentLanguageInSubdir = false source = "content" target = "content" + [[module.mounts]] + source = "../tutorials/getting-started" + target = "content/docs/tutorials/getting-started" + includeFiles = ["/_index.md", "/images/**"] + [[module.mounts]] source = "../tutorials/building-comfort" target = "content/docs/tutorials/building-comfort" diff --git a/website/content/docs/tutorials/_index.md b/website/content/docs/tutorials/_index.md index 3a6e9fc..4a86dd1 100644 --- a/website/content/docs/tutorials/_index.md +++ b/website/content/docs/tutorials/_index.md @@ -15,6 +15,15 @@ in the repository (runnable code plus its write-up), so you can read it here or clone it and run it locally. <div class="card-grid card-grid--2"> + <a href="getting-started/"> + <div class="unified-card unified-card--tutorials"> + <div class="unified-card-icon"><i class="fas fa-terminal"></i></div> + <div class="unified-card-content"> + <h3 class="unified-card-title">Getting Started</h3> + <p class="unified-card-summary">Connect Drasi to a live PostgreSQL feed and build three continuous queries (filter, aggregate, and a temporal absence-of-change) in a small no-UI console app driven by a Python reaction.</p> + </div> + </div> + </a> <a href="building-comfort/"> <div class="unified-card unified-card--tutorials"> <div class="unified-card-icon"><i class="fas fa-building"></i></div> diff --git a/website/layouts/_default/_markup/render-codeblock-cypher.html b/website/layouts/_default/_markup/render-codeblock-cypher.html index f217fb1..9e9325b 100644 --- a/website/layouts/_default/_markup/render-codeblock-cypher.html +++ b/website/layouts/_default/_markup/render-codeblock-cypher.html @@ -20,7 +20,7 @@ {{- /* keywords */ -}} {{- $h = $h | replaceRE `(?i)\b(MATCH|OPTIONAL|WITH|WHERE|RETURN|MERGE|CREATE|DELETE|SET|REMOVE|UNWIND|ORDER|BY|SKIP|LIMIT|AS|CASE|WHEN|THEN|ELSE|END|AND|OR|XOR|NOT|IN|STARTS|ENDS|CONTAINS|IS|NULL|TRUE|FALSE|DISTINCT)\b` `<span class="k">$1</span>` -}} {{- /* functions called as name( โ€” includes Drasi's Cypher extension functions */ -}} -{{- $h = $h | replaceRE `\b(avg|count|sum|min|max|collect|floor|ceil|round|abs|coalesce|toInteger|toFloat|size|length|duration|trueFor|changeDateTime|getVersionByTimestamp|listMax|listMin)(\s*\()` `<span class="nf">$1</span>$2` -}} +{{- $h = $h | replaceRE `\b(avg|count|sum|min|max|collect|floor|ceil|round|abs|coalesce|toInteger|toFloat|size|length|duration|trueFor|trueLater|changeDateTime|getVersionByTimestamp|realtime|listMax|listMin)(\s*\()` `<span class="nf">$1</span>$2` -}} {{- /* node / relationship labels after a colon */ -}} {{- $h = $h | replaceRE `:([A-Za-z_][A-Za-z0-9_]*)` `:<span class="nl">$1</span>` -}} {{- /* numbers */ -}} From 36131cb40223a8b33849b8a9c620e10add65c2e2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 13:26:41 -0700 Subject: [PATCH 08/19] Standardize Getting Started on docker exec (drop message scripts) The drive-change steps had the bash tab call a dedicated script while the PowerShell tab ran docker exec. Standardize on a single `docker exec ... psql` command that is identical in both shells, and remove the dedicated add-message/delete-message scripts. To keep the command free of shell-specific quote escaping, rename the reserved `from` column to `sender` (so no quoted identifier is needed); the query property becomes m.sender. Re-verified end-to-end: the documented docker exec INSERT/DELETE drive the filter, aggregation, and the temporal inactive-people query. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/getting-started/README.md | 43 +++++++--------- tutorials/getting-started/_index.md | 45 ++++++++-------- tutorials/getting-started/app.py | 6 +-- tutorials/getting-started/database/init.sql | 8 +-- tutorials/getting-started/demo/queries.py | 4 +- .../getting-started/scripts/add-message.ps1 | 34 ------------- .../getting-started/scripts/add-message.sh | 51 ------------------- .../scripts/delete-message.ps1 | 29 ----------- .../getting-started/scripts/delete-message.sh | 48 ----------------- .../scripts/setup-database.ps1 | 2 +- .../getting-started/scripts/setup-database.sh | 2 +- .../getting-started/scripts/start-demo.ps1 | 2 +- .../getting-started/scripts/start-demo.sh | 2 +- 13 files changed, 51 insertions(+), 225 deletions(-) delete mode 100644 tutorials/getting-started/scripts/add-message.ps1 delete mode 100755 tutorials/getting-started/scripts/add-message.sh delete mode 100644 tutorials/getting-started/scripts/delete-message.ps1 delete mode 100755 tutorials/getting-started/scripts/delete-message.sh diff --git a/tutorials/getting-started/README.md b/tutorials/getting-started/README.md index 20786b0..5ae81ff 100644 --- a/tutorials/getting-started/README.md +++ b/tutorials/getting-started/README.md @@ -20,7 +20,7 @@ This is the getting-started tutorial for **`drasi-lib`**, the Python binding tha > **Before you begin** > -> - **Two terminals:** **Terminal 1** runs the console app (it stays in the foreground and prints changes). Use **Terminal 2** for the helper scripts that add and delete messages. +> - **Two terminals:** **Terminal 1** runs the console app (it stays in the foreground and prints changes). Use **Terminal 2** to change messages with SQL. > - **Working directory:** run every command from the tutorial directory (`tutorials/getting-started/`). The dev container opens there automatically; if you're running locally, `cd tutorials/getting-started` first. > - **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*). Use the one for your shell. > - **Ports:** PostgreSQL is published on `5752`. There is no web UI. @@ -109,26 +109,19 @@ Once it's ready, the app prints each query's initial results and then waits for ``` ## Step 4 of 4: Drive Change -With Terminal 1 running the app, use **Terminal 2** to change the data and watch it react. +With Terminal 1 running the app, use **Terminal 2** to change the data and watch it react. The commands below use `docker exec` to run SQL inside the database container, and are identical in bash and PowerShell. -> **No middle tier โ€” the scripts just write to the database** +> **No middle tier โ€” just SQL** > -> The helper scripts run a plain SQL `INSERT` or `DELETE` against PostgreSQL โ€” exactly what an existing app would already do. There's no API to call and no event to publish. Drasi observes the row change through logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which prints the change. The **PowerShell** tabs make this obvious โ€” they're the raw SQL statements. +> Each command runs a plain SQL `INSERT` or `DELETE` against PostgreSQL โ€” exactly what an existing app would already do. There's no API to call and no event to publish. Drasi observes the row change through logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which prints the change. ### Add a message Send a new `Hello World`, from someone new: -**bash / zsh** - ```bash -bash scripts/add-message.sh "Grace Hopper" "Hello World" -``` - -**PowerShell** - -```powershell -docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "INSERT INTO message (""from"", message) VALUES ('Grace Hopper', 'Hello World');" +docker exec getting-started-postgres psql -U drasi_user -d getting_started \ + -c "INSERT INTO message (sender, message) VALUES ('Grace Hopper', 'Hello World') RETURNING messageid;" ``` Terminal 1 reacts immediately โ€” the filter picks up the new sender and the aggregate ticks up: @@ -138,22 +131,20 @@ Terminal 1 reacts immediately โ€” the filter picks up the new sender and the agg [13:06:26] [message-count] ~ Frequency=1 Message='Hello World' -> Frequency=2 Message='Hello World' ``` -Try a message that *isn't* `Hello World` (say `bash scripts/add-message.sh "Bob" "Goodbye World"`) and only `message-count` changes โ€” the filter query ignores it. - -### Delete a message - -Remove a message by its id (the add script prints the `messageid`; message `2` is the seeded `Hello World`): - -**bash / zsh** +Try a message that *isn't* `Hello World` and only `message-count` changes โ€” the filter query ignores it: ```bash -bash scripts/delete-message.sh 2 +docker exec getting-started-postgres psql -U drasi_user -d getting_started \ + -c "INSERT INTO message (sender, message) VALUES ('Bob', 'Goodbye World');" ``` -**PowerShell** +### Delete a message -```powershell -docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "DELETE FROM message WHERE messageid=2;" +Remove a message by its id (the `INSERT` above prints the new id via `RETURNING`; message `2` is the seeded `Hello World`): + +```bash +docker exec getting-started-postgres psql -U drasi_user -d getting_started \ + -c "DELETE FROM message WHERE messageid=2;" ``` `hello-world-from` drops that row and `message-count` decrements โ€” a row leaving a result set is a change Drasi reports too. @@ -196,7 +187,7 @@ They build up in complexity. ```cypher MATCH (m:message) WHERE m.message = 'Hello World' -RETURN m.messageid AS MessageId, m.from AS MessageFrom +RETURN m.messageid AS MessageId, m.sender AS MessageFrom ``` **Aggregation** โ€” `message-count` groups by message text and counts. Like SQL's `GROUP BY`, the non-aggregated `Message` travels alongside `count(m)`, and Drasi maintains the counts **incrementally** โ€” one insert bumps exactly one row: @@ -210,7 +201,7 @@ RETURN m.message AS Message, count(m) AS Frequency ```cypher MATCH (m:message) -WITH m.from AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp +WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) OR drasi.trueLater( LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), diff --git a/tutorials/getting-started/_index.md b/tutorials/getting-started/_index.md index 5403a3f..759dc82 100644 --- a/tutorials/getting-started/_index.md +++ b/tutorials/getting-started/_index.md @@ -54,7 +54,7 @@ This is the getting-started tutorial for **`drasi-lib`**, the Python binding tha | **[How It Works](#how)** | Understand the source, the three queries, and the Python reaction | {{% alert title="Before you begin" color="info" %}} -- **Two terminals:** **Terminal 1** runs the console app (it stays in the foreground and prints changes). Use **Terminal 2** for the helper scripts that add and delete messages. +- **Two terminals:** **Terminal 1** runs the console app (it stays in the foreground and prints changes). Use **Terminal 2** to change messages with SQL. - **Working directory:** run every command from the tutorial directory (`tutorials/getting-started/`). The dev container opens there automatically; if you're running locally, `cd tutorials/getting-started` first. - **Command tabs:** commands are shown in tabs (*bash / zsh* and *PowerShell*). Use the one for your shell. - **Ports:** PostgreSQL is published on `5752`. There is no web UI. @@ -142,24 +142,20 @@ Once it's ready, the app prints each query's initial results and then waits for ## Step 4 of 4: Drive Change {#drive} -With Terminal 1 running the app, use **Terminal 2** to change the data and watch it react. +With Terminal 1 running the app, use **Terminal 2** to change the data and watch it react. The commands below use `docker exec` to run SQL inside the database container, and are identical in bash and PowerShell. -{{% alert title="No middle tier โ€” the scripts just write to the database" color="info" %}} -The helper scripts run a plain SQL `INSERT` or `DELETE` against PostgreSQL โ€” exactly what an existing app would already do. There's no API to call and no event to publish. Drasi observes the row change through logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which prints the change. The **PowerShell** tabs make this obvious โ€” they're the raw SQL statements. +{{% alert title="No middle tier โ€” just SQL" color="info" %}} +Each command runs a plain SQL `INSERT` or `DELETE` against PostgreSQL โ€” exactly what an existing app would already do. There's no API to call and no event to publish. Drasi observes the row change through logical replication (CDC), re-evaluates the affected queries, and calls the Python reaction, which prints the change. {{% /alert %}} ### Add a message Send a new `Hello World`, from someone new: -{{< tabpane persist="header" >}} -{{< tab header="bash / zsh" lang="bash" >}} -bash scripts/add-message.sh "Grace Hopper" "Hello World" -{{< /tab >}} -{{< tab header="PowerShell" lang="powershell" >}} -docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "INSERT INTO message (""from"", message) VALUES ('Grace Hopper', 'Hello World');" -{{< /tab >}} -{{< /tabpane >}} +```bash +docker exec getting-started-postgres psql -U drasi_user -d getting_started \ + -c "INSERT INTO message (sender, message) VALUES ('Grace Hopper', 'Hello World') RETURNING messageid;" +``` Terminal 1 reacts immediately โ€” the filter picks up the new sender and the aggregate ticks up: @@ -168,20 +164,21 @@ Terminal 1 reacts immediately โ€” the filter picks up the new sender and the agg [13:06:26] [message-count] ~ Frequency=1 Message='Hello World' -> Frequency=2 Message='Hello World' ``` -Try a message that *isn't* `Hello World` (say `bash scripts/add-message.sh "Bob" "Goodbye World"`) and only `message-count` changes โ€” the filter query ignores it. +Try a message that *isn't* `Hello World` and only `message-count` changes โ€” the filter query ignores it: + +```bash +docker exec getting-started-postgres psql -U drasi_user -d getting_started \ + -c "INSERT INTO message (sender, message) VALUES ('Bob', 'Goodbye World');" +``` ### Delete a message -Remove a message by its id (the add script prints the `messageid`; message `2` is the seeded `Hello World`): +Remove a message by its id (the `INSERT` above prints the new id via `RETURNING`; message `2` is the seeded `Hello World`): -{{< tabpane persist="header" >}} -{{< tab header="bash / zsh" lang="bash" >}} -bash scripts/delete-message.sh 2 -{{< /tab >}} -{{< tab header="PowerShell" lang="powershell" >}} -docker exec getting-started-postgres psql -U drasi_user -d getting_started -c "DELETE FROM message WHERE messageid=2;" -{{< /tab >}} -{{< /tabpane >}} +```bash +docker exec getting-started-postgres psql -U drasi_user -d getting_started \ + -c "DELETE FROM message WHERE messageid=2;" +``` `hello-world-from` drops that row and `message-count` decrements โ€” a row leaving a result set is a change Drasi reports too. @@ -224,7 +221,7 @@ They build up in complexity. ```cypher MATCH (m:message) WHERE m.message = 'Hello World' -RETURN m.messageid AS MessageId, m.from AS MessageFrom +RETURN m.messageid AS MessageId, m.sender AS MessageFrom ``` **Aggregation** โ€” `message-count` groups by message text and counts. Like SQL's `GROUP BY`, the non-aggregated `Message` travels alongside `count(m)`, and Drasi maintains the counts **incrementally** โ€” one insert bumps exactly one row: @@ -238,7 +235,7 @@ RETURN m.message AS Message, count(m) AS Frequency ```cypher MATCH (m:message) -WITH m.from AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp +WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) OR drasi.trueLater( LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), diff --git a/tutorials/getting-started/app.py b/tutorials/getting-started/app.py index a69334a..8c375a5 100644 --- a/tutorials/getting-started/app.py +++ b/tutorials/getting-started/app.py @@ -22,7 +22,7 @@ three continuous queries, and registers a single **Python reaction** that prints each result-set change to the console. Then it stays running and watches. There is no UI and no prompt: you drive changes by running SQL directly against the -database (see scripts/add-message.sh and scripts/delete-message.sh) and watch the +database (with `docker exec ... psql`, shown in the tutorial) and watch the queries react here in real time. Press Ctrl+C to stop. """ @@ -104,8 +104,8 @@ async def main() -> None: print( f"\n=== Watching for changes (Ctrl+C to stop) ===\n" - f"Add or delete messages with SQL (scripts/add-message.sh, " - f"scripts/delete-message.sh) and watch the queries react. A sender " + f"Add or delete messages by running SQL against the database " + f"(docker exec ... psql) and watch the queries react. A sender " f"appears in inactive-people once they have been quiet for " f"{INACTIVITY_SECONDS}s.\n" ) diff --git a/tutorials/getting-started/database/init.sql b/tutorials/getting-started/database/init.sql index 3e8ae44..521f51e 100644 --- a/tutorials/getting-started/database/init.sql +++ b/tutorials/getting-started/database/init.sql @@ -17,7 +17,7 @@ -- A single `message` table -- imagine a simple live message feed. The table and -- columns are lower-case and unquoted so the node label and property names Drasi -- sees match the Cypher continuous queries, which use (m:message) and m.message, --- m.from, m.messageid without any change. +-- m.sender, m.messageid without any change. -- Suppress noisy output during setup. \set QUIET on @@ -42,7 +42,7 @@ DROP TABLE IF EXISTS message CASCADE; -- message table: one row per message in the feed. CREATE TABLE message ( messageid SERIAL PRIMARY KEY, - "from" VARCHAR(50) NOT NULL, + sender VARCHAR(50) NOT NULL, message VARCHAR(200) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); @@ -73,12 +73,12 @@ $$; -- Seed a few messages. One of them is exactly 'Hello World', which the -- hello-world-from query matches. -INSERT INTO message ("from", message) +INSERT INTO message (sender, message) SELECT * FROM (VALUES ('Buzz Lightyear', 'To infinity and beyond!'), ('Brian Kernighan', 'Hello World'), ('Ada Lovelace', 'The Analytical Engine weaves algebraic patterns.') -) AS d("from", message) +) AS d(sender, message) WHERE NOT EXISTS (SELECT 1 FROM message); -- Summary. diff --git a/tutorials/getting-started/demo/queries.py b/tutorials/getting-started/demo/queries.py index b49cd6c..c445584 100644 --- a/tutorials/getting-started/demo/queries.py +++ b/tutorials/getting-started/demo/queries.py @@ -45,7 +45,7 @@ class Query: cypher=""" MATCH (m:message) WHERE m.message = 'Hello World' - RETURN m.messageid AS MessageId, m.from AS MessageFrom + RETURN m.messageid AS MessageId, m.sender AS MessageFrom """, ) @@ -67,7 +67,7 @@ class Query: key="MessageFrom", cypher=""" MATCH (m:message) - WITH m.from AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) OR drasi.trueLater( LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), diff --git a/tutorials/getting-started/scripts/add-message.ps1 b/tutorials/getting-started/scripts/add-message.ps1 deleted file mode 100644 index 4332b18..0000000 --- a/tutorials/getting-started/scripts/add-message.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Add Message Script (Windows) -# Inserts a message into the feed with a plain SQL INSERT. Drasi captures the -# change via CDC and the console app prints how the queries react. - -param( - [Parameter(Mandatory = $true)][string]$From, - [Parameter(Mandatory = $true)][string]$Message -) - -$Container = if ($env:POSTGRES_CONTAINER) { $env:POSTGRES_CONTAINER } else { "getting-started-postgres" } -$Db = if ($env:POSTGRES_DATABASE) { $env:POSTGRES_DATABASE } else { "getting_started" } -$DbUser = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "drasi_user" } - -# Escape single quotes for safe SQL string literals ('' is an escaped quote). -$FromSql = $From -replace "'", "''" -$MessageSql = $Message -replace "'", "''" - -Write-Host "Adding message from '$From': $Message" -$sql = "INSERT INTO message (""from"", message) VALUES ('$FromSql', '$MessageSql') RETURNING messageid, ""from"", message;" -$sql | docker exec -i $Container psql -U $DbUser -d $Db diff --git a/tutorials/getting-started/scripts/add-message.sh b/tutorials/getting-started/scripts/add-message.sh deleted file mode 100755 index 8081c03..0000000 --- a/tutorials/getting-started/scripts/add-message.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Add Message Script -# Inserts a message into the feed with a plain SQL INSERT. Drasi's PostgreSQL -# source captures the change via CDC and the console app prints how the queries -# react. There is no application code in the loop -- just SQL. - -set -e - -CONTAINER="${POSTGRES_CONTAINER:-getting-started-postgres}" -DB="${POSTGRES_DATABASE:-getting_started}" -DB_USER="${POSTGRES_USER:-drasi_user}" - -FROM_NAME="${1:-}" -MESSAGE_TEXT="${2:-}" - -if [ -z "$FROM_NAME" ] || [ -z "$MESSAGE_TEXT" ]; then - echo "Usage: $0 <from> <message>" - echo - echo "Examples:" - echo " $0 'Alice' 'Hello World' # matches the hello-world-from query" - echo " $0 'Bob' 'Goodbye World' # updates message-count" - exit 1 -fi - -# Escape single quotes for safe SQL string literals ('' is an escaped quote). -FROM_SQL="${FROM_NAME//\'/\'\'}" -MESSAGE_SQL="${MESSAGE_TEXT//\'/\'\'}" - -if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then - echo "Error: the ${CONTAINER} container is not running." - echo "Run ./scripts/setup-database.sh first." - exit 1 -fi - -echo "Adding message from '$FROM_NAME': $MESSAGE_TEXT" -docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB" -c \ - "INSERT INTO message (\"from\", message) VALUES ('$FROM_SQL', '$MESSAGE_SQL') RETURNING messageid, \"from\", message;" diff --git a/tutorials/getting-started/scripts/delete-message.ps1 b/tutorials/getting-started/scripts/delete-message.ps1 deleted file mode 100644 index f93a696..0000000 --- a/tutorials/getting-started/scripts/delete-message.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Delete Message Script (Windows) -# Deletes a message by id with a plain SQL DELETE. Drasi captures the change via -# CDC and the console app prints how the queries react. - -param( - [Parameter(Mandatory = $true)][int]$MessageId -) - -$Container = if ($env:POSTGRES_CONTAINER) { $env:POSTGRES_CONTAINER } else { "getting-started-postgres" } -$Db = if ($env:POSTGRES_DATABASE) { $env:POSTGRES_DATABASE } else { "getting_started" } -$DbUser = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "drasi_user" } - -Write-Host "Deleting message $MessageId" -$sql = "DELETE FROM message WHERE messageid=$MessageId RETURNING messageid, ""from"", message;" -$sql | docker exec -i $Container psql -U $DbUser -d $Db diff --git a/tutorials/getting-started/scripts/delete-message.sh b/tutorials/getting-started/scripts/delete-message.sh deleted file mode 100755 index a25a620..0000000 --- a/tutorials/getting-started/scripts/delete-message.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Delete Message Script -# Deletes a message by id with a plain SQL DELETE. Drasi captures the change via -# CDC and the console app prints how the queries react. - -set -e - -CONTAINER="${POSTGRES_CONTAINER:-getting-started-postgres}" -DB="${POSTGRES_DATABASE:-getting_started}" -DB_USER="${POSTGRES_USER:-drasi_user}" - -MESSAGE_ID="${1:-}" - -if [ -z "$MESSAGE_ID" ]; then - echo "Usage: $0 <messageid>" - echo - echo "Tip: run 'docker exec $CONTAINER psql -U $DB_USER -d $DB -c \"SELECT messageid, \\\"from\\\", message FROM message;\"' to list ids." - exit 1 -fi - -if ! printf '%s' "$MESSAGE_ID" | grep -Eq '^[0-9]+$'; then - echo "Error: messageid must be an integer (got '$MESSAGE_ID')." - exit 1 -fi - -if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then - echo "Error: the ${CONTAINER} container is not running." - echo "Run ./scripts/setup-database.sh first." - exit 1 -fi - -echo "Deleting message $MESSAGE_ID" -docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB" -c \ - "DELETE FROM message WHERE messageid=$MESSAGE_ID RETURNING messageid, \"from\", message;" diff --git a/tutorials/getting-started/scripts/setup-database.ps1 b/tutorials/getting-started/scripts/setup-database.ps1 index 673fa00..c222c85 100644 --- a/tutorials/getting-started/scripts/setup-database.ps1 +++ b/tutorials/getting-started/scripts/setup-database.ps1 @@ -60,7 +60,7 @@ try { Write-Host "" Write-Host "Seeded messages:" - 'SELECT messageid, "from", message FROM message ORDER BY messageid;' | ` + 'SELECT messageid, sender, message FROM message ORDER BY messageid;' | ` docker exec -i getting-started-postgres psql -U drasi_user -d getting_started Write-Host "" diff --git a/tutorials/getting-started/scripts/setup-database.sh b/tutorials/getting-started/scripts/setup-database.sh index 4dd6b1e..6c83cfb 100755 --- a/tutorials/getting-started/scripts/setup-database.sh +++ b/tutorials/getting-started/scripts/setup-database.sh @@ -75,7 +75,7 @@ docker exec -i getting-started-postgres \ echo echo "Seeded messages:" docker exec getting-started-postgres psql -U drasi_user -d getting_started -c \ - "SELECT messageid, \"from\", message FROM message ORDER BY messageid;" + "SELECT messageid, sender, message FROM message ORDER BY messageid;" echo echo "=== Database setup complete! ===" diff --git a/tutorials/getting-started/scripts/start-demo.ps1 b/tutorials/getting-started/scripts/start-demo.ps1 index 46c648f..9c5c6f3 100644 --- a/tutorials/getting-started/scripts/start-demo.ps1 +++ b/tutorials/getting-started/scripts/start-demo.ps1 @@ -15,7 +15,7 @@ # Start Demo Script (Windows) # Starts (and seeds) the database, then runs the console app in the foreground. # Leave it running in this terminal; use a second terminal to add and delete -# messages with scripts/add-message.ps1 and scripts/delete-message.ps1. Press +# messages with SQL (docker exec ... psql), as shown in the tutorial. Press # Ctrl+C to stop, then run scripts/cleanup.ps1. $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path diff --git a/tutorials/getting-started/scripts/start-demo.sh b/tutorials/getting-started/scripts/start-demo.sh index 0ae4c0c..3f223a9 100755 --- a/tutorials/getting-started/scripts/start-demo.sh +++ b/tutorials/getting-started/scripts/start-demo.sh @@ -17,7 +17,7 @@ # Starts (and seeds) the database, then runs the console app in the foreground. # The app embeds the Drasi engine and prints query changes. Leave it running in # this terminal (Terminal 1); use a second terminal to add and delete messages -# with scripts/add-message.sh and scripts/delete-message.sh. Press Ctrl+C to +# with SQL (docker exec ... psql), as shown in the tutorial. Press Ctrl+C to # stop, then run ./scripts/cleanup.sh. set -e From 3173893e7ad5b4f4b062aa3d8b76801f605d033a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:04:23 -0700 Subject: [PATCH 09/19] Use structural pattern matching in the Python reactions Replace the if/elif chains in the reaction callbacks with match/case, which reads the diff's type and destructures its payload in one step. Handle the real diff shapes carefully: aggregation changes (e.g. a count) arrive with type "aggregation" and a null `data`, carrying the row in `after`, and every diff always has data/before/after keys (null where N/A). So the printing reaction matches "UPDATE" | "aggregation" on before/after, and the keyed-store reactions use dict() patterns (after-then-data) so a null payload key never wins. Verified equivalent to the previous logic across all shapes, and end-to-end: message-count aggregation prints, and building-comfort's avg roll-ups update without error. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/building-comfort/README.md | 12 +++++++----- tutorials/building-comfort/_index.md | 12 +++++++----- tutorials/building-comfort/demo/engine.py | 15 +++++++-------- tutorials/curbside-pickup/README.md | 12 +++++++----- tutorials/curbside-pickup/_index.md | 12 +++++++----- tutorials/curbside-pickup/demo/engine.py | 15 +++++++-------- tutorials/getting-started/README.md | 17 +++++++++-------- tutorials/getting-started/_index.md | 17 +++++++++-------- tutorials/getting-started/app.py | 13 +++++++------ 9 files changed, 67 insertions(+), 58 deletions(-) diff --git a/tutorials/building-comfort/README.md b/tutorials/building-comfort/README.md index 61006a4..553d977 100644 --- a/tutorials/building-comfort/README.md +++ b/tutorials/building-comfort/README.md @@ -259,11 +259,13 @@ def on_results(event): with lock: store = snapshot[query.id] # this query's rows, keyed by id; the UI reads it for diff in event["results"]: - row = diff.get("data") or diff.get("after") - if diff["type"] == "DELETE": - store.pop(row[query.key], None) - else: # ADD or UPDATE: upsert by row identity - store[row[query.key]] = row + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[query.key], None) + case {"after": dict() as row} | { + "data": dict() as row + }: # ADD / UPDATE / aggregation + store[row[query.key]] = row await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) diff --git a/tutorials/building-comfort/_index.md b/tutorials/building-comfort/_index.md index 74a8f3f..a428158 100644 --- a/tutorials/building-comfort/_index.md +++ b/tutorials/building-comfort/_index.md @@ -293,11 +293,13 @@ def on_results(event): with lock: store = snapshot[query.id] # this query's rows, keyed by id; the UI reads it for diff in event["results"]: - row = diff.get("data") or diff.get("after") - if diff["type"] == "DELETE": - store.pop(row[query.key], None) - else: # ADD or UPDATE: upsert by row identity - store[row[query.key]] = row + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[query.key], None) + case {"after": dict() as row} | { + "data": dict() as row + }: # ADD / UPDATE / aggregation + store[row[query.key]] = row await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) diff --git a/tutorials/building-comfort/demo/engine.py b/tutorials/building-comfort/demo/engine.py index 26431a2..a323b71 100644 --- a/tutorials/building-comfort/demo/engine.py +++ b/tutorials/building-comfort/demo/engine.py @@ -153,14 +153,13 @@ def _on_results(self, event: QueryResultEvent) -> None: with self._lock: store = self._results.setdefault(query_id, {}) for diff in event["results"]: - row = diff.get("data") or diff.get("after") - if row is None: - continue - key = row.get(key_field) - if diff["type"] == "DELETE": - store.pop(key, None) - else: - store[key] = row + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key_field], None) + case {"after": dict() as row} | {"data": dict() as row}: + # ADD / UPDATE / aggregation: `after` is the new state + # (aggregation diffs carry a null `data`), `data` covers ADD. + store[row[key_field]] = row self._version += 1 async def _load_room_ids(self) -> list[str]: diff --git a/tutorials/curbside-pickup/README.md b/tutorials/curbside-pickup/README.md index 5ee28fc..db55907 100644 --- a/tutorials/curbside-pickup/README.md +++ b/tutorials/curbside-pickup/README.md @@ -240,11 +240,13 @@ def on_results(event): with lock: store = snapshot[query.id] # this query's rows, keyed by id for diff in event["results"]: - row = diff.get("data") or diff.get("after") - if diff["type"] == "DELETE": - store.pop(row[query.key], None) - else: # ADD or UPDATE: upsert by row identity - store[row[query.key]] = row + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[query.key], None) + case {"after": dict() as row} | { + "data": dict() as row + }: # ADD / UPDATE / aggregation + store[row[query.key]] = row await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) diff --git a/tutorials/curbside-pickup/_index.md b/tutorials/curbside-pickup/_index.md index ea6751d..1f48235 100644 --- a/tutorials/curbside-pickup/_index.md +++ b/tutorials/curbside-pickup/_index.md @@ -274,11 +274,13 @@ def on_results(event): with lock: store = snapshot[query.id] # this query's rows, keyed by id for diff in event["results"]: - row = diff.get("data") or diff.get("after") - if diff["type"] == "DELETE": - store.pop(row[query.key], None) - else: # ADD or UPDATE: upsert by row identity - store[row[query.key]] = row + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[query.key], None) + case {"after": dict() as row} | { + "data": dict() as row + }: # ADD / UPDATE / aggregation + store[row[query.key]] = row await drasi.add_python_reaction("ui", [q.id for q in QUERIES], on_results) diff --git a/tutorials/curbside-pickup/demo/engine.py b/tutorials/curbside-pickup/demo/engine.py index 93ce71f..f49417f 100644 --- a/tutorials/curbside-pickup/demo/engine.py +++ b/tutorials/curbside-pickup/demo/engine.py @@ -147,14 +147,13 @@ def _on_results(self, event: QueryResultEvent) -> None: with self._lock: store = self._results.setdefault(query_id, {}) for diff in event["results"]: - row = diff.get("data") or diff.get("after") - if row is None: - continue - key = row.get(key_field) - if diff["type"] == "DELETE": - store.pop(key, None) - else: - store[key] = row + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key_field], None) + case {"after": dict() as row} | {"data": dict() as row}: + # ADD / UPDATE / aggregation: `after` is the new state + # (aggregation diffs carry a null `data`), `data` covers ADD. + store[row[key_field]] = row self._version += 1 # -- readiness ----------------------------------------------------------- diff --git a/tutorials/getting-started/README.md b/tutorials/getting-started/README.md index 5ae81ff..d8261df 100644 --- a/tutorials/getting-started/README.md +++ b/tutorials/getting-started/README.md @@ -214,24 +214,25 @@ RETURN MessageFrom, LastMessageTimestamp ### The Python Reaction -Instead of a dashboard or an SSE stream, a single **Python reaction** subscribed to all three queries prints each change to the console: +Instead of a dashboard or an SSE stream, a single **Python reaction** subscribed to all three queries prints each change to the console. Structural pattern matching reads the diff's `type` and pulls out the payload it carries in one step: ```python def on_change(event): query_id = event["query_id"] for diff in event["results"]: - if diff["type"] == "ADD": - print(f"[{query_id}] + {diff['data']}") - elif diff["type"] == "DELETE": - print(f"[{query_id}] - {diff['data']}") - else: # UPDATE - print(f"[{query_id}] ~ {diff['before']} -> {diff['after']}") + match diff: + case {"type": "ADD", "data": data}: + print(f"[{query_id}] + {data}") + case {"type": "DELETE", "data": data}: + print(f"[{query_id}] - {data}") + case {"type": "UPDATE" | "aggregation", "before": before, "after": after}: + print(f"[{query_id}] ~ {before} -> {after}") await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) ``` -`type` tells you which of `data`, or `before` and `after`, a diff carries. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. +An `ADD` and a `DELETE` carry `data`; an `UPDATE` โ€” and an `aggregation` change, like a `count` ticking up โ€” carries `before` and `after`. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. ## Clean Up When you're finished, stop the app with **Ctrl+C** in Terminal 1, then remove the database container: diff --git a/tutorials/getting-started/_index.md b/tutorials/getting-started/_index.md index 759dc82..9b53208 100644 --- a/tutorials/getting-started/_index.md +++ b/tutorials/getting-started/_index.md @@ -248,24 +248,25 @@ RETURN MessageFrom, LastMessageTimestamp ### The Python Reaction -Instead of a dashboard or an SSE stream, a single **Python reaction** subscribed to all three queries prints each change to the console: +Instead of a dashboard or an SSE stream, a single **Python reaction** subscribed to all three queries prints each change to the console. Structural pattern matching reads the diff's `type` and pulls out the payload it carries in one step: ```python def on_change(event): query_id = event["query_id"] for diff in event["results"]: - if diff["type"] == "ADD": - print(f"[{query_id}] + {diff['data']}") - elif diff["type"] == "DELETE": - print(f"[{query_id}] - {diff['data']}") - else: # UPDATE - print(f"[{query_id}] ~ {diff['before']} -> {diff['after']}") + match diff: + case {"type": "ADD", "data": data}: + print(f"[{query_id}] + {data}") + case {"type": "DELETE", "data": data}: + print(f"[{query_id}] - {data}") + case {"type": "UPDATE" | "aggregation", "before": before, "after": after}: + print(f"[{query_id}] ~ {before} -> {after}") await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) ``` -`type` tells you which of `data`, or `before` and `after`, a diff carries. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. +An `ADD` and a `DELETE` carry `data`; an `UPDATE` โ€” and an `aggregation` change, like a `count` ticking up โ€” carries `before` and `after`. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. ## Clean Up {#cleanup} diff --git a/tutorials/getting-started/app.py b/tutorials/getting-started/app.py index 8c375a5..a6e9900 100644 --- a/tutorials/getting-started/app.py +++ b/tutorials/getting-started/app.py @@ -65,12 +65,13 @@ def on_change(event: QueryResultEvent) -> None: """Print each way a query's result set changed (the console 'reaction').""" query_id = event["query_id"] for diff in event["results"]: - if diff["type"] == "ADD": - print(f"[{_stamp()}] [{query_id}] + {_row(diff['data'])}") - elif diff["type"] == "DELETE": - print(f"[{_stamp()}] [{query_id}] - {_row(diff['data'])}") - else: # UPDATE - print(f"[{_stamp()}] [{query_id}] ~ {_row(diff['before'])} -> {_row(diff['after'])}") + match diff: + case {"type": "ADD", "data": data}: + print(f"[{_stamp()}] [{query_id}] + {_row(data)}") + case {"type": "DELETE", "data": data}: + print(f"[{_stamp()}] [{query_id}] - {_row(data)}") + case {"type": "UPDATE" | "aggregation", "before": before, "after": after}: + print(f"[{_stamp()}] [{query_id}] ~ {_row(before)} -> {_row(after)}") async def main() -> None: From c86bff91ed68a46d0821a9416ec2dd0b204547aa Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:21:39 -0700 Subject: [PATCH 10/19] Make the code-block copy button always visible The docs already add Docsy's click-to-copy button to every code block, but the project styles set it to opacity:0 (visible only when hovering the block), so it was easy to miss. Show it at rest (opacity 0.7, with a subtle border/background) and at full strength on hover. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- website/assets/scss/_styles_project.scss | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/website/assets/scss/_styles_project.scss b/website/assets/scss/_styles_project.scss index 0159285..b7f8a34 100644 --- a/website/assets/scss/_styles_project.scss +++ b/website/assets/scss/_styles_project.scss @@ -278,7 +278,8 @@ pre.chroma, // ============================================================================= // CODE BLOCK COPY BUTTON -// Ensures Docsy's click-to-copy button is visible on dark code blocks +// Docsy's click-to-copy button, made always visible (not hover-only) on the +// dark code blocks so it is easy to discover. // ============================================================================= .td-content .highlight { position: relative; @@ -296,18 +297,19 @@ pre.chroma, right: 8px; top: 8px; padding: 4px 8px; - border: none; + border: 1px solid rgba(255, 255, 255, 0.18); border-radius: $border-radius-sm; - background-color: rgba(255, 255, 255, 0.1); - color: rgba(255, 255, 255, 0.6); + background-color: rgba(255, 255, 255, 0.12); + color: rgba(255, 255, 255, 0.75); cursor: pointer; - opacity: 0; + opacity: 0.7; transition: opacity $transition-fast, background-color $transition-fast, color $transition-fast; z-index: 10; &:hover { - background-color: rgba(255, 255, 255, 0.2); - color: rgba(255, 255, 255, 0.9); + opacity: 1; + background-color: rgba(255, 255, 255, 0.22); + color: rgba(255, 255, 255, 0.95); } &:active { @@ -316,6 +318,7 @@ pre.chroma, } } + // Full strength when the reader is hovering the block. &:hover pre button.td-click-to-copy { opacity: 1; } From 9f228da25bf0fab3b6c4fa048708b6d38b1e033b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:21:51 -0700 Subject: [PATCH 11/19] Add getting-started devcontainer lock file Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../getting-started/devcontainer-lock.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .devcontainer/getting-started/devcontainer-lock.json diff --git a/.devcontainer/getting-started/devcontainer-lock.json b/.devcontainer/getting-started/devcontainer-lock.json new file mode 100644 index 0000000..895e5a2 --- /dev/null +++ b/.devcontainer/getting-started/devcontainer-lock.json @@ -0,0 +1,24 @@ +{ + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.9", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a", + "integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.17.0", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c", + "integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.8", + "resolved": "ghcr.io/devcontainers/features/git@sha256:fd75977de13a9979000e0e78baf949adb0ca71d2398995fa22e0a36d7e7e7fe2", + "integrity": "sha256:fd75977de13a9979000e0e78baf949adb0ca71d2398995fa22e0a36d7e7e7fe2" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "1.8.0", + "resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511", + "integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511" + } + } +} From 43e83e4b0b49b036a64d3f616b43b3add739fd91 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:32:49 -0700 Subject: [PATCH 12/19] Consolidate Getting Started into a single app.py Inline the Postgres source config and the three queries directly in app.py and drop the demo/ package, so the whole console demo is one self-contained file. Add a "Putting It All Together" section to the tutorial that shows the complete program. Verified end-to-end: bootstrap, the Hello World filter, and the count aggregation all react as before. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/getting-started/README.md | 104 +++++++++++++++++++- tutorials/getting-started/_index.md | 105 ++++++++++++++++++++- tutorials/getting-started/app.py | 99 ++++++++++++++----- tutorials/getting-started/demo/__init__.py | 15 --- tutorials/getting-started/demo/config.py | 56 ----------- tutorials/getting-started/demo/queries.py | 85 ----------------- 6 files changed, 281 insertions(+), 183 deletions(-) delete mode 100644 tutorials/getting-started/demo/__init__.py delete mode 100644 tutorials/getting-started/demo/config.py delete mode 100644 tutorials/getting-started/demo/queries.py diff --git a/tutorials/getting-started/README.md b/tutorials/getting-started/README.md index d8261df..2958a5b 100644 --- a/tutorials/getting-started/README.md +++ b/tutorials/getting-started/README.md @@ -150,7 +150,7 @@ docker exec getting-started-postgres psql -U drasi_user -d getting_started \ `hello-world-from` drops that row and `message-count` decrements โ€” a row leaving a result set is a change Drasi reports too. ## How It Works -The whole demo is a small Python program in `tutorials/getting-started/`: `app.py` runs the engine and prints, and `demo/` holds the queries and configuration. +The whole demo is a single file, `tutorials/getting-started/app.py`. Here's what each part does; the [complete file](#putting-it-all-together) is at the end. ### The Source @@ -229,11 +229,111 @@ def on_change(event): print(f"[{query_id}] ~ {before} -> {after}") -await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) +await drasi.add_python_reaction("console", list(QUERIES), on_change) ``` An `ADD` and a `DELETE` carry `data`; an `UPDATE` โ€” and an `aggregation` change, like a `count` ticking up โ€” carries `before` and `after`. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. +## Putting It All Together +The sections above walked through the program one piece at a time. Here is the complete `app.py` that `python app.py` actually runs โ€” the source, the three queries, the reaction, and the loop, in a single file: + +```python +import asyncio +import os +import sys +import time + +os.environ.setdefault("RUST_LOG", "warn") # quiet the engine's INFO logging + +from drasi import Drasi + +# --- The source: the tutorial's PostgreSQL database --- +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5752")), + "database": os.environ.get("POSTGRES_DATABASE", "getting_started"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} +SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.message"], + "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], + "slotName": "drasi_getting_started_slot", + "publicationName": "drasi_getting_started_pub", +} +BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} + +# --- The three continuous queries --- +QUERIES = { + "hello-world-from": """ + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.sender AS MessageFrom + """, + "message-count": """ + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, + "inactive-people": """ + MATCH (m:message) + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, +} + + +def row(r): + return " ".join(f"{k}={v!r}" for k, v in r.items()) + + +# --- The reaction: print each result-set change --- +def on_change(event): + query_id = event["query_id"] + for diff in event["results"]: + match diff: + case {"type": "ADD", "data": data}: + print(f"[{query_id}] + {row(data)}") + case {"type": "DELETE", "data": data}: + print(f"[{query_id}] - {row(data)}") + case {"type": "UPDATE" | "aggregation", "before": before, "after": after}: + print(f"[{query_id}] ~ {row(before)} -> {row(after)}") + + +async def main(): + sys.stdout.reconfigure(line_buffering=True) + async with await Drasi.create("getting-started") as drasi: + await drasi.install_plugin("source/postgres") + await drasi.install_plugin("bootstrap/postgres") + await drasi.start() + + await drasi.add_source("postgres", "messages", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + for query_id, cypher in QUERIES.items(): + await drasi.add_query(query_id, cypher, ["messages"]) + for query_id in QUERIES: + await drasi.wait_for_query(query_id, timeout=60) + + print("=== Initial results ===") + for query_id in QUERIES: + for r in await drasi.get_query_results(query_id): + print(f"[{query_id}] {row(r)}") + + await drasi.add_python_reaction("console", list(QUERIES), on_change) + print("=== Watching for changes (Ctrl+C to stop) ===") + await asyncio.Event().wait() + + +asyncio.run(main()) +``` + +That's the whole thing: a live view over a database in one file, with no polling and no UI. + ## Clean Up When you're finished, stop the app with **Ctrl+C** in Terminal 1, then remove the database container: diff --git a/tutorials/getting-started/_index.md b/tutorials/getting-started/_index.md index 9b53208..b221f11 100644 --- a/tutorials/getting-started/_index.md +++ b/tutorials/getting-started/_index.md @@ -184,7 +184,7 @@ docker exec getting-started-postgres psql -U drasi_user -d getting_started \ ## How It Works {#how} -The whole demo is a small Python program in `tutorials/getting-started/`: `app.py` runs the engine and prints, and `demo/` holds the queries and configuration. +The whole demo is a single file, `tutorials/getting-started/app.py`. Here's what each part does; the [complete file](#all) is at the end. ### The Source @@ -263,11 +263,112 @@ def on_change(event): print(f"[{query_id}] ~ {before} -> {after}") -await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) +await drasi.add_python_reaction("console", list(QUERIES), on_change) ``` An `ADD` and a `DELETE` carry `data`; an `UPDATE` โ€” and an `aggregation` change, like a `count` ticking up โ€” carries `before` and `after`. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. +## Putting It All Together {#all} + +The sections above walked through the program one piece at a time. Here is the complete `app.py` that `python app.py` actually runs โ€” the source, the three queries, the reaction, and the loop, in a single file: + +```python +import asyncio +import os +import sys +import time + +os.environ.setdefault("RUST_LOG", "warn") # quiet the engine's INFO logging + +from drasi import Drasi + +# --- The source: the tutorial's PostgreSQL database --- +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5752")), + "database": os.environ.get("POSTGRES_DATABASE", "getting_started"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} +SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.message"], + "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], + "slotName": "drasi_getting_started_slot", + "publicationName": "drasi_getting_started_pub", +} +BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} + +# --- The three continuous queries --- +QUERIES = { + "hello-world-from": """ + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.sender AS MessageFrom + """, + "message-count": """ + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, + "inactive-people": """ + MATCH (m:message) + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, +} + + +def row(r): + return " ".join(f"{k}={v!r}" for k, v in r.items()) + + +# --- The reaction: print each result-set change --- +def on_change(event): + query_id = event["query_id"] + for diff in event["results"]: + match diff: + case {"type": "ADD", "data": data}: + print(f"[{query_id}] + {row(data)}") + case {"type": "DELETE", "data": data}: + print(f"[{query_id}] - {row(data)}") + case {"type": "UPDATE" | "aggregation", "before": before, "after": after}: + print(f"[{query_id}] ~ {row(before)} -> {row(after)}") + + +async def main(): + sys.stdout.reconfigure(line_buffering=True) + async with await Drasi.create("getting-started") as drasi: + await drasi.install_plugin("source/postgres") + await drasi.install_plugin("bootstrap/postgres") + await drasi.start() + + await drasi.add_source("postgres", "messages", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + for query_id, cypher in QUERIES.items(): + await drasi.add_query(query_id, cypher, ["messages"]) + for query_id in QUERIES: + await drasi.wait_for_query(query_id, timeout=60) + + print("=== Initial results ===") + for query_id in QUERIES: + for r in await drasi.get_query_results(query_id): + print(f"[{query_id}] {row(r)}") + + await drasi.add_python_reaction("console", list(QUERIES), on_change) + print("=== Watching for changes (Ctrl+C to stop) ===") + await asyncio.Event().wait() + + +asyncio.run(main()) +``` + +That's the whole thing: a live view over a database in one file, with no polling and no UI. + ## Clean Up {#cleanup} When you're finished, stop the app with **Ctrl+C** in Terminal 1, then remove the database container: diff --git a/tutorials/getting-started/app.py b/tutorials/getting-started/app.py index a6e9900..c212270 100644 --- a/tutorials/getting-started/app.py +++ b/tutorials/getting-started/app.py @@ -18,12 +18,12 @@ python app.py -It connects Drasi's PostgreSQL source to the tutorial's `message` table, starts -three continuous queries, and registers a single **Python reaction** that prints -each result-set change to the console. Then it stays running and watches. There -is no UI and no prompt: you drive changes by running SQL directly against the -database (with `docker exec ... psql`, shown in the tutorial) and watch the -queries react here in real time. Press Ctrl+C to stop. +The whole demo is this one file. It connects Drasi's PostgreSQL source to the +tutorial's `message` table, starts three continuous queries, and registers a +single Python reaction that prints each result-set change to the console. Then +it stays running and watches. There is no UI and no prompt: you drive changes by +running SQL directly against the database (with `docker exec ... psql`, shown in +the tutorial) and watch the queries react here in real time. Press Ctrl+C to stop. """ from __future__ import annotations @@ -40,17 +40,70 @@ import time # noqa: E402 from typing import Any # noqa: E402 -from demo.config import ( # noqa: E402 - BOOTSTRAP_CONFIG, - INACTIVITY_SECONDS, - SOURCE_CONFIG, - SOURCE_ID, -) -from demo.queries import QUERIES # noqa: E402 - from drasi import Drasi # noqa: E402 from drasi.types import QueryResultEvent # noqa: E402 +# --- The source ------------------------------------------------------------- +# Connection settings for the tutorial's PostgreSQL database, read from the +# environment with the same defaults the database uses. +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5752")), + "database": os.environ.get("POSTGRES_DATABASE", "getting_started"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} + +# The Postgres source streams changes from the `message` table via logical +# replication (CDC). The table is schema-qualified here (public.message) while +# tableKeys uses the bare name; the node label Drasi sees is the bare name, so +# the Cypher matches (m:message). +SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.message"], + "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], + "slotName": "drasi_getting_started_slot", + "publicationName": "drasi_getting_started_pub", +} +# The bootstrap provider loads the rows that already exist when a query starts. +BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} + +# --- The three continuous queries ------------------------------------------- +# They build up in complexity: a filter, an aggregation, and a temporal query +# that detects the *absence* of change. +QUERIES = { + # Filter: messages whose text is exactly "Hello World", and who sent them. + "hello-world-from": """ + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.sender AS MessageFrom + """, + # Aggregation: how many times each distinct message has been sent. + "message-count": """ + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, + # Absence of change: senders who have not sent a message in the last 20 + # seconds. drasi.trueLater schedules a future re-evaluation so a sender + # appears the instant they cross the threshold, not only when some other + # change happens. + "inactive-people": """ + MATCH (m:message) + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, +} + +# How long a sender can be quiet before inactive-people flags them (matches the +# duration in the query). Shown in the startup banner. +INACTIVITY_SECONDS = 20 + def _row(row: dict[str, Any]) -> str: """Render a result row as a compact, readable `key=value` string.""" @@ -85,23 +138,23 @@ async def main() -> None: await drasi.install_plugin("bootstrap/postgres") await drasi.start() - await drasi.add_source("postgres", SOURCE_ID, SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + await drasi.add_source("postgres", "messages", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) - for query in QUERIES: - await drasi.add_query(query.id, query.cypher, [SOURCE_ID]) - for query in QUERIES: - await drasi.wait_for_query(query.id, timeout=60) + for query_id, cypher in QUERIES.items(): + await drasi.add_query(query_id, cypher, ["messages"]) + for query_id in QUERIES: + await drasi.wait_for_query(query_id, timeout=60) # Show the state each query bootstrapped from the existing rows. print("\n=== Initial results ===") - for query in QUERIES: - rows = await drasi.get_query_results(query.id) - print(f"\n[{query.id}] {len(rows)} row(s):") + for query_id in QUERIES: + rows = await drasi.get_query_results(query_id) + print(f"\n[{query_id}] {len(rows)} row(s):") for row in rows: print(f" {_row(row)}") # From here on, print changes as they arrive. - await drasi.add_python_reaction("console", [q.id for q in QUERIES], on_change) + await drasi.add_python_reaction("console", list(QUERIES), on_change) print( f"\n=== Watching for changes (Ctrl+C to stop) ===\n" diff --git a/tutorials/getting-started/demo/__init__.py b/tutorials/getting-started/demo/__init__.py deleted file mode 100644 index c305d38..0000000 --- a/tutorials/getting-started/demo/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The Getting Started demo package: the tutorial's queries and configuration.""" diff --git a/tutorials/getting-started/demo/config.py b/tutorials/getting-started/demo/config.py deleted file mode 100644 index 8900a00..0000000 --- a/tutorials/getting-started/demo/config.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Configuration for the Getting Started tutorial. - -Just enough to point Drasi's PostgreSQL source at the tutorial database, read -from the environment with the same defaults the database uses. The app only -reads and reacts -- it never writes -- so there is no database client here; you -drive changes with SQL run directly against the database. -""" - -from __future__ import annotations - -import os - -POSTGRES = { - "host": os.environ.get("POSTGRES_HOST", "localhost"), - "port": int(os.environ.get("POSTGRES_PORT", "5752")), - "database": os.environ.get("POSTGRES_DATABASE", "getting_started"), - "user": os.environ.get("POSTGRES_USER", "drasi_user"), - "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), -} - -# The Postgres source creates its own replication slot on startup and reuses the -# publication created by database/init.sql. The table is schema-qualified here -# (public.message) while tableKeys uses the bare name; the node label Drasi sees -# is the bare name, so the Cypher matches (m:message). -SOURCE_CONFIG = { - **POSTGRES, - "sslMode": "prefer", - "tables": ["public.message"], - "tableKeys": [{"table": "message", "keyColumns": ["messageid"]}], - "slotName": "drasi_getting_started_slot", - "publicationName": "drasi_getting_started_pub", -} - -# The bootstrap provider loads the rows that already exist when the query starts. -BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} - -# The id of the single source the queries read from. -SOURCE_ID = "messages" - -# How long a sender can be quiet before inactive-people flags them (matches the -# duration in the query). Shown in the app's startup banner. -INACTIVITY_SECONDS = 20 diff --git a/tutorials/getting-started/demo/queries.py b/tutorials/getting-started/demo/queries.py deleted file mode 100644 index c445584..0000000 --- a/tutorials/getting-started/demo/queries.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The three continuous queries that power the demo. - -They build up in complexity: a filter, an aggregation, and a temporal query that -detects the *absence* of change. Each is written out in full so you can read -exactly what Drasi runs. -""" - -from __future__ import annotations - -from dataclasses import dataclass - -# Query ids, used to register the queries and to label their console output. -HELLO_WORLD_FROM = "hello-world-from" -MESSAGE_COUNT = "message-count" -INACTIVE_PEOPLE = "inactive-people" - - -@dataclass(frozen=True) -class Query: - """One continuous query: how to register it and how to index its rows.""" - - id: str - key: str # the RETURN column that identifies each row (its primary key) - cypher: str - - -# Filter: messages whose text is exactly "Hello World", with who sent them. -HELLO_WORLD_FROM_QUERY = Query( - id=HELLO_WORLD_FROM, - key="MessageId", - cypher=""" - MATCH (m:message) - WHERE m.message = 'Hello World' - RETURN m.messageid AS MessageId, m.sender AS MessageFrom - """, -) - -# Aggregation: how many times each distinct message has been sent. -MESSAGE_COUNT_QUERY = Query( - id=MESSAGE_COUNT, - key="Message", - cypher=""" - MATCH (m:message) - RETURN m.message AS Message, count(m) AS Frequency - """, -) - -# Temporal: senders who have not sent a message in the last 20 seconds. -# drasi.trueLater schedules a future re-evaluation so a sender appears the instant -# they cross the 20-second threshold, not only when some other change happens. -INACTIVE_PEOPLE_QUERY = Query( - id=INACTIVE_PEOPLE, - key="MessageFrom", - cypher=""" - MATCH (m:message) - WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp - WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) - OR drasi.trueLater( - LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), - LastMessageTimestamp + duration({ seconds: 20 }) - ) - RETURN MessageFrom, LastMessageTimestamp - """, -) - -# The engine registers these in order and subscribes one reaction to them all. -QUERIES = [ - HELLO_WORLD_FROM_QUERY, - MESSAGE_COUNT_QUERY, - INACTIVE_PEOPLE_QUERY, -] From 6f38a1466f10f6036b165a8bb97f651e7874f402 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:37:57 -0700 Subject: [PATCH 13/19] Serve the docs at the root URL in local dev `npm run serve` used a bare `hugo server`, which keeps the config's `/drasi-python/` base path, so http://localhost:1313/ 404s and it collides with other local servers. Pass `--baseURL http://localhost:1313/ --appendPort=false` so local dev serves at the root. Production (`npm run build`, used by CI) is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- website/README.md | 2 +- website/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/README.md b/website/README.md index f570247..af91be1 100644 --- a/website/README.md +++ b/website/README.md @@ -22,7 +22,7 @@ standard build cannot compile the theme's SCSS โ€” and Node for the theme's asse cd website npm install npm --prefix themes/docsy install -npm run serve # http://localhost:1313/drasi-python/ +npm run serve # http://localhost:1313/ ``` To reproduce what CI publishes: diff --git a/website/package.json b/website/package.json index 5255edb..7118ce5 100644 --- a/website/package.json +++ b/website/package.json @@ -12,7 +12,7 @@ "scripts": { "get:theme": "cd themes/docsy && npm install", "build": "hugo --gc --minify", - "serve": "hugo server" + "serve": "hugo server --baseURL http://localhost:1313/ --appendPort=false" }, "devDependencies": { "autoprefixer": "^10.4.22", From f49b84d02d11995ea68e2271de0431498d244a20 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:51:01 -0700 Subject: [PATCH 14/19] Collapse Curbside demo/ into a single engine.py Merge config.py, queries.py and engine.py into one engine.py (the two source configs, the synthetic join, the six queries, and CurbsideEngine), and drop the demo/ package; app.py imports CurbsideEngine and the query ids from it. Add a "Putting It All Together" section to the tutorial showing the assembled engine. Verified end-to-end against real PostgreSQL and MySQL: bootstrap, the cross-DB delivery join, the drasi.trueFor delay, and reset all work. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/curbside-pickup/README.md | 176 ++++++++- tutorials/curbside-pickup/_index.md | 177 ++++++++- tutorials/curbside-pickup/app.py | 6 +- tutorials/curbside-pickup/demo/__init__.py | 19 - tutorials/curbside-pickup/demo/config.py | 109 ----- tutorials/curbside-pickup/demo/engine.py | 221 ----------- tutorials/curbside-pickup/demo/queries.py | 196 --------- tutorials/curbside-pickup/engine.py | 440 +++++++++++++++++++++ 8 files changed, 794 insertions(+), 550 deletions(-) delete mode 100644 tutorials/curbside-pickup/demo/__init__.py delete mode 100644 tutorials/curbside-pickup/demo/config.py delete mode 100644 tutorials/curbside-pickup/demo/engine.py delete mode 100644 tutorials/curbside-pickup/demo/queries.py create mode 100644 tutorials/curbside-pickup/engine.py diff --git a/tutorials/curbside-pickup/README.md b/tutorials/curbside-pickup/README.md index db55907..7a6f527 100644 --- a/tutorials/curbside-pickup/README.md +++ b/tutorials/curbside-pickup/README.md @@ -135,7 +135,7 @@ This is the interesting one. Drasi doesn't poll to find slow orders โ€” the **co Press **Reset everything** in the sidebar to return to the starting state (all orders *preparing*, all vehicles *Parking*). ## How It Works -The whole demo is a single Streamlit program in `tutorials/curbside-pickup/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state and hosts the controls. +The demo is two files in `tutorials/curbside-pickup/`: **`engine.py`** holds the two sources, the six queries, and the `CurbsideEngine` (its Python reaction and the SQL writes); **`app.py`** renders the reaction's state and hosts the controls. The [complete `engine.py`](#putting-it-all-together) is at the end. ### Two Sources @@ -258,6 +258,180 @@ The engine runs on its own asyncio event loop in a background daemon thread, cre <img src="images/curbside-ui.png" width="960" alt="The Curbside Pickup Streamlit UI: four panels of orders and vehicles with inline buttons across the top, Matched and Delayed panels below, and a sidebar with a reset button and a color-coded SQL activity log"> +## Putting It All Together +The sections above walked through the Drasi side one piece at a time. Here it is assembled into the single `engine.py` โ€” the two sources, the synthetic join, the six queries, and the `CurbsideEngine` that runs them and writes back. `app.py` (the Streamlit UI) imports `CurbsideEngine` and the query ids from it: + +```python +import asyncio +import os +import threading +import time +from collections import deque +from dataclasses import dataclass, field +from typing import Any + +os.environ.setdefault("RUST_LOG", "warn") # quiet the engine's INFO logging + +import psycopg +import pymysql +from drasi import Drasi + +# --- The two sources --- +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5742")), + "database": os.environ.get("POSTGRES_DATABASE", "RetailOperations"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} +POSTGRES_SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.orders"], + "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], + "slotName": "drasi_curbside_slot", + "publicationName": "drasi_curbside_pub", +} + +MYSQL = { + "host": os.environ.get("MYSQL_HOST", "localhost"), + "port": int(os.environ.get("MYSQL_PORT", "3309")), + "database": os.environ.get("MYSQL_DATABASE", "PhysicalOperations"), + "user": os.environ.get("MYSQL_USER", "drasi_user"), + "password": os.environ.get("MYSQL_PASSWORD", "drasi_password"), +} +MYSQL_SOURCE_CONFIG = { + **MYSQL, + "sslMode": "disabled", # the tutorial container has no TLS + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], +} +# Each source also takes a bootstrap config, and the UI writes through a psycopg +# (Postgres) and a PyMySQL (MySQL) connection built from the dicts above. + +RETAIL_OPS, PHYSICAL_OPS = "retail-ops", "physical-ops" + +# The synthetic join: match a vehicle to an order by equal plate, across databases. +PICKUP_BY = { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], +} + + +@dataclass(frozen=True) +class Query: + id: str + key: str # the RETURN column that identifies each row + sources: list[str] + cypher: str + joins: list[dict] = field(default_factory=list) + + +QUERIES = [ + # Four single-source list queries split orders and vehicles by state. Each has + # the same shape; orders-preparing is shown here, and orders-ready / + # vehicles-parking / vehicles-curbside flip the WHERE clause. + Query( + id="orders-preparing", + key="id", + sources=[RETAIL_OPS], + cypher="MATCH (o:orders) WHERE o.status <> 'ready' " + "RETURN o.id AS id, o.customer_name AS customerName, o.plate AS plate", + ), + # delivery: an order is ready AND its driver's vehicle is at the curbside. + Query( + id="delivery", + key="id", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status = 'ready' AND v.location = 'Curbside' + RETURN o.id AS id, o.customer_name AS customerName, o.plate AS vehicleId + """, + ), + # delay: at the curbside over 10s while the order is still not ready. + Query( + id="delay", + key="orderId", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status <> 'ready' + AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) + RETURN o.id AS orderId, o.customer_name AS customerName, + drasi.changeDateTime(v) AS waitingSinceTimestamp + """, + ), +] + + +class CurbsideEngine: + """Runs Drasi over two databases and exposes a thread-safe snapshot.""" + + def __init__(self): + self._lock = threading.Lock() + self._results = {} # query id -> {row key -> row} + self._key_by_id = {q.id: q.key for q in QUERIES} + self._activity = deque(maxlen=20) # recent SQL writes for the sidebar + self._ready = threading.Event() + self._drasi = None + # Run the engine on its own event loop in a background thread. + threading.Thread(target=self._run, daemon=True).start() + + def _run(self): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self._start()) + self._ready.set() + loop.run_forever() + + async def _start(self): + self._drasi = await Drasi.create("curbside-pickup") + for plugin in ("source/postgres", "bootstrap/postgres", "source/mysql", "bootstrap/mysql"): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + await self._drasi.add_source("postgres", RETAIL_OPS, POSTGRES_SOURCE_CONFIG, bootstrap=...) + await self._drasi.add_source("mysql", PHYSICAL_OPS, MYSQL_SOURCE_CONFIG, bootstrap=...) + for q in QUERIES: + await self._drasi.add_query(q.id, q.cypher, q.sources, joins=q.joins or None) + for q in QUERIES: + await self._drasi.wait_for_query(q.id) + rows = await self._drasi.get_query_results(q.id) + self._results[q.id] = {r[q.key]: r for r in rows} + + await self._drasi.add_python_reaction("ui", [q.id for q in QUERIES], self._on_results) + + def _on_results(self, event): + key = self._key_by_id[event["query_id"]] + with self._lock: + store = self._results.setdefault(event["query_id"], {}) + for diff in event["results"]: + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key], None) + case {"after": dict() as row} | {"data": dict() as row}: + store[row[key]] = row + + def snapshot(self): + with self._lock: + return { + "results": {q: list(s.values()) for q, s in self._results.items()}, + "activity": list(self._activity), + } + + # set_order_status(id, status) writes to Postgres, set_vehicle_location(plate, + # location) writes to MySQL, and reset() returns everything to the start. + # Each records the SQL it ran in the activity log. +``` + +That's the whole Drasi side in one file: two databases joined by plate, six continuous queries (one temporal), and a reaction that keeps a snapshot the UI renders and drives. + ## Clean Up When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database containers: diff --git a/tutorials/curbside-pickup/_index.md b/tutorials/curbside-pickup/_index.md index 1f48235..c746593 100644 --- a/tutorials/curbside-pickup/_index.md +++ b/tutorials/curbside-pickup/_index.md @@ -169,7 +169,7 @@ Press **Reset everything** in the sidebar to return to the starting state (all o ## How It Works {#how} -The whole demo is a single Streamlit program in `tutorials/curbside-pickup/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state and hosts the controls. +The demo is two files in `tutorials/curbside-pickup/`: **`engine.py`** holds the two sources, the six queries, and the `CurbsideEngine` (its Python reaction and the SQL writes); **`app.py`** renders the reaction's state and hosts the controls. The [complete `engine.py`](#all) is at the end. ### Two Sources @@ -292,6 +292,181 @@ The engine runs on its own asyncio event loop in a background daemon thread, cre <img src="images/curbside-ui.png" width="960" alt="The Curbside Pickup Streamlit UI: four panels of orders and vehicles with inline buttons across the top, Matched and Delayed panels below, and a sidebar with a reset button and a color-coded SQL activity log"> +## Putting It All Together {#all} + +The sections above walked through the Drasi side one piece at a time. Here it is assembled into the single `engine.py` โ€” the two sources, the synthetic join, the six queries, and the `CurbsideEngine` that runs them and writes back. `app.py` (the Streamlit UI) imports `CurbsideEngine` and the query ids from it: + +```python +import asyncio +import os +import threading +import time +from collections import deque +from dataclasses import dataclass, field +from typing import Any + +os.environ.setdefault("RUST_LOG", "warn") # quiet the engine's INFO logging + +import psycopg +import pymysql +from drasi import Drasi + +# --- The two sources --- +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5742")), + "database": os.environ.get("POSTGRES_DATABASE", "RetailOperations"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} +POSTGRES_SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.orders"], + "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], + "slotName": "drasi_curbside_slot", + "publicationName": "drasi_curbside_pub", +} + +MYSQL = { + "host": os.environ.get("MYSQL_HOST", "localhost"), + "port": int(os.environ.get("MYSQL_PORT", "3309")), + "database": os.environ.get("MYSQL_DATABASE", "PhysicalOperations"), + "user": os.environ.get("MYSQL_USER", "drasi_user"), + "password": os.environ.get("MYSQL_PASSWORD", "drasi_password"), +} +MYSQL_SOURCE_CONFIG = { + **MYSQL, + "sslMode": "disabled", # the tutorial container has no TLS + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], +} +# Each source also takes a bootstrap config, and the UI writes through a psycopg +# (Postgres) and a PyMySQL (MySQL) connection built from the dicts above. + +RETAIL_OPS, PHYSICAL_OPS = "retail-ops", "physical-ops" + +# The synthetic join: match a vehicle to an order by equal plate, across databases. +PICKUP_BY = { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], +} + + +@dataclass(frozen=True) +class Query: + id: str + key: str # the RETURN column that identifies each row + sources: list[str] + cypher: str + joins: list[dict] = field(default_factory=list) + + +QUERIES = [ + # Four single-source list queries split orders and vehicles by state. Each has + # the same shape; orders-preparing is shown here, and orders-ready / + # vehicles-parking / vehicles-curbside flip the WHERE clause. + Query( + id="orders-preparing", + key="id", + sources=[RETAIL_OPS], + cypher="MATCH (o:orders) WHERE o.status <> 'ready' " + "RETURN o.id AS id, o.customer_name AS customerName, o.plate AS plate", + ), + # delivery: an order is ready AND its driver's vehicle is at the curbside. + Query( + id="delivery", + key="id", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status = 'ready' AND v.location = 'Curbside' + RETURN o.id AS id, o.customer_name AS customerName, o.plate AS vehicleId + """, + ), + # delay: at the curbside over 10s while the order is still not ready. + Query( + id="delay", + key="orderId", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status <> 'ready' + AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) + RETURN o.id AS orderId, o.customer_name AS customerName, + drasi.changeDateTime(v) AS waitingSinceTimestamp + """, + ), +] + + +class CurbsideEngine: + """Runs Drasi over two databases and exposes a thread-safe snapshot.""" + + def __init__(self): + self._lock = threading.Lock() + self._results = {} # query id -> {row key -> row} + self._key_by_id = {q.id: q.key for q in QUERIES} + self._activity = deque(maxlen=20) # recent SQL writes for the sidebar + self._ready = threading.Event() + self._drasi = None + # Run the engine on its own event loop in a background thread. + threading.Thread(target=self._run, daemon=True).start() + + def _run(self): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self._start()) + self._ready.set() + loop.run_forever() + + async def _start(self): + self._drasi = await Drasi.create("curbside-pickup") + for plugin in ("source/postgres", "bootstrap/postgres", "source/mysql", "bootstrap/mysql"): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + await self._drasi.add_source("postgres", RETAIL_OPS, POSTGRES_SOURCE_CONFIG, bootstrap=...) + await self._drasi.add_source("mysql", PHYSICAL_OPS, MYSQL_SOURCE_CONFIG, bootstrap=...) + for q in QUERIES: + await self._drasi.add_query(q.id, q.cypher, q.sources, joins=q.joins or None) + for q in QUERIES: + await self._drasi.wait_for_query(q.id) + rows = await self._drasi.get_query_results(q.id) + self._results[q.id] = {r[q.key]: r for r in rows} + + await self._drasi.add_python_reaction("ui", [q.id for q in QUERIES], self._on_results) + + def _on_results(self, event): + key = self._key_by_id[event["query_id"]] + with self._lock: + store = self._results.setdefault(event["query_id"], {}) + for diff in event["results"]: + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key], None) + case {"after": dict() as row} | {"data": dict() as row}: + store[row[key]] = row + + def snapshot(self): + with self._lock: + return { + "results": {q: list(s.values()) for q, s in self._results.items()}, + "activity": list(self._activity), + } + + # set_order_status(id, status) writes to Postgres, set_vehicle_location(plate, + # location) writes to MySQL, and reset() returns everything to the start. + # Each records the SQL it ran in the activity log. +``` + +That's the whole Drasi side in one file: two databases joined by plate, six continuous queries (one temporal), and a reaction that keeps a snapshot the UI renders and drives. + ## Clean Up {#cleanup} When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database containers: diff --git a/tutorials/curbside-pickup/app.py b/tutorials/curbside-pickup/app.py index 4212958..8424cf1 100644 --- a/tutorials/curbside-pickup/app.py +++ b/tutorials/curbside-pickup/app.py @@ -30,15 +30,15 @@ from typing import Any import streamlit as st -from demo.config import DELAY_SECONDS -from demo.engine import CurbsideEngine -from demo.queries import ( +from engine import ( DELAY, + DELAY_SECONDS, DELIVERY, ORDERS_PREPARING, ORDERS_READY, VEHICLES_CURBSIDE, VEHICLES_PARKING, + CurbsideEngine, ) from streamlit_autorefresh import st_autorefresh diff --git a/tutorials/curbside-pickup/demo/__init__.py b/tutorials/curbside-pickup/demo/__init__.py deleted file mode 100644 index 28c1c7a..0000000 --- a/tutorials/curbside-pickup/demo/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The Curbside Pickup demo package: an embedded Drasi engine and its queries.""" - -from .engine import CurbsideEngine - -__all__ = ["CurbsideEngine"] diff --git a/tutorials/curbside-pickup/demo/config.py b/tutorials/curbside-pickup/demo/config.py deleted file mode 100644 index a5246c1..0000000 --- a/tutorials/curbside-pickup/demo/config.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Configuration for the Curbside Pickup tutorial. - -Two independent databases: PostgreSQL holds the orders (Retail Operations) and -MySQL holds the vehicles (Physical Operations). This module has the connection -settings for both -- for Drasi's source plugins and for the direct SQL writes the -UI makes (psycopg for Postgres, PyMySQL for MySQL) -- read from the environment -with the same defaults the tutorial's databases use. -""" - -from __future__ import annotations - -import os - -# --- PostgreSQL (orders) ----------------------------------------------------- - -POSTGRES = { - "host": os.environ.get("POSTGRES_HOST", "localhost"), - "port": int(os.environ.get("POSTGRES_PORT", "5742")), - "database": os.environ.get("POSTGRES_DATABASE", "RetailOperations"), - "user": os.environ.get("POSTGRES_USER", "drasi_user"), - "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), -} - -# The Postgres source plugin creates its own replication slot on startup and -# reuses the publication created by database/postgres-init.sql. -POSTGRES_SOURCE_CONFIG = { - **POSTGRES, - "sslMode": "prefer", - "tables": ["public.orders"], - "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], - "slotName": "drasi_curbside_slot", - "publicationName": "drasi_curbside_pub", -} -POSTGRES_BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} - -# psycopg spells the database key ``dbname``; the UI writes order status changes -# through this connection so Drasi observes them via logical replication. -PSYCOPG_CONNECTION = { - "host": POSTGRES["host"], - "port": POSTGRES["port"], - "dbname": POSTGRES["database"], - "user": POSTGRES["user"], - "password": POSTGRES["password"], -} - -# --- MySQL (vehicles) -------------------------------------------------------- - -MYSQL = { - "host": os.environ.get("MYSQL_HOST", "localhost"), - "port": int(os.environ.get("MYSQL_PORT", "3309")), - "database": os.environ.get("MYSQL_DATABASE", "PhysicalOperations"), - "user": os.environ.get("MYSQL_USER", "drasi_user"), - "password": os.environ.get("MYSQL_PASSWORD", "drasi_password"), -} - -# The MySQL source streams the binlog. TLS is disabled on the tutorial container, -# so the source must connect in plaintext. Unlike Postgres, the MySQL bootstrap -# provider takes its own connection settings (and does not accept sslMode). -MYSQL_SOURCE_CONFIG = { - **MYSQL, - "sslMode": "disabled", - "tables": ["vehicles"], - "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], -} -MYSQL_BOOTSTRAP_CONFIG = { - "kind": "mysql", - **MYSQL, - "tables": ["vehicles"], - "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], -} - -# PyMySQL spells the database key ``database``; the UI writes vehicle location -# changes through this connection so Drasi observes them via the binlog. -PYMYSQL_CONNECTION = { - "host": MYSQL["host"], - "port": MYSQL["port"], - "database": MYSQL["database"], - "user": MYSQL["user"], - "password": MYSQL["password"], - "autocommit": True, -} - -# --- Demo constants ---------------------------------------------------------- - -ORDER_PREPARING = "preparing" -ORDER_READY = "ready" -VEHICLE_PARKING = "Parking" -VEHICLE_CURBSIDE = "Curbside" - -# How long (matching the delay query) a curbside vehicle waits on an unready order -# before it is flagged as delayed. Shown in the UI copy. -DELAY_SECONDS = 10 - -# How many recent SQL writes to keep in the activity log. -ACTIVITY_LOG_SIZE = 20 diff --git a/tutorials/curbside-pickup/demo/engine.py b/tutorials/curbside-pickup/demo/engine.py deleted file mode 100644 index f49417f..0000000 --- a/tutorials/curbside-pickup/demo/engine.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The embedded Drasi engine that powers the Streamlit UI. - -The same pattern as the Building Comfort tutorial: the Drasi engine runs on its -own asyncio event loop in a background daemon thread (created once, cached by -Streamlit), and a single **Python reaction** over all six queries keeps a -thread-safe snapshot the UI renders. - -What is new here is that there are **two** databases. Drasi reads changes from a -PostgreSQL `orders` table and a MySQL `vehicles` table and joins them by license -plate. The UI's controls write order changes to Postgres (via ``psycopg``) and -vehicle changes to MySQL (via ``PyMySQL``); Drasi observes both through CDC. Each -write is also appended to an in-memory activity log the UI shows. -""" - -from __future__ import annotations - -import os - -# Quiet the engine's default INFO logging. Must run before ``drasi`` is imported. -os.environ.setdefault("RUST_LOG", "warn") - -import asyncio # noqa: E402 -import threading # noqa: E402 -import time # noqa: E402 -from collections import deque # noqa: E402 -from typing import Any # noqa: E402 - -import psycopg # noqa: E402 -import pymysql # noqa: E402 - -from drasi import Drasi # noqa: E402 -from drasi.types import QueryResultEvent # noqa: E402 - -from .config import ( # noqa: E402 - ACTIVITY_LOG_SIZE, - MYSQL_BOOTSTRAP_CONFIG, - MYSQL_SOURCE_CONFIG, - ORDER_PREPARING, - POSTGRES_BOOTSTRAP_CONFIG, - POSTGRES_SOURCE_CONFIG, - PSYCOPG_CONNECTION, - PYMYSQL_CONNECTION, - VEHICLE_PARKING, -) -from .queries import PHYSICAL_OPS, QUERIES, RETAIL_OPS # noqa: E402 - - -class CurbsideEngine: - """Runs Drasi over two databases and exposes a thread-safe snapshot.""" - - def __init__(self) -> None: - self._lock = threading.Lock() - # Per query: a dict keyed by that query's primary-key field. The reaction - # applies diffs into it; the UI reads a snapshot of the values. - self._results: dict[str, dict[Any, dict[str, Any]]] = {} - # Which RETURN column identifies each query's rows, looked up by query id. - self._key_by_id = {query.id: query.key for query in QUERIES} - self._version = 0 - # Recent SQL writes, newest last: {"db": ..., "sql": ..., "at": ...}. - self._activity: deque[dict[str, Any]] = deque(maxlen=ACTIVITY_LOG_SIZE) - - self._loop: asyncio.AbstractEventLoop | None = None - self._drasi: Drasi | None = None - - self._ready = threading.Event() - self._error: BaseException | None = None - - self._thread = threading.Thread(target=self._run, name="drasi-curbside-engine", daemon=True) - self._thread.start() - - # -- lifecycle (background thread) --------------------------------------- - - def _run(self) -> None: - loop = asyncio.new_event_loop() - self._loop = loop - asyncio.set_event_loop(loop) - try: - loop.run_until_complete(self._start()) - except BaseException as exc: # noqa: BLE001 - surfaced to the UI thread - self._error = exc - self._ready.set() - return - self._ready.set() - loop.run_forever() - - async def _start(self) -> None: - self._drasi = await Drasi.create("curbside-pickup") - - for plugin in ( - "source/postgres", - "bootstrap/postgres", - "source/mysql", - "bootstrap/mysql", - ): - await self._drasi.install_plugin(plugin) - await self._drasi.start() - - # Two sources: PostgreSQL orders and MySQL vehicles. - await self._drasi.add_source( - "postgres", RETAIL_OPS, POSTGRES_SOURCE_CONFIG, bootstrap=POSTGRES_BOOTSTRAP_CONFIG - ) - await self._drasi.add_source( - "mysql", PHYSICAL_OPS, MYSQL_SOURCE_CONFIG, bootstrap=MYSQL_BOOTSTRAP_CONFIG - ) - - for query in QUERIES: - await self._drasi.add_query( - query.id, query.cypher, query.sources, joins=query.joins or None - ) - for query in QUERIES: - await self._drasi.wait_for_query(query.id, timeout=60) - - # Prime the snapshot from the bootstrapped result sets, then let the - # reaction keep it current. - for query in QUERIES: - rows = await self._drasi.get_query_results(query.id) - with self._lock: - self._results[query.id] = {row[query.key]: row for row in rows} - - await self._drasi.add_python_reaction( - "ui", [query.id for query in QUERIES], self._on_results - ) - - def _on_results(self, event: QueryResultEvent) -> None: - """Apply a batch of diffs to the snapshot (called synchronously). - - Every diff carries the affected row in ``data`` (for an UPDATE it equals - ``after``), so the snapshot is kept by row identity: upsert on ADD/UPDATE, - drop on DELETE. - """ - query_id = event["query_id"] - key_field = self._key_by_id[query_id] - with self._lock: - store = self._results.setdefault(query_id, {}) - for diff in event["results"]: - match diff: - case {"type": "DELETE", "data": dict() as row}: - store.pop(row[key_field], None) - case {"after": dict() as row} | {"data": dict() as row}: - # ADD / UPDATE / aggregation: `after` is the new state - # (aggregation diffs carry a null `data`), `data` covers ADD. - store[row[key_field]] = row - self._version += 1 - - # -- readiness ----------------------------------------------------------- - - def wait_ready(self, timeout: float = 240.0) -> None: - """Block until the engine is running, raising if it failed to start.""" - if not self._ready.wait(timeout): - raise TimeoutError("the Drasi engine did not start in time") - if self._error is not None: - raise RuntimeError(f"the Drasi engine failed to start: {self._error}") - - # -- reads (any thread) -------------------------------------------------- - - def snapshot(self) -> dict[str, Any]: - """Return the current result set of every query, plus the activity log.""" - with self._lock: - results = {qid: list(store.values()) for qid, store in self._results.items()} - activity = list(self._activity) - return { - "version": self._version, - "results": results, - "activity": activity, - "error": str(self._error) if self._error else None, - } - - # -- writes (any thread -> a database, observed via CDC) ----------------- - - def set_order_status(self, order_id: int, status: str) -> None: - """Update one order's status in PostgreSQL.""" - with psycopg.connect(**PSYCOPG_CONNECTION) as conn: - conn.execute("UPDATE orders SET status = %s WHERE id = %s", (status, order_id)) - self._log("PostgreSQL", f"UPDATE orders SET status='{status}' WHERE id={order_id};") - - def set_vehicle_location(self, plate: str, location: str) -> None: - """Update one vehicle's location in MySQL.""" - conn = pymysql.connect(**PYMYSQL_CONNECTION) - try: - with conn.cursor() as cur: - cur.execute("UPDATE vehicles SET location = %s WHERE plate = %s", (location, plate)) - finally: - conn.close() - self._log("MySQL", f"UPDATE vehicles SET location='{location}' WHERE plate='{plate}';") - - def reset(self) -> None: - """Return everything to the start: all orders preparing, all parked.""" - with psycopg.connect(**PSYCOPG_CONNECTION) as conn: - conn.execute( - "UPDATE orders SET status = %s WHERE status <> %s", - (ORDER_PREPARING, ORDER_PREPARING), - ) - conn2 = pymysql.connect(**PYMYSQL_CONNECTION) - try: - with conn2.cursor() as cur: - cur.execute( - "UPDATE vehicles SET location = %s WHERE location <> %s", - (VEHICLE_PARKING, VEHICLE_PARKING), - ) - finally: - conn2.close() - self._log("PostgreSQL", f"UPDATE orders SET status='{ORDER_PREPARING}';") - self._log("MySQL", f"UPDATE vehicles SET location='{VEHICLE_PARKING}';") - - def _log(self, db: str, sql: str) -> None: - with self._lock: - self._activity.append({"db": db, "sql": sql, "at": time.strftime("%H:%M:%S")}) diff --git a/tutorials/curbside-pickup/demo/queries.py b/tutorials/curbside-pickup/demo/queries.py deleted file mode 100644 index ed43e2f..0000000 --- a/tutorials/curbside-pickup/demo/queries.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The six continuous queries that power the demo. - -Four are single-source filtered lists that split the orders and vehicles by -state, so a row hops from one panel to another the instant it changes. Two join -across *both* databases through a synthetic join on license plate: - - delivery : an order is 'ready' AND its driver's vehicle is at the 'Curbside' - delay : a driver has waited at the 'Curbside' for over 10s while the order - is still being prepared (uses the temporal drasi.trueFor) - -Each query is written out in full so you can read exactly what Drasi runs. -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Any - -# Source ids (one per database). -RETAIL_OPS = "retail-ops" # PostgreSQL: orders -PHYSICAL_OPS = "physical-ops" # MySQL: vehicles - -# --- Synthetic join ---------------------------------------------------------- -# There is no foreign key between the two databases. Drasi creates the -# relationship in the query, matching a vehicle to an order by equal plate. -PICKUP_BY = { - "id": "PICKUP_BY", - "keys": [ - {"label": "vehicles", "property": "plate"}, - {"label": "orders", "property": "plate"}, - ], -} - -# --- Query ids --------------------------------------------------------------- -# The UI reads each query's result set from the snapshot by these ids. - -ORDERS_PREPARING = "orders-preparing" -ORDERS_READY = "orders-ready" -VEHICLES_PARKING = "vehicles-parking" -VEHICLES_CURBSIDE = "vehicles-curbside" -DELIVERY = "delivery" -DELAY = "delay" - - -@dataclass(frozen=True) -class Query: - """One continuous query: how to register it and how to index its rows.""" - - id: str - key: str # the RETURN column that identifies each row (its primary key) - sources: list[str] # the source ids this query reads from - cypher: str - joins: list[dict[str, Any]] = field(default_factory=list) - - -# Orders still being prepared (status != 'ready'). -ORDERS_PREPARING_QUERY = Query( - id=ORDERS_PREPARING, - key="id", - sources=[RETAIL_OPS], - cypher=""" - MATCH (o:orders) - WHERE o.status <> 'ready' - RETURN - o.id AS id, - o.id AS orderId, - o.customer_name AS customerName, - o.driver_name AS driverName, - o.plate AS plate, - o.status AS status - """, -) - -# Orders that are ready for pickup (status = 'ready'). -ORDERS_READY_QUERY = Query( - id=ORDERS_READY, - key="id", - sources=[RETAIL_OPS], - cypher=""" - MATCH (o:orders) - WHERE o.status = 'ready' - RETURN - o.id AS id, - o.id AS orderId, - o.customer_name AS customerName, - o.driver_name AS driverName, - o.plate AS plate, - o.status AS status - """, -) - -# Vehicles still in the parking lot (location = 'Parking'). -VEHICLES_PARKING_QUERY = Query( - id=VEHICLES_PARKING, - key="id", - sources=[PHYSICAL_OPS], - cypher=""" - MATCH (v:vehicles) - WHERE v.location = 'Parking' - RETURN - v.plate AS id, - v.plate AS plate, - v.make AS make, - v.model AS model, - v.color AS color, - v.location AS location - """, -) - -# Vehicles waiting at the curb (location = 'Curbside'). -VEHICLES_CURBSIDE_QUERY = Query( - id=VEHICLES_CURBSIDE, - key="id", - sources=[PHYSICAL_OPS], - cypher=""" - MATCH (v:vehicles) - WHERE v.location = 'Curbside' - RETURN - v.plate AS id, - v.plate AS plate, - v.make AS make, - v.model AS model, - v.color AS color, - v.location AS location - """, -) - -# Matched orders: ready AND the driver's vehicle is at the curbside. Joins the -# PostgreSQL order to the MySQL vehicle by plate. drasi.changeDateTime exposes the -# wall-clock time of each change; drasi.listMax picks the later of the two. -DELIVERY_QUERY = Query( - id=DELIVERY, - key="id", - sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], - cypher=""" - MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) - WHERE o.status = 'ready' AND v.location = 'Curbside' - RETURN - o.id AS id, - o.id AS orderId, - o.customer_name AS customerName, - o.driver_name AS driverName, - o.plate AS vehicleId, - v.make AS vehicleMake, - v.model AS vehicleModel, - v.color AS vehicleColor, - v.location AS vehicleLocation, - drasi.listMax([drasi.changeDateTime(o), drasi.changeDateTime(v)]) AS readyTimestamp - """, -) - -# Delayed orders: a driver has been at the curbside for over 10 seconds while the -# order is still not ready. drasi.trueFor schedules a future re-evaluation and -# fires the instant the condition has held for the given duration. -DELAY_QUERY = Query( - id=DELAY, - key="orderId", - sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], - cypher=""" - MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) - WHERE o.status <> 'ready' - AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) - RETURN - o.id AS orderId, - o.customer_name AS customerName, - o.driver_name AS driverName, - o.plate AS plate, - drasi.changeDateTime(v) AS waitingSinceTimestamp - """, -) - -# The engine registers these in order and subscribes one reaction to them all. -QUERIES = [ - ORDERS_PREPARING_QUERY, - ORDERS_READY_QUERY, - VEHICLES_PARKING_QUERY, - VEHICLES_CURBSIDE_QUERY, - DELIVERY_QUERY, - DELAY_QUERY, -] diff --git a/tutorials/curbside-pickup/engine.py b/tutorials/curbside-pickup/engine.py new file mode 100644 index 0000000..0d940a5 --- /dev/null +++ b/tutorials/curbside-pickup/engine.py @@ -0,0 +1,440 @@ +# Copyright 2026 The Drasi Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The embedded Drasi engine that powers the Streamlit UI. + +Everything the demo needs on the Drasi side lives in this one file: the two +databases' connection settings and source configs, the synthetic join, the six +continuous queries, and the ``CurbsideEngine`` that wires them together. + +The engine runs Drasi on its own asyncio event loop in a background daemon +thread (created once, cached by Streamlit), and a single **Python reaction** over +all six queries keeps a thread-safe snapshot the UI renders. Drasi reads changes +from a PostgreSQL ``orders`` table and a MySQL ``vehicles`` table and joins them +by license plate. The UI's controls write order changes to Postgres (via +``psycopg``) and vehicle changes to MySQL (via ``PyMySQL``); Drasi observes both +through CDC. Each write is also appended to an in-memory activity log the UI shows. +""" + +from __future__ import annotations + +import os + +# Quiet the engine's default INFO logging. Must run before ``drasi`` is imported. +os.environ.setdefault("RUST_LOG", "warn") + +import asyncio # noqa: E402 +import threading # noqa: E402 +import time # noqa: E402 +from collections import deque # noqa: E402 +from dataclasses import dataclass, field # noqa: E402 +from typing import Any # noqa: E402 + +import psycopg # noqa: E402 +import pymysql # noqa: E402 + +from drasi import Drasi # noqa: E402 +from drasi.types import QueryResultEvent # noqa: E402 + +# ============================================================================= +# Configuration -- two databases, read from the environment with defaults. +# ============================================================================= + +# --- PostgreSQL (orders) --- +POSTGRES = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5742")), + "database": os.environ.get("POSTGRES_DATABASE", "RetailOperations"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), +} +# The Postgres source creates its own replication slot on startup and reuses the +# publication created by database/postgres-init.sql. +POSTGRES_SOURCE_CONFIG = { + **POSTGRES, + "sslMode": "prefer", + "tables": ["public.orders"], + "tableKeys": [{"table": "orders", "keyColumns": ["id"]}], + "slotName": "drasi_curbside_slot", + "publicationName": "drasi_curbside_pub", +} +POSTGRES_BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} +# psycopg spells the database key ``dbname``; the UI writes order status changes +# through this connection so Drasi observes them via logical replication. +PSYCOPG_CONNECTION = { + "host": POSTGRES["host"], + "port": POSTGRES["port"], + "dbname": POSTGRES["database"], + "user": POSTGRES["user"], + "password": POSTGRES["password"], +} + +# --- MySQL (vehicles) --- +MYSQL = { + "host": os.environ.get("MYSQL_HOST", "localhost"), + "port": int(os.environ.get("MYSQL_PORT", "3309")), + "database": os.environ.get("MYSQL_DATABASE", "PhysicalOperations"), + "user": os.environ.get("MYSQL_USER", "drasi_user"), + "password": os.environ.get("MYSQL_PASSWORD", "drasi_password"), +} +# The MySQL source streams the binlog. TLS is disabled on the tutorial container, +# so the source connects in plaintext. Unlike Postgres, the MySQL bootstrap +# provider takes its own connection settings (and does not accept sslMode). +MYSQL_SOURCE_CONFIG = { + **MYSQL, + "sslMode": "disabled", + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], +} +MYSQL_BOOTSTRAP_CONFIG = { + "kind": "mysql", + **MYSQL, + "tables": ["vehicles"], + "tableKeys": [{"table": "vehicles", "keyColumns": ["plate"]}], +} +# PyMySQL spells the database key ``database``; the UI writes vehicle location +# changes through this connection so Drasi observes them via the binlog. +PYMYSQL_CONNECTION = { + "host": MYSQL["host"], + "port": MYSQL["port"], + "database": MYSQL["database"], + "user": MYSQL["user"], + "password": MYSQL["password"], + "autocommit": True, +} + +ORDER_PREPARING = "preparing" +VEHICLE_PARKING = "Parking" + +# How long (matching the delay query) a curbside vehicle waits on an unready order +# before it is flagged as delayed. Shown in the UI copy. +DELAY_SECONDS = 10 +# How many recent SQL writes to keep in the activity log. +ACTIVITY_LOG_SIZE = 20 + +# ============================================================================= +# The six continuous queries. +# ============================================================================= + +# Source ids (one per database). +RETAIL_OPS = "retail-ops" # PostgreSQL: orders +PHYSICAL_OPS = "physical-ops" # MySQL: vehicles + +# There is no foreign key between the two databases. Drasi creates the +# relationship in the query, matching a vehicle to an order by equal plate. +PICKUP_BY = { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], +} + +# Query ids -- the UI reads each query's result set from the snapshot by these. +ORDERS_PREPARING = "orders-preparing" +ORDERS_READY = "orders-ready" +VEHICLES_PARKING = "vehicles-parking" +VEHICLES_CURBSIDE = "vehicles-curbside" +DELIVERY = "delivery" +DELAY = "delay" + + +@dataclass(frozen=True) +class Query: + """One continuous query: how to register it and how to index its rows.""" + + id: str + key: str # the RETURN column that identifies each row (its primary key) + sources: list[str] # the source ids this query reads from + cypher: str + joins: list[dict[str, Any]] = field(default_factory=list) + + +# The four single-source list queries split orders and vehicles by state, and the +# two join queries (delivery, delay) relate them across the two databases. +QUERIES = [ + # Orders still being prepared (status != 'ready'). + Query( + id=ORDERS_PREPARING, + key="id", + sources=[RETAIL_OPS], + cypher=""" + MATCH (o:orders) + WHERE o.status <> 'ready' + RETURN + o.id AS id, + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS plate, + o.status AS status + """, + ), + # Orders that are ready for pickup (status = 'ready'). + Query( + id=ORDERS_READY, + key="id", + sources=[RETAIL_OPS], + cypher=""" + MATCH (o:orders) + WHERE o.status = 'ready' + RETURN + o.id AS id, + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS plate, + o.status AS status + """, + ), + # Vehicles still in the parking lot (location = 'Parking'). + Query( + id=VEHICLES_PARKING, + key="id", + sources=[PHYSICAL_OPS], + cypher=""" + MATCH (v:vehicles) + WHERE v.location = 'Parking' + RETURN + v.plate AS id, + v.plate AS plate, + v.make AS make, + v.model AS model, + v.color AS color, + v.location AS location + """, + ), + # Vehicles waiting at the curb (location = 'Curbside'). + Query( + id=VEHICLES_CURBSIDE, + key="id", + sources=[PHYSICAL_OPS], + cypher=""" + MATCH (v:vehicles) + WHERE v.location = 'Curbside' + RETURN + v.plate AS id, + v.plate AS plate, + v.make AS make, + v.model AS model, + v.color AS color, + v.location AS location + """, + ), + # Matched orders: ready AND the driver's vehicle is at the curbside. Joins the + # PostgreSQL order to the MySQL vehicle by plate. drasi.listMax picks the later + # of the two change times (drasi.changeDateTime). + Query( + id=DELIVERY, + key="id", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status = 'ready' AND v.location = 'Curbside' + RETURN + o.id AS id, + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS vehicleId, + v.make AS vehicleMake, + v.model AS vehicleModel, + v.color AS vehicleColor, + v.location AS vehicleLocation, + drasi.listMax([drasi.changeDateTime(o), drasi.changeDateTime(v)]) AS readyTimestamp + """, + ), + # Delayed orders: a driver has been at the curbside for over 10 seconds while + # the order is still not ready. drasi.trueFor schedules a future re-evaluation + # and fires the instant the condition has held for the given duration. + Query( + id=DELAY, + key="orderId", + sources=[RETAIL_OPS, PHYSICAL_OPS], + joins=[PICKUP_BY], + cypher=""" + MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) + WHERE o.status <> 'ready' + AND drasi.trueFor(v.location = 'Curbside', duration({ seconds: 10 })) + RETURN + o.id AS orderId, + o.customer_name AS customerName, + o.driver_name AS driverName, + o.plate AS plate, + drasi.changeDateTime(v) AS waitingSinceTimestamp + """, + ), +] + +# ============================================================================= +# The engine. +# ============================================================================= + + +class CurbsideEngine: + """Runs Drasi over two databases and exposes a thread-safe snapshot.""" + + def __init__(self) -> None: + self._lock = threading.Lock() + # Per query: a dict keyed by that query's primary-key field. The reaction + # applies diffs into it; the UI reads a snapshot of the values. + self._results: dict[str, dict[Any, dict[str, Any]]] = {} + # Which RETURN column identifies each query's rows, looked up by query id. + self._key_by_id = {query.id: query.key for query in QUERIES} + self._version = 0 + # Recent SQL writes, newest last: {"db": ..., "sql": ..., "at": ...}. + self._activity: deque[dict[str, Any]] = deque(maxlen=ACTIVITY_LOG_SIZE) + + self._loop: asyncio.AbstractEventLoop | None = None + self._drasi: Drasi | None = None + + self._ready = threading.Event() + self._error: BaseException | None = None + + self._thread = threading.Thread(target=self._run, name="drasi-curbside-engine", daemon=True) + self._thread.start() + + # -- lifecycle (background thread) --------------------------------------- + + def _run(self) -> None: + loop = asyncio.new_event_loop() + self._loop = loop + asyncio.set_event_loop(loop) + try: + loop.run_until_complete(self._start()) + except BaseException as exc: # noqa: BLE001 - surfaced to the UI thread + self._error = exc + self._ready.set() + return + self._ready.set() + loop.run_forever() + + async def _start(self) -> None: + self._drasi = await Drasi.create("curbside-pickup") + + for plugin in ( + "source/postgres", + "bootstrap/postgres", + "source/mysql", + "bootstrap/mysql", + ): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + # Two sources: PostgreSQL orders and MySQL vehicles. + await self._drasi.add_source( + "postgres", RETAIL_OPS, POSTGRES_SOURCE_CONFIG, bootstrap=POSTGRES_BOOTSTRAP_CONFIG + ) + await self._drasi.add_source( + "mysql", PHYSICAL_OPS, MYSQL_SOURCE_CONFIG, bootstrap=MYSQL_BOOTSTRAP_CONFIG + ) + + for query in QUERIES: + await self._drasi.add_query( + query.id, query.cypher, query.sources, joins=query.joins or None + ) + for query in QUERIES: + await self._drasi.wait_for_query(query.id, timeout=60) + + # Prime the snapshot from the bootstrapped result sets, then let the + # reaction keep it current. + for query in QUERIES: + rows = await self._drasi.get_query_results(query.id) + with self._lock: + self._results[query.id] = {row[query.key]: row for row in rows} + + await self._drasi.add_python_reaction( + "ui", [query.id for query in QUERIES], self._on_results + ) + + def _on_results(self, event: QueryResultEvent) -> None: + """Apply a batch of diffs to the snapshot (called synchronously).""" + query_id = event["query_id"] + key_field = self._key_by_id[query_id] + with self._lock: + store = self._results.setdefault(query_id, {}) + for diff in event["results"]: + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key_field], None) + case {"after": dict() as row} | {"data": dict() as row}: + # ADD / UPDATE / aggregation: `after` is the new state + # (aggregation diffs carry a null `data`), `data` covers ADD. + store[row[key_field]] = row + self._version += 1 + + # -- readiness ----------------------------------------------------------- + + def wait_ready(self, timeout: float = 240.0) -> None: + """Block until the engine is running, raising if it failed to start.""" + if not self._ready.wait(timeout): + raise TimeoutError("the Drasi engine did not start in time") + if self._error is not None: + raise RuntimeError(f"the Drasi engine failed to start: {self._error}") + + # -- reads (any thread) -------------------------------------------------- + + def snapshot(self) -> dict[str, Any]: + """Return the current result set of every query, plus the activity log.""" + with self._lock: + results = {qid: list(store.values()) for qid, store in self._results.items()} + activity = list(self._activity) + return { + "version": self._version, + "results": results, + "activity": activity, + "error": str(self._error) if self._error else None, + } + + # -- writes (any thread -> a database, observed via CDC) ----------------- + + def set_order_status(self, order_id: int, status: str) -> None: + """Update one order's status in PostgreSQL.""" + with psycopg.connect(**PSYCOPG_CONNECTION) as conn: + conn.execute("UPDATE orders SET status = %s WHERE id = %s", (status, order_id)) + self._log("PostgreSQL", f"UPDATE orders SET status='{status}' WHERE id={order_id};") + + def set_vehicle_location(self, plate: str, location: str) -> None: + """Update one vehicle's location in MySQL.""" + conn = pymysql.connect(**PYMYSQL_CONNECTION) + try: + with conn.cursor() as cur: + cur.execute("UPDATE vehicles SET location = %s WHERE plate = %s", (location, plate)) + finally: + conn.close() + self._log("MySQL", f"UPDATE vehicles SET location='{location}' WHERE plate='{plate}';") + + def reset(self) -> None: + """Return everything to the start: all orders preparing, all parked.""" + with psycopg.connect(**PSYCOPG_CONNECTION) as conn: + conn.execute( + "UPDATE orders SET status = %s WHERE status <> %s", + (ORDER_PREPARING, ORDER_PREPARING), + ) + conn2 = pymysql.connect(**PYMYSQL_CONNECTION) + try: + with conn2.cursor() as cur: + cur.execute( + "UPDATE vehicles SET location = %s WHERE location <> %s", + (VEHICLE_PARKING, VEHICLE_PARKING), + ) + finally: + conn2.close() + self._log("PostgreSQL", f"UPDATE orders SET status='{ORDER_PREPARING}';") + self._log("MySQL", f"UPDATE vehicles SET location='{VEHICLE_PARKING}';") + + def _log(self, db: str, sql: str) -> None: + with self._lock: + self._activity.append({"db": db, "sql": sql, "at": time.strftime("%H:%M:%S")}) From b59e7f3706bbcdcd85dbc1c4cab7e6ecbec972c0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 14:57:54 -0700 Subject: [PATCH 15/19] Inline the PICKUP_BY synthetic join in the curbside tutorial Drop the standalone PICKUP_BY constant and declare the join dict directly in each query's joins list, so the delivery and delay queries are self-contained. Update the docs (The Synthetic Join and Putting It All Together) to match and regenerate the README. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/curbside-pickup/README.md | 38 +++++++++++++++++------------ tutorials/curbside-pickup/_index.md | 38 +++++++++++++++++------------ tutorials/curbside-pickup/engine.py | 37 ++++++++++++++++------------ 3 files changed, 68 insertions(+), 45 deletions(-) diff --git a/tutorials/curbside-pickup/README.md b/tutorials/curbside-pickup/README.md index 7a6f527..4759d12 100644 --- a/tutorials/curbside-pickup/README.md +++ b/tutorials/curbside-pickup/README.md @@ -184,10 +184,10 @@ The MySQL container is started with ROW-based logging, full row images/metadata ### The Synthetic Join -There is no foreign key between the two databases โ€” they're completely separate systems. Drasi creates the relationship in the query with a **synthetic join**, matching a vehicle to an order whenever their `plate` values are equal: +There is no foreign key between the two databases โ€” they're completely separate systems. Drasi creates the relationship in the query with a **synthetic join**, matching a vehicle to an order whenever their `plate` values are equal. The `delivery` and `delay` queries read from **both** sources and declare this join dict in their `joins` list: ```python -PICKUP_BY = { +{ "id": "PICKUP_BY", "keys": [ {"label": "vehicles", "property": "plate"}, @@ -196,7 +196,7 @@ PICKUP_BY = { } ``` -The `delivery` and `delay` queries read from **both** sources and pass `joins=[PICKUP_BY]` to `add_query`, then walk `(o:orders)-[:PICKUP_BY]->(v:vehicles)` as if it were a real graph edge โ€” across two different databases. +With that declared, the query walks `(o:orders)-[:PICKUP_BY]->(v:vehicles)` as if it were a real graph edge โ€” across two different databases. ### The Six Continuous Queries @@ -311,15 +311,6 @@ MYSQL_SOURCE_CONFIG = { RETAIL_OPS, PHYSICAL_OPS = "retail-ops", "physical-ops" -# The synthetic join: match a vehicle to an order by equal plate, across databases. -PICKUP_BY = { - "id": "PICKUP_BY", - "keys": [ - {"label": "vehicles", "property": "plate"}, - {"label": "orders", "property": "plate"}, - ], -} - @dataclass(frozen=True) class Query: @@ -341,12 +332,21 @@ QUERIES = [ cypher="MATCH (o:orders) WHERE o.status <> 'ready' " "RETURN o.id AS id, o.customer_name AS customerName, o.plate AS plate", ), - # delivery: an order is ready AND its driver's vehicle is at the curbside. + # delivery: an order is ready AND its driver's vehicle is at the curbside. The + # synthetic join matches a vehicle to an order by equal plate, across databases. Query( id="delivery", key="id", sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], + joins=[ + { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], + } + ], cypher=""" MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) WHERE o.status = 'ready' AND v.location = 'Curbside' @@ -358,7 +358,15 @@ QUERIES = [ id="delay", key="orderId", sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], + joins=[ + { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], + } + ], cypher=""" MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) WHERE o.status <> 'ready' diff --git a/tutorials/curbside-pickup/_index.md b/tutorials/curbside-pickup/_index.md index c746593..9207e3a 100644 --- a/tutorials/curbside-pickup/_index.md +++ b/tutorials/curbside-pickup/_index.md @@ -218,10 +218,10 @@ The MySQL container is started with ROW-based logging, full row images/metadata ### The Synthetic Join -There is no foreign key between the two databases โ€” they're completely separate systems. Drasi creates the relationship in the query with a **synthetic join**, matching a vehicle to an order whenever their `plate` values are equal: +There is no foreign key between the two databases โ€” they're completely separate systems. Drasi creates the relationship in the query with a **synthetic join**, matching a vehicle to an order whenever their `plate` values are equal. The `delivery` and `delay` queries read from **both** sources and declare this join dict in their `joins` list: ```python -PICKUP_BY = { +{ "id": "PICKUP_BY", "keys": [ {"label": "vehicles", "property": "plate"}, @@ -230,7 +230,7 @@ PICKUP_BY = { } ``` -The `delivery` and `delay` queries read from **both** sources and pass `joins=[PICKUP_BY]` to `add_query`, then walk `(o:orders)-[:PICKUP_BY]->(v:vehicles)` as if it were a real graph edge โ€” across two different databases. +With that declared, the query walks `(o:orders)-[:PICKUP_BY]->(v:vehicles)` as if it were a real graph edge โ€” across two different databases. ### The Six Continuous Queries @@ -346,15 +346,6 @@ MYSQL_SOURCE_CONFIG = { RETAIL_OPS, PHYSICAL_OPS = "retail-ops", "physical-ops" -# The synthetic join: match a vehicle to an order by equal plate, across databases. -PICKUP_BY = { - "id": "PICKUP_BY", - "keys": [ - {"label": "vehicles", "property": "plate"}, - {"label": "orders", "property": "plate"}, - ], -} - @dataclass(frozen=True) class Query: @@ -376,12 +367,21 @@ QUERIES = [ cypher="MATCH (o:orders) WHERE o.status <> 'ready' " "RETURN o.id AS id, o.customer_name AS customerName, o.plate AS plate", ), - # delivery: an order is ready AND its driver's vehicle is at the curbside. + # delivery: an order is ready AND its driver's vehicle is at the curbside. The + # synthetic join matches a vehicle to an order by equal plate, across databases. Query( id="delivery", key="id", sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], + joins=[ + { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], + } + ], cypher=""" MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) WHERE o.status = 'ready' AND v.location = 'Curbside' @@ -393,7 +393,15 @@ QUERIES = [ id="delay", key="orderId", sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], + joins=[ + { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], + } + ], cypher=""" MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) WHERE o.status <> 'ready' diff --git a/tutorials/curbside-pickup/engine.py b/tutorials/curbside-pickup/engine.py index 0d940a5..95a4397 100644 --- a/tutorials/curbside-pickup/engine.py +++ b/tutorials/curbside-pickup/engine.py @@ -131,16 +131,6 @@ RETAIL_OPS = "retail-ops" # PostgreSQL: orders PHYSICAL_OPS = "physical-ops" # MySQL: vehicles -# There is no foreign key between the two databases. Drasi creates the -# relationship in the query, matching a vehicle to an order by equal plate. -PICKUP_BY = { - "id": "PICKUP_BY", - "keys": [ - {"label": "vehicles", "property": "plate"}, - {"label": "orders", "property": "plate"}, - ], -} - # Query ids -- the UI reads each query's result set from the snapshot by these. ORDERS_PREPARING = "orders-preparing" ORDERS_READY = "orders-ready" @@ -232,14 +222,23 @@ class Query: v.location AS location """, ), - # Matched orders: ready AND the driver's vehicle is at the curbside. Joins the - # PostgreSQL order to the MySQL vehicle by plate. drasi.listMax picks the later - # of the two change times (drasi.changeDateTime). + # Matched orders: ready AND the driver's vehicle is at the curbside. There is + # no foreign key between the two databases, so the query declares the + # relationship as a synthetic join, matching a vehicle to an order by equal + # plate. drasi.listMax picks the later of the two change times. Query( id=DELIVERY, key="id", sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], + joins=[ + { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], + } + ], cypher=""" MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) WHERE o.status = 'ready' AND v.location = 'Curbside' @@ -263,7 +262,15 @@ class Query: id=DELAY, key="orderId", sources=[RETAIL_OPS, PHYSICAL_OPS], - joins=[PICKUP_BY], + joins=[ + { + "id": "PICKUP_BY", + "keys": [ + {"label": "vehicles", "property": "plate"}, + {"label": "orders", "property": "plate"}, + ], + } + ], cypher=""" MATCH (o:orders)-[:PICKUP_BY]->(v:vehicles) WHERE o.status <> 'ready' From 5c131329c860ab51ebd5998592fd1e6d9d52bc79 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 15:06:19 -0700 Subject: [PATCH 16/19] Add the getting-started queries inline instead of a dict loop Register each of the three continuous queries with its own add_query call, inline where its explanatory comment lives, rather than storing them in a QUERIES dict and looping. Update the docs (The Python Reaction and Putting It All Together) to match and regenerate the README. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/getting-started/README.md | 71 +++++++++++++---------- tutorials/getting-started/_index.md | 71 +++++++++++++---------- tutorials/getting-started/app.py | 87 +++++++++++++++++------------ 3 files changed, 134 insertions(+), 95 deletions(-) diff --git a/tutorials/getting-started/README.md b/tutorials/getting-started/README.md index 2958a5b..23a5376 100644 --- a/tutorials/getting-started/README.md +++ b/tutorials/getting-started/README.md @@ -229,7 +229,9 @@ def on_change(event): print(f"[{query_id}] ~ {before} -> {after}") -await drasi.add_python_reaction("console", list(QUERIES), on_change) +await drasi.add_python_reaction( + "console", ["hello-world-from", "message-count", "inactive-people"], on_change +) ``` An `ADD` and a `DELETE` carry `data`; an `UPDATE` โ€” and an `aggregation` change, like a `count` ticking up โ€” carries `before` and `after`. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. @@ -265,29 +267,6 @@ SOURCE_CONFIG = { } BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} -# --- The three continuous queries --- -QUERIES = { - "hello-world-from": """ - MATCH (m:message) - WHERE m.message = 'Hello World' - RETURN m.messageid AS MessageId, m.sender AS MessageFrom - """, - "message-count": """ - MATCH (m:message) - RETURN m.message AS Message, count(m) AS Frequency - """, - "inactive-people": """ - MATCH (m:message) - WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp - WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) - OR drasi.trueLater( - LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), - LastMessageTimestamp + duration({ seconds: 20 }) - ) - RETURN MessageFrom, LastMessageTimestamp - """, -} - def row(r): return " ".join(f"{k}={v!r}" for k, v in r.items()) @@ -314,17 +293,51 @@ async def main(): await drasi.start() await drasi.add_source("postgres", "messages", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) - for query_id, cypher in QUERIES.items(): - await drasi.add_query(query_id, cypher, ["messages"]) - for query_id in QUERIES: + + # Add the three continuous queries, in place: a filter, an aggregation, + # and a temporal query that detects the absence of change. + await drasi.add_query( + "hello-world-from", + """ + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.sender AS MessageFrom + """, + ["messages"], + ) + await drasi.add_query( + "message-count", + """ + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, + ["messages"], + ) + await drasi.add_query( + "inactive-people", + """ + MATCH (m:message) + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, + ["messages"], + ) + + query_ids = ["hello-world-from", "message-count", "inactive-people"] + for query_id in query_ids: await drasi.wait_for_query(query_id, timeout=60) print("=== Initial results ===") - for query_id in QUERIES: + for query_id in query_ids: for r in await drasi.get_query_results(query_id): print(f"[{query_id}] {row(r)}") - await drasi.add_python_reaction("console", list(QUERIES), on_change) + await drasi.add_python_reaction("console", query_ids, on_change) print("=== Watching for changes (Ctrl+C to stop) ===") await asyncio.Event().wait() diff --git a/tutorials/getting-started/_index.md b/tutorials/getting-started/_index.md index b221f11..9b2b52b 100644 --- a/tutorials/getting-started/_index.md +++ b/tutorials/getting-started/_index.md @@ -263,7 +263,9 @@ def on_change(event): print(f"[{query_id}] ~ {before} -> {after}") -await drasi.add_python_reaction("console", list(QUERIES), on_change) +await drasi.add_python_reaction( + "console", ["hello-world-from", "message-count", "inactive-people"], on_change +) ``` An `ADD` and a `DELETE` carry `data`; an `UPDATE` โ€” and an `aggregation` change, like a `count` ticking up โ€” carries `before` and `after`. After registering the reaction the app just `await asyncio.Event().wait()`s, so it stays in the foreground printing changes until you press Ctrl+C. See [Python reactions](../../guides/python-reactions/) for the full callback contract, or [Streaming results](../../guides/streaming/) to consume the same changes as an async iterator instead. @@ -300,29 +302,6 @@ SOURCE_CONFIG = { } BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} -# --- The three continuous queries --- -QUERIES = { - "hello-world-from": """ - MATCH (m:message) - WHERE m.message = 'Hello World' - RETURN m.messageid AS MessageId, m.sender AS MessageFrom - """, - "message-count": """ - MATCH (m:message) - RETURN m.message AS Message, count(m) AS Frequency - """, - "inactive-people": """ - MATCH (m:message) - WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp - WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) - OR drasi.trueLater( - LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), - LastMessageTimestamp + duration({ seconds: 20 }) - ) - RETURN MessageFrom, LastMessageTimestamp - """, -} - def row(r): return " ".join(f"{k}={v!r}" for k, v in r.items()) @@ -349,17 +328,51 @@ async def main(): await drasi.start() await drasi.add_source("postgres", "messages", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) - for query_id, cypher in QUERIES.items(): - await drasi.add_query(query_id, cypher, ["messages"]) - for query_id in QUERIES: + + # Add the three continuous queries, in place: a filter, an aggregation, + # and a temporal query that detects the absence of change. + await drasi.add_query( + "hello-world-from", + """ + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.sender AS MessageFrom + """, + ["messages"], + ) + await drasi.add_query( + "message-count", + """ + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, + ["messages"], + ) + await drasi.add_query( + "inactive-people", + """ + MATCH (m:message) + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, + ["messages"], + ) + + query_ids = ["hello-world-from", "message-count", "inactive-people"] + for query_id in query_ids: await drasi.wait_for_query(query_id, timeout=60) print("=== Initial results ===") - for query_id in QUERIES: + for query_id in query_ids: for r in await drasi.get_query_results(query_id): print(f"[{query_id}] {row(r)}") - await drasi.add_python_reaction("console", list(QUERIES), on_change) + await drasi.add_python_reaction("console", query_ids, on_change) print("=== Watching for changes (Ctrl+C to stop) ===") await asyncio.Event().wait() diff --git a/tutorials/getting-started/app.py b/tutorials/getting-started/app.py index c212270..a99c58f 100644 --- a/tutorials/getting-started/app.py +++ b/tutorials/getting-started/app.py @@ -69,39 +69,8 @@ # The bootstrap provider loads the rows that already exist when a query starts. BOOTSTRAP_CONFIG = {"kind": "postgres", **POSTGRES} -# --- The three continuous queries ------------------------------------------- -# They build up in complexity: a filter, an aggregation, and a temporal query -# that detects the *absence* of change. -QUERIES = { - # Filter: messages whose text is exactly "Hello World", and who sent them. - "hello-world-from": """ - MATCH (m:message) - WHERE m.message = 'Hello World' - RETURN m.messageid AS MessageId, m.sender AS MessageFrom - """, - # Aggregation: how many times each distinct message has been sent. - "message-count": """ - MATCH (m:message) - RETURN m.message AS Message, count(m) AS Frequency - """, - # Absence of change: senders who have not sent a message in the last 20 - # seconds. drasi.trueLater schedules a future re-evaluation so a sender - # appears the instant they cross the threshold, not only when some other - # change happens. - "inactive-people": """ - MATCH (m:message) - WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp - WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) - OR drasi.trueLater( - LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), - LastMessageTimestamp + duration({ seconds: 20 }) - ) - RETURN MessageFrom, LastMessageTimestamp - """, -} - # How long a sender can be quiet before inactive-people flags them (matches the -# duration in the query). Shown in the startup banner. +# duration in the inactive-people query below). Shown in the startup banner. INACTIVITY_SECONDS = 20 @@ -140,21 +109,65 @@ async def main() -> None: await drasi.add_source("postgres", "messages", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) - for query_id, cypher in QUERIES.items(): - await drasi.add_query(query_id, cypher, ["messages"]) - for query_id in QUERIES: + # Add the three continuous queries, in place. They build up in + # complexity: a filter, an aggregation, and a temporal query that + # detects the *absence* of change. + + # Filter: messages whose text is exactly "Hello World", and who sent them. + await drasi.add_query( + "hello-world-from", + """ + MATCH (m:message) + WHERE m.message = 'Hello World' + RETURN m.messageid AS MessageId, m.sender AS MessageFrom + """, + ["messages"], + ) + + # Aggregation: how many times each distinct message has been sent. + await drasi.add_query( + "message-count", + """ + MATCH (m:message) + RETURN m.message AS Message, count(m) AS Frequency + """, + ["messages"], + ) + + # Absence of change: senders who have not sent a message in the last 20 + # seconds. drasi.trueLater schedules a future re-evaluation so a sender + # appears the instant they cross the threshold, not only when some other + # change happens. + await drasi.add_query( + "inactive-people", + """ + MATCH (m:message) + WITH m.sender AS MessageFrom, max(drasi.changeDateTime(m)) AS LastMessageTimestamp + WHERE LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }) + OR drasi.trueLater( + LastMessageTimestamp <= datetime.realtime() - duration({ seconds: 20 }), + LastMessageTimestamp + duration({ seconds: 20 }) + ) + RETURN MessageFrom, LastMessageTimestamp + """, + ["messages"], + ) + + # Wait for all three to bootstrap their initial result sets. + query_ids = ["hello-world-from", "message-count", "inactive-people"] + for query_id in query_ids: await drasi.wait_for_query(query_id, timeout=60) # Show the state each query bootstrapped from the existing rows. print("\n=== Initial results ===") - for query_id in QUERIES: + for query_id in query_ids: rows = await drasi.get_query_results(query_id) print(f"\n[{query_id}] {len(rows)} row(s):") for row in rows: print(f" {_row(row)}") # From here on, print changes as they arrive. - await drasi.add_python_reaction("console", list(QUERIES), on_change) + await drasi.add_python_reaction("console", query_ids, on_change) print( f"\n=== Watching for changes (Ctrl+C to stop) ===\n" From dcdd107f20ade91bc5639810d82482aacc2d9a42 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 15:16:09 -0700 Subject: [PATCH 17/19] Collapse the building-comfort demo package into a single engine.py Merge demo/config.py, demo/queries.py and demo/engine.py into one engine.py at the tutorial root (config, the six queries, and ComfortEngine), mirroring the curbside layout. app.py now imports everything from engine. Add a Putting It All Together section to the docs showing the whole engine.py and regenerate the README. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/building-comfort/README.md | 177 +++++++++++++- tutorials/building-comfort/_index.md | 178 +++++++++++++- tutorials/building-comfort/app.py | 7 +- tutorials/building-comfort/demo/__init__.py | 19 -- tutorials/building-comfort/demo/config.py | 88 ------- tutorials/building-comfort/demo/queries.py | 184 -------------- .../building-comfort/{demo => }/engine.py | 227 +++++++++++++++++- 7 files changed, 576 insertions(+), 304 deletions(-) delete mode 100644 tutorials/building-comfort/demo/__init__.py delete mode 100644 tutorials/building-comfort/demo/config.py delete mode 100644 tutorials/building-comfort/demo/queries.py rename tutorials/building-comfort/{demo => }/engine.py (53%) diff --git a/tutorials/building-comfort/README.md b/tutorials/building-comfort/README.md index 553d977..5e119bc 100644 --- a/tutorials/building-comfort/README.md +++ b/tutorials/building-comfort/README.md @@ -136,7 +136,7 @@ That gives `50 + (82-72) + (40-42) + 0 = 58` โ€” above 50, so the room and its f Flip the **Simulation mode** toggle at the top of the sidebar. Every few seconds it picks a random room and assigns new readings, so comfort levels rise and fall and alerts come and go on their own. Flip it off to stop, then **Reset all rooms** to return everything to comfortable. ## How It Works -The whole demo is a single Streamlit program in `tutorials/building-comfort/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state. Here's what each part does. +The demo is two files in `tutorials/building-comfort/`: **`engine.py`** holds the source, the six queries, and the `ComfortEngine` (its Python reaction and the SQL writes); **`app.py`** renders the reaction's state and hosts the sidebar controls. The [complete `engine.py`](#putting-it-all-together) is at the end. Here's what each part does. ### The Source @@ -312,6 +312,181 @@ A Python reaction is the most direct way to turn query changes into a UI, but it Because Drasi emits only what *changed* โ€” never the full result set โ€” whichever you choose stays live without polling. +## Putting It All Together +The sections above walked through the Drasi side one piece at a time. Here it is assembled into the single `engine.py` โ€” the source, the two synthetic joins, the six queries, and the `ComfortEngine` that runs them and writes back. `app.py` (the Streamlit UI) imports `ComfortEngine`, the query ids, and the comfort band from it: + +```python +import asyncio +import os +import random +import threading +from dataclasses import dataclass, field + +os.environ.setdefault("RUST_LOG", "warn") # quiet the engine's INFO logging + +import psycopg +from drasi import Drasi + +# --- The source: the tutorial's PostgreSQL database --- +CONNECTION = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5732")), + "database": os.environ.get("POSTGRES_DATABASE", "building_comfort"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), + "sslMode": "prefer", +} +SOURCE_CONFIG = { + **CONNECTION, + "tables": ["public.Building", "public.Floor", "public.Room"], + "tableKeys": [ + {"table": "Building", "keyColumns": ["id"]}, + {"table": "Floor", "keyColumns": ["id"]}, + {"table": "Room", "keyColumns": ["id"]}, + ], + "slotName": "drasi_building_comfort_slot", + "publicationName": "drasi_building_comfort_pub", +} +BOOTSTRAP_CONFIG = {"kind": "postgres", **CONNECTION} +# The UI writes room updates through a psycopg connection built from CONNECTION +# (psycopg spells the database key "dbname"), so Drasi observes them via CDC. + +COMFORT_DEFAULTS = {"temperature": 70, "humidity": 40, "co2": 10} +COMFORT_MIN, COMFORT_MAX = 40, 50 + +# --- Two synthetic joins walk Room -> Floor -> Building --- +PART_OF_FLOOR = { + "id": "PART_OF_FLOOR", + "keys": [ + {"label": "Room", "property": "floor_id"}, + {"label": "Floor", "property": "id"}, + ], +} +PART_OF_BUILDING = { + "id": "PART_OF_BUILDING", + "keys": [ + {"label": "Floor", "property": "building_id"}, + {"label": "Building", "property": "id"}, + ], +} + + +@dataclass(frozen=True) +class Query: + id: str + key: str # the RETURN column that identifies each row + cypher: str + joins: list[dict] = field(default_factory=list) + + +# Every query computes the same comfort level; a value in 40-50 is comfortable. +QUERIES = [ + # building-comfort-ui: one row per room with its comfort level. Drives the + # building view; walks Room -> Floor -> Building through the two joins. + Query( + id="building-comfort-ui", + key="RoomId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + RETURN r.id AS RoomId, r.name AS RoomName, f.id AS FloorId, f.name AS FloorName, + b.name AS BuildingName, r.temperature AS Temperature, r.humidity AS Humidity, + r.co2 AS CO2, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + """, + ), + # floor-comfort-level-calc: each floor's comfort, the average of its rooms. + Query( + id="floor-comfort-level-calc", + key="FloorId", + joins=[PART_OF_FLOOR], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH f, floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfort + WITH f, avg(RoomComfort) AS ComfortLevel + RETURN f.id AS FloorId, ComfortLevel + """, + ), + # room-alert: only the rooms whose comfort is outside the 40-50 band. + Query( + id="room-alert", + key="RoomId", + cypher=""" + MATCH (r:Room) + WITH r.id AS RoomId, r.name AS RoomName, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN RoomId, RoomName, ComfortLevel + """, + ), + # building-comfort-level-calc (average of floor averages), floor-alert, and + # building-alert follow the same formula with avg()/WHERE variations. +] + + +class ComfortEngine: + """Runs Drasi on a background loop and exposes a thread-safe snapshot.""" + + def __init__(self): + self._lock = threading.Lock() + self._results = {} # query id -> {row key -> row} + self._key_by_id = {q.id: q.key for q in QUERIES} + self._simulation = False + self._ready = threading.Event() + self._drasi = None + threading.Thread(target=self._run, daemon=True).start() + + def _run(self): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self._start()) + self._ready.set() + loop.run_forever() + + async def _start(self): + self._drasi = await Drasi.create("building-comfort") + for plugin in ("source/postgres", "bootstrap/postgres"): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + await self._drasi.add_source("postgres", "db", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + for q in QUERIES: + await self._drasi.add_query(q.id, q.cypher, ["db"], joins=q.joins or None) + for q in QUERIES: + await self._drasi.wait_for_query(q.id) + rows = await self._drasi.get_query_results(q.id) + self._results[q.id] = {r[q.key]: r for r in rows} + + await self._drasi.add_python_reaction("ui", [q.id for q in QUERIES], self._on_results) + + def _on_results(self, event): + key = self._key_by_id[event["query_id"]] + with self._lock: + store = self._results.setdefault(event["query_id"], {}) + for diff in event["results"]: + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key], None) + case {"after": dict() as row} | {"data": dict() as row}: + store[row[key]] = row + + def snapshot(self): + with self._lock: + return { + "simulation": self._simulation, + "results": {q: list(s.values()) for q, s in self._results.items()}, + } + + # set_room(id, t, h, co2) and reset_room(id=None) write UPDATEs to Postgres; + # set_simulation(on) toggles a background task that assigns a random room new + # readings every few seconds. Drasi observes each write via CDC. +``` + +That's the whole Drasi side in one file: one source, two synthetic joins up the Room -> Floor -> Building hierarchy, six continuous queries (four of them aggregations), and a reaction that keeps a snapshot the UI renders and drives. + ## Clean Up When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database container: diff --git a/tutorials/building-comfort/_index.md b/tutorials/building-comfort/_index.md index a428158..8e1c2af 100644 --- a/tutorials/building-comfort/_index.md +++ b/tutorials/building-comfort/_index.md @@ -170,7 +170,7 @@ Flip the **Simulation mode** toggle at the top of the sidebar. Every few seconds ## How It Works {#how} -The whole demo is a single Streamlit program in `tutorials/building-comfort/`. The engine, queries and reaction live in `demo/`, and `app.py` renders the reaction's state. Here's what each part does. +The demo is two files in `tutorials/building-comfort/`: **`engine.py`** holds the source, the six queries, and the `ComfortEngine` (its Python reaction and the SQL writes); **`app.py`** renders the reaction's state and hosts the sidebar controls. The [complete `engine.py`](#all) is at the end. Here's what each part does. ### The Source @@ -346,6 +346,182 @@ A Python reaction is the most direct way to turn query changes into a UI, but it Because Drasi emits only what *changed* โ€” never the full result set โ€” whichever you choose stays live without polling. +## Putting It All Together {#all} + +The sections above walked through the Drasi side one piece at a time. Here it is assembled into the single `engine.py` โ€” the source, the two synthetic joins, the six queries, and the `ComfortEngine` that runs them and writes back. `app.py` (the Streamlit UI) imports `ComfortEngine`, the query ids, and the comfort band from it: + +```python +import asyncio +import os +import random +import threading +from dataclasses import dataclass, field + +os.environ.setdefault("RUST_LOG", "warn") # quiet the engine's INFO logging + +import psycopg +from drasi import Drasi + +# --- The source: the tutorial's PostgreSQL database --- +CONNECTION = { + "host": os.environ.get("POSTGRES_HOST", "localhost"), + "port": int(os.environ.get("POSTGRES_PORT", "5732")), + "database": os.environ.get("POSTGRES_DATABASE", "building_comfort"), + "user": os.environ.get("POSTGRES_USER", "drasi_user"), + "password": os.environ.get("POSTGRES_PASSWORD", "drasi_password"), + "sslMode": "prefer", +} +SOURCE_CONFIG = { + **CONNECTION, + "tables": ["public.Building", "public.Floor", "public.Room"], + "tableKeys": [ + {"table": "Building", "keyColumns": ["id"]}, + {"table": "Floor", "keyColumns": ["id"]}, + {"table": "Room", "keyColumns": ["id"]}, + ], + "slotName": "drasi_building_comfort_slot", + "publicationName": "drasi_building_comfort_pub", +} +BOOTSTRAP_CONFIG = {"kind": "postgres", **CONNECTION} +# The UI writes room updates through a psycopg connection built from CONNECTION +# (psycopg spells the database key "dbname"), so Drasi observes them via CDC. + +COMFORT_DEFAULTS = {"temperature": 70, "humidity": 40, "co2": 10} +COMFORT_MIN, COMFORT_MAX = 40, 50 + +# --- Two synthetic joins walk Room -> Floor -> Building --- +PART_OF_FLOOR = { + "id": "PART_OF_FLOOR", + "keys": [ + {"label": "Room", "property": "floor_id"}, + {"label": "Floor", "property": "id"}, + ], +} +PART_OF_BUILDING = { + "id": "PART_OF_BUILDING", + "keys": [ + {"label": "Floor", "property": "building_id"}, + {"label": "Building", "property": "id"}, + ], +} + + +@dataclass(frozen=True) +class Query: + id: str + key: str # the RETURN column that identifies each row + cypher: str + joins: list[dict] = field(default_factory=list) + + +# Every query computes the same comfort level; a value in 40-50 is comfortable. +QUERIES = [ + # building-comfort-ui: one row per room with its comfort level. Drives the + # building view; walks Room -> Floor -> Building through the two joins. + Query( + id="building-comfort-ui", + key="RoomId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + RETURN r.id AS RoomId, r.name AS RoomName, f.id AS FloorId, f.name AS FloorName, + b.name AS BuildingName, r.temperature AS Temperature, r.humidity AS Humidity, + r.co2 AS CO2, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + """, + ), + # floor-comfort-level-calc: each floor's comfort, the average of its rooms. + Query( + id="floor-comfort-level-calc", + key="FloorId", + joins=[PART_OF_FLOOR], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH f, floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfort + WITH f, avg(RoomComfort) AS ComfortLevel + RETURN f.id AS FloorId, ComfortLevel + """, + ), + # room-alert: only the rooms whose comfort is outside the 40-50 band. + Query( + id="room-alert", + key="RoomId", + cypher=""" + MATCH (r:Room) + WITH r.id AS RoomId, r.name AS RoomName, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN RoomId, RoomName, ComfortLevel + """, + ), + # building-comfort-level-calc (average of floor averages), floor-alert, and + # building-alert follow the same formula with avg()/WHERE variations. +] + + +class ComfortEngine: + """Runs Drasi on a background loop and exposes a thread-safe snapshot.""" + + def __init__(self): + self._lock = threading.Lock() + self._results = {} # query id -> {row key -> row} + self._key_by_id = {q.id: q.key for q in QUERIES} + self._simulation = False + self._ready = threading.Event() + self._drasi = None + threading.Thread(target=self._run, daemon=True).start() + + def _run(self): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self._start()) + self._ready.set() + loop.run_forever() + + async def _start(self): + self._drasi = await Drasi.create("building-comfort") + for plugin in ("source/postgres", "bootstrap/postgres"): + await self._drasi.install_plugin(plugin) + await self._drasi.start() + + await self._drasi.add_source("postgres", "db", SOURCE_CONFIG, bootstrap=BOOTSTRAP_CONFIG) + for q in QUERIES: + await self._drasi.add_query(q.id, q.cypher, ["db"], joins=q.joins or None) + for q in QUERIES: + await self._drasi.wait_for_query(q.id) + rows = await self._drasi.get_query_results(q.id) + self._results[q.id] = {r[q.key]: r for r in rows} + + await self._drasi.add_python_reaction("ui", [q.id for q in QUERIES], self._on_results) + + def _on_results(self, event): + key = self._key_by_id[event["query_id"]] + with self._lock: + store = self._results.setdefault(event["query_id"], {}) + for diff in event["results"]: + match diff: + case {"type": "DELETE", "data": dict() as row}: + store.pop(row[key], None) + case {"after": dict() as row} | {"data": dict() as row}: + store[row[key]] = row + + def snapshot(self): + with self._lock: + return { + "simulation": self._simulation, + "results": {q: list(s.values()) for q, s in self._results.items()}, + } + + # set_room(id, t, h, co2) and reset_room(id=None) write UPDATEs to Postgres; + # set_simulation(on) toggles a background task that assigns a random room new + # readings every few seconds. Drasi observes each write via CDC. +``` + +That's the whole Drasi side in one file: one source, two synthetic joins up the Room -> Floor -> Building hierarchy, six continuous queries (four of them aggregations), and a reaction that keeps a snapshot the UI renders and drives. + ## Clean Up {#cleanup} When you're finished, stop the app with **Ctrl+C** in the terminal, then remove the database container: diff --git a/tutorials/building-comfort/app.py b/tutorials/building-comfort/app.py index 5afdb52..13f0c2c 100644 --- a/tutorials/building-comfort/app.py +++ b/tutorials/building-comfort/app.py @@ -30,13 +30,14 @@ from typing import Any import streamlit as st -from demo.config import COMFORT_MAX, COMFORT_MIN -from demo.engine import ComfortEngine -from demo.queries import ( +from engine import ( BUILDING_COMFORT_UI, + COMFORT_MAX, + COMFORT_MIN, FLOOR_ALERT, FLOOR_COMFORT_LEVEL, ROOM_ALERT, + ComfortEngine, ) from streamlit_autorefresh import st_autorefresh diff --git a/tutorials/building-comfort/demo/__init__.py b/tutorials/building-comfort/demo/__init__.py deleted file mode 100644 index 327c330..0000000 --- a/tutorials/building-comfort/demo/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The Building Comfort demo package: an embedded Drasi engine and its queries.""" - -from .engine import ComfortEngine - -__all__ = ["ComfortEngine"] diff --git a/tutorials/building-comfort/demo/config.py b/tutorials/building-comfort/demo/config.py deleted file mode 100644 index 3457798..0000000 --- a/tutorials/building-comfort/demo/config.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Configuration for the Building Comfort tutorial. - -Everything the demo needs to reach Postgres, plus the demo's constants, read -from the environment with the same defaults the tutorial's database uses. Copy -``.env.example`` to ``.env`` (and ``source`` it) to override any of them. -""" - -from __future__ import annotations - -import os - -# --- PostgreSQL connection --------------------------------------------------- - -POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "localhost") -POSTGRES_PORT = int(os.environ.get("POSTGRES_PORT", "5732")) -POSTGRES_DATABASE = os.environ.get("POSTGRES_DATABASE", "building_comfort") -POSTGRES_USER = os.environ.get("POSTGRES_USER", "drasi_user") -POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "drasi_password") -POSTGRES_SSLMODE = os.environ.get("POSTGRES_SSLMODE", "prefer") - -# The Drasi source plugin spells the connection keys the same way the Node.js -# binding does (camelCase); Drasi passes plugin config through untouched. -SOURCE_CONNECTION = { - "host": POSTGRES_HOST, - "port": POSTGRES_PORT, - "database": POSTGRES_DATABASE, - "user": POSTGRES_USER, - "password": POSTGRES_PASSWORD, - "sslMode": POSTGRES_SSLMODE, -} - -# psycopg spells the database key ``dbname`` rather than ``database``. The demo -# writes room updates directly to Postgres through this connection so Drasi -# observes them via CDC -- exactly as a real building-management app would. -PSYCOPG_CONNECTION = { - "host": POSTGRES_HOST, - "port": POSTGRES_PORT, - "dbname": POSTGRES_DATABASE, - "user": POSTGRES_USER, - "password": POSTGRES_PASSWORD, -} - -# --- Drasi source / bootstrap ------------------------------------------------ - -# The Postgres source replicates these three tables. They are schema-qualified -# here (``public.Room``) while ``tableKeys`` uses the bare table name; the node -# label Drasi sees is the bare name, so the Cypher matches (r:Room) etc. -SOURCE_CONFIG = { - **SOURCE_CONNECTION, - "tables": ["public.Building", "public.Floor", "public.Room"], - "tableKeys": [ - {"table": "Building", "keyColumns": ["id"]}, - {"table": "Floor", "keyColumns": ["id"]}, - {"table": "Room", "keyColumns": ["id"]}, - ], - "slotName": "drasi_building_comfort_slot", - "publicationName": "drasi_building_comfort_pub", -} - -# The bootstrap provider loads the rows that already exist when the query starts, -# so the dashboard is populated before any change arrives. -BOOTSTRAP_CONFIG = {"kind": "postgres", **SOURCE_CONNECTION} - -# --- Demo constants ---------------------------------------------------------- - -# Comfortable defaults. floor(50 + (70-72) + (40-42) + 0) = 46, inside 40-50. -COMFORT_DEFAULTS = {"temperature": 70, "humidity": 40, "co2": 10} - -# The comfortable band. A room, floor or building outside it raises an alert. -COMFORT_MIN = 40 -COMFORT_MAX = 50 - -# How often simulation mode assigns a random room new readings, in seconds. -SIMULATION_INTERVAL_S = float(os.environ.get("SIMULATION_INTERVAL_S", "3")) diff --git a/tutorials/building-comfort/demo/queries.py b/tutorials/building-comfort/demo/queries.py deleted file mode 100644 index b09077d..0000000 --- a/tutorials/building-comfort/demo/queries.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright 2026 The Drasi Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The six continuous queries that power the demo. - -Each query is written out in full below so you can read (or copy) exactly what -Drasi runs. They all compute the same comfort level: - - floor(50 + (temperature - 72) + (humidity - 42) - + CASE WHEN co2 > 500 THEN (co2 - 500) / 25 ELSE 0 END) - -A value between 40 and 50 is comfortable; the seed values (70F, 40%, 10 ppm) -give floor(50 + (70-72) + (40-42) + 0) = 46. - -The Room -> Floor -> Building hierarchy is walked through two *synthetic joins*. -Drasi does not read Postgres foreign keys, so each query declares the -relationships it needs. A join is a plain mapping passed to ``add_query``. -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Any - -# --- Synthetic joins --------------------------------------------------------- -# Room.floor_id -> Floor.id, and Floor.building_id -> Building.id. - -PART_OF_FLOOR = { - "id": "PART_OF_FLOOR", - "keys": [ - {"label": "Room", "property": "floor_id"}, - {"label": "Floor", "property": "id"}, - ], -} - -PART_OF_BUILDING = { - "id": "PART_OF_BUILDING", - "keys": [ - {"label": "Floor", "property": "building_id"}, - {"label": "Building", "property": "id"}, - ], -} - -# --- Query ids --------------------------------------------------------------- -# The UI reads each query's result set from the snapshot by these ids. - -BUILDING_COMFORT_UI = "building-comfort-ui" -BUILDING_COMFORT_LEVEL = "building-comfort-level-calc" -FLOOR_COMFORT_LEVEL = "floor-comfort-level-calc" -ROOM_ALERT = "room-alert" -FLOOR_ALERT = "floor-alert" -BUILDING_ALERT = "building-alert" - - -@dataclass(frozen=True) -class Query: - """One continuous query: how to register it and how to index its rows.""" - - id: str - key: str # the RETURN column that identifies each row (its primary key) - cypher: str - joins: list[dict[str, Any]] = field(default_factory=list) - - -# One row per room, with its comfort level. Drives the building view. -BUILDING_COMFORT_UI_QUERY = Query( - id=BUILDING_COMFORT_UI, - key="RoomId", - joins=[PART_OF_FLOOR, PART_OF_BUILDING], - cypher=""" - MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) - RETURN - r.id AS RoomId, - r.name AS RoomName, - f.id AS FloorId, - f.name AS FloorName, - b.id AS BuildingId, - b.name AS BuildingName, - r.temperature AS Temperature, - r.humidity AS Humidity, - r.co2 AS CO2, - floor(50 + (r.temperature - 72) + (r.humidity - 42) - + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel - """, -) - -# The building's overall comfort: the average of each floor's average. -BUILDING_COMFORT_LEVEL_QUERY = Query( - id=BUILDING_COMFORT_LEVEL, - key="BuildingId", - joins=[PART_OF_FLOOR, PART_OF_BUILDING], - cypher=""" - MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) - WITH b, f, - floor(50 + (r.temperature - 72) + (r.humidity - 42) - + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel - WITH b, avg(RoomComfortLevel) AS FloorComfortLevel - WITH b, avg(FloorComfortLevel) AS ComfortLevel - RETURN b.id AS BuildingId, ComfortLevel - """, -) - -# Each floor's comfort: the average of the rooms on it. -FLOOR_COMFORT_LEVEL_QUERY = Query( - id=FLOOR_COMFORT_LEVEL, - key="FloorId", - joins=[PART_OF_FLOOR], - cypher=""" - MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) - WITH f, - floor(50 + (r.temperature - 72) + (r.humidity - 42) - + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel - WITH f, avg(RoomComfortLevel) AS ComfortLevel - RETURN f.id AS FloorId, ComfortLevel - """, -) - -# Only the rooms whose comfort is outside the 40-50 band. -ROOM_ALERT_QUERY = Query( - id=ROOM_ALERT, - key="RoomId", - cypher=""" - MATCH (r:Room) - WITH r.id AS RoomId, r.name AS RoomName, - floor(50 + (r.temperature - 72) + (r.humidity - 42) - + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel - WHERE ComfortLevel < 40 OR ComfortLevel > 50 - RETURN RoomId, RoomName, ComfortLevel - """, -) - -# Only the floors whose average comfort is outside the 40-50 band. -FLOOR_ALERT_QUERY = Query( - id=FLOOR_ALERT, - key="FloorId", - joins=[PART_OF_FLOOR], - cypher=""" - MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) - WITH f, - floor(50 + (r.temperature - 72) + (r.humidity - 42) - + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel - WITH f, avg(RoomComfortLevel) AS ComfortLevel - WHERE ComfortLevel < 40 OR ComfortLevel > 50 - RETURN f.id AS FloorId, f.name AS FloorName, ComfortLevel - """, -) - -# The building, only while its overall comfort is outside the 40-50 band. -BUILDING_ALERT_QUERY = Query( - id=BUILDING_ALERT, - key="BuildingId", - joins=[PART_OF_FLOOR, PART_OF_BUILDING], - cypher=""" - MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) - WITH b, f, - floor(50 + (r.temperature - 72) + (r.humidity - 42) - + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel - WITH b, f, avg(RoomComfortLevel) AS FloorComfortLevel - WITH b, avg(FloorComfortLevel) AS ComfortLevel - WHERE ComfortLevel < 40 OR ComfortLevel > 50 - RETURN b.id AS BuildingId, b.name AS BuildingName, ComfortLevel - """, -) - -# The engine registers these in order and subscribes one reaction to them all. -QUERIES = [ - BUILDING_COMFORT_UI_QUERY, - BUILDING_COMFORT_LEVEL_QUERY, - FLOOR_COMFORT_LEVEL_QUERY, - ROOM_ALERT_QUERY, - FLOOR_ALERT_QUERY, - BUILDING_ALERT_QUERY, -] diff --git a/tutorials/building-comfort/demo/engine.py b/tutorials/building-comfort/engine.py similarity index 53% rename from tutorials/building-comfort/demo/engine.py rename to tutorials/building-comfort/engine.py index a323b71..255788c 100644 --- a/tutorials/building-comfort/demo/engine.py +++ b/tutorials/building-comfort/engine.py @@ -14,6 +14,9 @@ """The embedded Drasi engine that powers the Streamlit UI. +This one file holds everything behind the demo: the configuration, the six +continuous queries, and the engine that runs them. + Streamlit re-runs its script top to bottom on every interaction, which is a poor fit for a long-lived async engine. So the Drasi engine runs on its own asyncio event loop in a background daemon thread, created exactly once (Streamlit caches @@ -45,6 +48,7 @@ import asyncio # noqa: E402 import random # noqa: E402 import threading # noqa: E402 +from dataclasses import dataclass, field # noqa: E402 from typing import Any # noqa: E402 import psycopg # noqa: E402 @@ -52,14 +56,221 @@ from drasi import Drasi # noqa: E402 from drasi.types import QueryResultEvent # noqa: E402 -from .config import ( # noqa: E402 - BOOTSTRAP_CONFIG, - COMFORT_DEFAULTS, - PSYCOPG_CONNECTION, - SIMULATION_INTERVAL_S, - SOURCE_CONFIG, -) -from .queries import QUERIES # noqa: E402 +# ============================================================================= +# Configuration -- everything read from the environment with defaults. +# ============================================================================= + +POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "localhost") +POSTGRES_PORT = int(os.environ.get("POSTGRES_PORT", "5732")) +POSTGRES_DATABASE = os.environ.get("POSTGRES_DATABASE", "building_comfort") +POSTGRES_USER = os.environ.get("POSTGRES_USER", "drasi_user") +POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "drasi_password") +POSTGRES_SSLMODE = os.environ.get("POSTGRES_SSLMODE", "prefer") + +# The Drasi source plugin spells the connection keys the same way the Node.js +# binding does (camelCase); Drasi passes plugin config through untouched. +SOURCE_CONNECTION = { + "host": POSTGRES_HOST, + "port": POSTGRES_PORT, + "database": POSTGRES_DATABASE, + "user": POSTGRES_USER, + "password": POSTGRES_PASSWORD, + "sslMode": POSTGRES_SSLMODE, +} + +# psycopg spells the database key ``dbname`` rather than ``database``. The demo +# writes room updates directly to Postgres through this connection so Drasi +# observes them via CDC -- exactly as a real building-management app would. +PSYCOPG_CONNECTION = { + "host": POSTGRES_HOST, + "port": POSTGRES_PORT, + "dbname": POSTGRES_DATABASE, + "user": POSTGRES_USER, + "password": POSTGRES_PASSWORD, +} + +# The Postgres source replicates these three tables. They are schema-qualified +# here (``public.Room``) while ``tableKeys`` uses the bare table name; the node +# label Drasi sees is the bare name, so the Cypher matches (r:Room) etc. +SOURCE_CONFIG = { + **SOURCE_CONNECTION, + "tables": ["public.Building", "public.Floor", "public.Room"], + "tableKeys": [ + {"table": "Building", "keyColumns": ["id"]}, + {"table": "Floor", "keyColumns": ["id"]}, + {"table": "Room", "keyColumns": ["id"]}, + ], + "slotName": "drasi_building_comfort_slot", + "publicationName": "drasi_building_comfort_pub", +} + +# The bootstrap provider loads the rows that already exist when the query starts, +# so the dashboard is populated before any change arrives. +BOOTSTRAP_CONFIG = {"kind": "postgres", **SOURCE_CONNECTION} + +# Comfortable defaults. floor(50 + (70-72) + (40-42) + 0) = 46, inside 40-50. +COMFORT_DEFAULTS = {"temperature": 70, "humidity": 40, "co2": 10} + +# The comfortable band. A room, floor or building outside it raises an alert. +COMFORT_MIN = 40 +COMFORT_MAX = 50 + +# How often simulation mode assigns a random room new readings, in seconds. +SIMULATION_INTERVAL_S = float(os.environ.get("SIMULATION_INTERVAL_S", "3")) + +# ============================================================================= +# The six continuous queries. +# ============================================================================= +# +# Each query is written out in full so you can read (or copy) exactly what Drasi +# runs. They all compute the same comfort level: +# +# floor(50 + (temperature - 72) + (humidity - 42) +# + CASE WHEN co2 > 500 THEN (co2 - 500) / 25 ELSE 0 END) +# +# A value between 40 and 50 is comfortable; the seed values (70F, 40%, 10 ppm) +# give floor(50 + (70-72) + (40-42) + 0) = 46. +# +# The Room -> Floor -> Building hierarchy is walked through two *synthetic +# joins*. Drasi does not read Postgres foreign keys, so each query declares the +# relationships it needs -- Room.floor_id -> Floor.id, and Floor.building_id -> +# Building.id -- as a plain mapping passed to ``add_query``. + +PART_OF_FLOOR = { + "id": "PART_OF_FLOOR", + "keys": [ + {"label": "Room", "property": "floor_id"}, + {"label": "Floor", "property": "id"}, + ], +} + +PART_OF_BUILDING = { + "id": "PART_OF_BUILDING", + "keys": [ + {"label": "Floor", "property": "building_id"}, + {"label": "Building", "property": "id"}, + ], +} + +# Query ids -- the UI reads each query's result set from the snapshot by these. +BUILDING_COMFORT_UI = "building-comfort-ui" +BUILDING_COMFORT_LEVEL = "building-comfort-level-calc" +FLOOR_COMFORT_LEVEL = "floor-comfort-level-calc" +ROOM_ALERT = "room-alert" +FLOOR_ALERT = "floor-alert" +BUILDING_ALERT = "building-alert" + + +@dataclass(frozen=True) +class Query: + """One continuous query: how to register it and how to index its rows.""" + + id: str + key: str # the RETURN column that identifies each row (its primary key) + cypher: str + joins: list[dict[str, Any]] = field(default_factory=list) + + +QUERIES = [ + # One row per room, with its comfort level. Drives the building view. + Query( + id=BUILDING_COMFORT_UI, + key="RoomId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + RETURN + r.id AS RoomId, + r.name AS RoomName, + f.id AS FloorId, + f.name AS FloorName, + b.id AS BuildingId, + b.name AS BuildingName, + r.temperature AS Temperature, + r.humidity AS Humidity, + r.co2 AS CO2, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + """, + ), + # The building's overall comfort: the average of each floor's average. + Query( + id=BUILDING_COMFORT_LEVEL, + key="BuildingId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH b, f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH b, avg(RoomComfortLevel) AS FloorComfortLevel + WITH b, avg(FloorComfortLevel) AS ComfortLevel + RETURN b.id AS BuildingId, ComfortLevel + """, + ), + # Each floor's comfort: the average of the rooms on it. + Query( + id=FLOOR_COMFORT_LEVEL, + key="FloorId", + joins=[PART_OF_FLOOR], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH f, avg(RoomComfortLevel) AS ComfortLevel + RETURN f.id AS FloorId, ComfortLevel + """, + ), + # Only the rooms whose comfort is outside the 40-50 band. + Query( + id=ROOM_ALERT, + key="RoomId", + cypher=""" + MATCH (r:Room) + WITH r.id AS RoomId, r.name AS RoomName, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN RoomId, RoomName, ComfortLevel + """, + ), + # Only the floors whose average comfort is outside the 40-50 band. + Query( + id=FLOOR_ALERT, + key="FloorId", + joins=[PART_OF_FLOOR], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor) + WITH f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH f, avg(RoomComfortLevel) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN f.id AS FloorId, f.name AS FloorName, ComfortLevel + """, + ), + # The building, only while its overall comfort is outside the 40-50 band. + Query( + id=BUILDING_ALERT, + key="BuildingId", + joins=[PART_OF_FLOOR, PART_OF_BUILDING], + cypher=""" + MATCH (r:Room)-[:PART_OF_FLOOR]->(f:Floor)-[:PART_OF_BUILDING]->(b:Building) + WITH b, f, + floor(50 + (r.temperature - 72) + (r.humidity - 42) + + CASE WHEN r.co2 > 500 THEN (r.co2 - 500) / 25 ELSE 0 END) AS RoomComfortLevel + WITH b, f, avg(RoomComfortLevel) AS FloorComfortLevel + WITH b, avg(FloorComfortLevel) AS ComfortLevel + WHERE ComfortLevel < 40 OR ComfortLevel > 50 + RETURN b.id AS BuildingId, b.name AS BuildingName, ComfortLevel + """, + ), +] + +# ============================================================================= +# The engine. +# ============================================================================= # Ranges chosen to straddle the comfortable band, so simulation makes alerts # come and go rather than sitting at one extreme. From c59a37becafbf4eb6854ea13d52805e75c5edd6a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 15:22:38 -0700 Subject: [PATCH 18/19] Fix broken drasi.io PostgreSQL source reference link The building-comfort tutorial linked to https://drasi.io/reference/sources/postgresql/, which 404s. Point it at the correct drasi-server source configuration page and regenerate the README. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tutorials/building-comfort/README.md | 2 +- tutorials/building-comfort/_index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/building-comfort/README.md b/tutorials/building-comfort/README.md index 5e119bc..c0288f0 100644 --- a/tutorials/building-comfort/README.md +++ b/tutorials/building-comfort/README.md @@ -167,7 +167,7 @@ await drasi.add_source( ) ``` -The source uses **logical replication (CDC)** to stream changes from the `Building`, `Floor`, and `Room` tables. `tableKeys` tells Drasi the primary key of each table so it can track row identity across changes. The `bootstrap` provider loads the rows that already exist when the query starts; after that, every `UPDATE` the UI makes flows to Drasi as a change. The table names are PascalCase so the node labels Drasi sees match the queries exactly: `(r:Room)`, `(f:Floor)`, `(b:Building)`. For every option the source accepts, see [Configure the PostgreSQL Source](https://drasi.io/reference/sources/postgresql/). +The source uses **logical replication (CDC)** to stream changes from the `Building`, `Floor`, and `Room` tables. `tableKeys` tells Drasi the primary key of each table so it can track row identity across changes. The `bootstrap` provider loads the rows that already exist when the query starts; after that, every `UPDATE` the UI makes flows to Drasi as a change. The table names are PascalCase so the node labels Drasi sees match the queries exactly: `(r:Room)`, `(f:Floor)`, `(b:Building)`. For every option the source accepts, see [Configure the PostgreSQL Source](https://drasi.io/drasi-server/how-to-guides/configuration/configure-sources/configure-postgresql-source/). ### The Continuous Queries diff --git a/tutorials/building-comfort/_index.md b/tutorials/building-comfort/_index.md index 8e1c2af..a70d4aa 100644 --- a/tutorials/building-comfort/_index.md +++ b/tutorials/building-comfort/_index.md @@ -201,7 +201,7 @@ await drasi.add_source( ) ``` -The source uses **logical replication (CDC)** to stream changes from the `Building`, `Floor`, and `Room` tables. `tableKeys` tells Drasi the primary key of each table so it can track row identity across changes. The `bootstrap` provider loads the rows that already exist when the query starts; after that, every `UPDATE` the UI makes flows to Drasi as a change. The table names are PascalCase so the node labels Drasi sees match the queries exactly: `(r:Room)`, `(f:Floor)`, `(b:Building)`. For every option the source accepts, see [Configure the PostgreSQL Source](https://drasi.io/reference/sources/postgresql/). +The source uses **logical replication (CDC)** to stream changes from the `Building`, `Floor`, and `Room` tables. `tableKeys` tells Drasi the primary key of each table so it can track row identity across changes. The `bootstrap` provider loads the rows that already exist when the query starts; after that, every `UPDATE` the UI makes flows to Drasi as a change. The table names are PascalCase so the node labels Drasi sees match the queries exactly: `(r:Room)`, `(f:Floor)`, `(b:Building)`. For every option the source accepts, see [Configure the PostgreSQL Source](https://drasi.io/drasi-server/how-to-guides/configuration/configure-sources/configure-postgresql-source/). ### The Continuous Queries From b24a2f5ab68181c930937d0123933796cc222f03 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag <daniel@gerlag.ca> Date: Fri, 31 Jul 2026 15:36:07 -0700 Subject: [PATCH 19/19] Address PR review: fail-fast DB readiness, modern asyncio, README grammar - setup-database scripts: track whether Postgres/MySQL actually became ready and exit with a clear error instead of proceeding to seeding (all three .sh and the curbside/getting-started .ps1; building-comfort .ps1 already did). - building-comfort engine.py: use asyncio.get_running_loop() over get_event_loop(), and asyncio.create_task() over ensure_future(). - README.md: fix 'They also render' to refer to the tutorials collectively. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 2 +- tutorials/building-comfort/engine.py | 6 +++--- .../building-comfort/scripts/setup-database.sh | 8 ++++++++ .../curbside-pickup/scripts/setup-database.ps1 | 16 ++++++++++++++-- .../curbside-pickup/scripts/setup-database.sh | 16 ++++++++++++++++ .../getting-started/scripts/setup-database.ps1 | 8 +++++++- .../getting-started/scripts/setup-database.sh | 8 ++++++++ 7 files changed, 57 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c4f8354..34f5728 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ Longer, hands-on walkthroughs live in [`tutorials/`](https://github.com/drasi-pr The [Building Comfort](https://github.com/drasi-project/drasi-python/tree/main/tutorials/building-comfort) tutorial builds a smart-building monitor from PostgreSQL CDC, six continuous queries with synthetic joins, and a Python reaction that drives a live Streamlit -UI. They also render on the [documentation site](https://drasi-project.github.io/drasi-python/docs/tutorials/). +UI. The tutorials also render on the [documentation site](https://drasi-project.github.io/drasi-python/docs/tutorials/). ## Development diff --git a/tutorials/building-comfort/engine.py b/tutorials/building-comfort/engine.py index 255788c..9914dac 100644 --- a/tutorials/building-comfort/engine.py +++ b/tutorials/building-comfort/engine.py @@ -380,7 +380,7 @@ def _query() -> list[str]: cur.execute('SELECT id FROM "Room" ORDER BY id') return [row[0] for row in cur.fetchall()] - return await asyncio.get_event_loop().run_in_executor(None, _query) + return await asyncio.get_running_loop().run_in_executor(None, _query) # -- readiness ----------------------------------------------------------- @@ -434,7 +434,7 @@ async def _set_simulation(self, enabled: bool) -> None: if enabled and self._sim_task is None: if not self._room_ids: self._room_ids = await self._load_room_ids() - self._sim_task = asyncio.ensure_future(self._simulate()) + self._sim_task = asyncio.create_task(self._simulate()) self._simulation = True elif not enabled and self._sim_task is not None: self._sim_task.cancel() @@ -442,7 +442,7 @@ async def _set_simulation(self, enabled: bool) -> None: self._simulation = False async def _simulate(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() try: while True: room = random.choice(self._room_ids) diff --git a/tutorials/building-comfort/scripts/setup-database.sh b/tutorials/building-comfort/scripts/setup-database.sh index 9b1d14d..5f14b3a 100755 --- a/tutorials/building-comfort/scripts/setup-database.sh +++ b/tutorials/building-comfort/scripts/setup-database.sh @@ -60,9 +60,11 @@ $COMPOSE_CMD up -d echo "Waiting for PostgreSQL to be ready..." MAX_RETRIES=30 RETRY_COUNT=0 +POSTGRES_READY=false while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do if docker exec building-comfort-postgres pg_isready -U postgres -d building_comfort &> /dev/null; then echo "PostgreSQL is ready!" + POSTGRES_READY=true break fi RETRY_COUNT=$((RETRY_COUNT + 1)) @@ -70,6 +72,12 @@ while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do sleep 2 done +if [ "$POSTGRES_READY" != "true" ]; then + echo "Error: PostgreSQL did not become ready within the timeout." + echo "Check logs with: docker logs building-comfort-postgres" + exit 1 +fi + if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then echo "Error: PostgreSQL failed to start within the timeout" echo "Check logs with: docker logs building-comfort-postgres" diff --git a/tutorials/curbside-pickup/scripts/setup-database.ps1 b/tutorials/curbside-pickup/scripts/setup-database.ps1 index 9d2917c..4906460 100644 --- a/tutorials/curbside-pickup/scripts/setup-database.ps1 +++ b/tutorials/curbside-pickup/scripts/setup-database.ps1 @@ -50,18 +50,30 @@ try { } Write-Host "Waiting for PostgreSQL to be ready..." + $pgReady = $false for ($i = 1; $i -le 30; $i++) { docker exec curbside-pickup-postgres pg_isready -h localhost -U postgres -d RetailOperations 2>&1 | Out-Null - if ($LASTEXITCODE -eq 0) { Write-Host "PostgreSQL is ready!"; break } + if ($LASTEXITCODE -eq 0) { Write-Host "PostgreSQL is ready!"; $pgReady = $true; break } Write-Host " Waiting... ($i/30)"; Start-Sleep -Seconds 2 } + if (-not $pgReady) { + Write-Host "Error: PostgreSQL did not become ready within the timeout." + Write-Host "Check logs with: docker logs curbside-pickup-postgres" + exit 1 + } Write-Host "Waiting for MySQL to be ready..." + $mysqlReady = $false for ($i = 1; $i -le 45; $i++) { docker exec curbside-pickup-mysql mysqladmin ping -h localhost -uroot -p"$MysqlRootPassword" 2>&1 | Out-Null - if ($LASTEXITCODE -eq 0) { Write-Host "MySQL is ready!"; break } + if ($LASTEXITCODE -eq 0) { Write-Host "MySQL is ready!"; $mysqlReady = $true; break } Write-Host " Waiting... ($i/45)"; Start-Sleep -Seconds 2 } + if (-not $mysqlReady) { + Write-Host "Error: MySQL did not become ready within the timeout." + Write-Host "Check logs with: docker logs curbside-pickup-mysql" + exit 1 + } Write-Host "" Write-Host "Seeding PostgreSQL (orders)..." diff --git a/tutorials/curbside-pickup/scripts/setup-database.sh b/tutorials/curbside-pickup/scripts/setup-database.sh index 8e2361e..7c2b9af 100755 --- a/tutorials/curbside-pickup/scripts/setup-database.sh +++ b/tutorials/curbside-pickup/scripts/setup-database.sh @@ -59,25 +59,41 @@ echo "Starting PostgreSQL and MySQL..." $COMPOSE_CMD up -d echo "Waiting for PostgreSQL to be ready..." +POSTGRES_READY=false for i in $(seq 1 30); do if docker exec curbside-pickup-postgres pg_isready -h localhost -U postgres -d RetailOperations &> /dev/null; then echo "PostgreSQL is ready!" + POSTGRES_READY=true break fi echo " Waiting... ($i/30)" sleep 2 done +if [ "$POSTGRES_READY" != "true" ]; then + echo "Error: PostgreSQL did not become ready within the timeout." + echo "Check logs with: docker logs curbside-pickup-postgres" + exit 1 +fi + echo "Waiting for MySQL to be ready..." +MYSQL_READY=false for i in $(seq 1 45); do if docker exec curbside-pickup-mysql mysqladmin ping -h localhost -uroot -p"$MYSQL_ROOT_PASSWORD" &> /dev/null; then echo "MySQL is ready!" + MYSQL_READY=true break fi echo " Waiting... ($i/45)" sleep 2 done +if [ "$MYSQL_READY" != "true" ]; then + echo "Error: MySQL did not become ready within the timeout." + echo "Check logs with: docker logs curbside-pickup-mysql" + exit 1 +fi + echo echo "Seeding PostgreSQL (orders)..." docker exec -i curbside-pickup-postgres \ diff --git a/tutorials/getting-started/scripts/setup-database.ps1 b/tutorials/getting-started/scripts/setup-database.ps1 index c222c85..f284eda 100644 --- a/tutorials/getting-started/scripts/setup-database.ps1 +++ b/tutorials/getting-started/scripts/setup-database.ps1 @@ -46,11 +46,17 @@ try { } Write-Host "Waiting for PostgreSQL to be ready..." + $pgReady = $false for ($i = 1; $i -le 30; $i++) { docker exec getting-started-postgres pg_isready -h localhost -U postgres -d getting_started 2>&1 | Out-Null - if ($LASTEXITCODE -eq 0) { Write-Host "PostgreSQL is ready!"; break } + if ($LASTEXITCODE -eq 0) { Write-Host "PostgreSQL is ready!"; $pgReady = $true; break } Write-Host " Waiting... ($i/30)"; Start-Sleep -Seconds 2 } + if (-not $pgReady) { + Write-Host "Error: PostgreSQL did not become ready within the timeout." + Write-Host "Check logs with: docker logs getting-started-postgres" + exit 1 + } Write-Host "" Write-Host "Applying schema and seed data..." diff --git a/tutorials/getting-started/scripts/setup-database.sh b/tutorials/getting-started/scripts/setup-database.sh index 6c83cfb..ea5cf99 100755 --- a/tutorials/getting-started/scripts/setup-database.sh +++ b/tutorials/getting-started/scripts/setup-database.sh @@ -58,15 +58,23 @@ echo "Starting PostgreSQL with WAL replication..." $COMPOSE_CMD up -d echo "Waiting for PostgreSQL to be ready..." +POSTGRES_READY=false for i in $(seq 1 30); do if docker exec getting-started-postgres pg_isready -h localhost -U postgres -d getting_started &> /dev/null; then echo "PostgreSQL is ready!" + POSTGRES_READY=true break fi echo " Waiting... ($i/30)" sleep 2 done +if [ "$POSTGRES_READY" != "true" ]; then + echo "Error: PostgreSQL did not become ready within the timeout." + echo "Check logs with: docker logs getting-started-postgres" + exit 1 +fi + echo echo "Applying schema and seed data..." docker exec -i getting-started-postgres \