Skip to content

Commit

Permalink
Framework/Gradio: Add basic example about Gradio, reading sys.summits
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Jun 27, 2024
1 parent 891a656 commit 0f17840
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ updates:
schedule:
interval: "daily"

- directory: "/framework/gradio"
package-ecosystem: "pip"
schedule:
interval: "daily"

- directory: "/framework/streamlit"
package-ecosystem: "pip"
schedule:
Expand Down
74 changes: 74 additions & 0 deletions .github/workflows/framework-gradio.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Gradio

on:
pull_request:
branches: ~
paths:
- '.github/workflows/framework-gradio.yml'
- 'framework/gradio/**'
- '/requirements.txt'
push:
branches: [ main ]
paths:
- '.github/workflows/framework-gradio.yml'
- 'framework/gradio/**'
- '/requirements.txt'

# Allow job to be triggered manually.
workflow_dispatch:

# Run job each night after CrateDB nightly has been published.
schedule:
- cron: '0 3 * * *'

# Cancel in-progress jobs when pushing to the same branch.
concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.ref }}

jobs:
test:
name: "
Python: ${{ matrix.python-version }}
CrateDB: ${{ matrix.cratedb-version }}
on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ 'ubuntu-latest' ]
python-version: [ '3.10', '3.11', '3.12' ]
cratedb-version: [ 'nightly' ]

services:
cratedb:
image: crate/crate:${{ matrix.cratedb-version }}
ports:
- 4200:4200
- 5432:5432
env:
CRATE_HEAP_SIZE: 4g

steps:

- name: Acquire sources
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
cache: 'pip'
cache-dependency-path: |
requirements.txt
framework/gradio/requirements.txt
framework/gradio/requirements-dev.txt
- name: Install utilities
run: |
pip install -r requirements.txt
- name: Validate framework/gradio
run: |
ngr test --accept-no-venv framework/gradio
1 change: 1 addition & 0 deletions framework/gradio/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/flagged
62 changes: 62 additions & 0 deletions framework/gradio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Gradio with CrateDB Example

## About
Demonstrate connectivity from Gradio to CrateDB.

## Configuration
Configure database connection address and credentials by using
an SQLAlchemy connection string, see variable `CRATEDB_SQLALCHEMY_URL`
in `basic_sys_summits.py`. Please make sure to use valid credentials
matching your environment.

## Usage

### CrateDB on localhost
To start a CrateDB instance on your machine, invoke:
```shell
docker run -it --rm \
--publish=4200:4200 --publish=5432:5432 \
--env=CRATE_HEAP_SIZE=2g \
crate:latest -Cdiscovery.type=single-node
```

### CrateDB Cloud
Please have a look at the [basic_sys_summits.py](basic_sys_summits.py) program
as a blueprint. It includes a `CRATEDB_SQLALCHEMY_URL` variable definition
that configures the application to connect to CrateDB Cloud.
```python
CRATEDB_SQLALCHEMY_URL = "crate://admin:g_,8.F0fNbVSk0.*!n54S5c,@example.gke1.us-central1.gcp.cratedb.net:4200?ssl=true"```
```

Install dependencies.
```shell
pip install -r requirements.txt
```

Invoke Gradio to serve the application.
```shell
gradio basic_sys_summits.py
```

## Screenshot

Enjoy the list of mountains.

![image](https://github.com/crate/cratedb-examples/assets/453543/af417966-b694-45ec-9391-f0e99a2ac014)


## Development

Acquire `cratedb-example` repository, and set up development sandbox.
```shell
git clone https://github.com/crate/cratedb-examples
cd cratedb-examples
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

Invoke the integration test cases.
```shell
ngr test framework/gradio
```
48 changes: 48 additions & 0 deletions framework/gradio/basic_sys_summits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
A basic Gradio application connecting to CrateDB using SQLAlchemy.
It reads the built-in `sys.summits` table into a dataframe, and
displays its contents.
- https://www.gradio.app/guides/connecting-to-a-database
- https://www.gradio.app/docs/gradio/dataframe
- https://cratedb.com/docs/sqlalchemy-cratedb/
"""
import gradio as gr
import pandas as pd

# Connect to CrateDB on localhost.
CRATEDB_SQLALCHEMY_URL = "crate://localhost:4200"

# Connect to CrateDB Cloud.
# CRATEDB_SQLALCHEMY_URL = "crate://admin:g_,8.F0fNbVSk0.*!n54S5c,@example.gke1.us-central1.gcp.cratedb.net:4200?ssl=true"


def get_sys_summits():
"""
Query the database using pandas.
"""
return pd.read_sql('SELECT * FROM "sys"."summits";', con=CRATEDB_SQLALCHEMY_URL)


def get_dataframe():
"""
Create a dataframe widget.
"""
df = get_sys_summits()
return gr.DataFrame(df)


# Define the Gradio interface.
demo = gr.Interface(
fn=get_dataframe,
inputs=[],
outputs=["dataframe"],
title="Gradio with CrateDB Example",
live=True,
allow_flagging="never",
)


if __name__ == "__main__":
demo.launch()
12 changes: 12 additions & 0 deletions framework/gradio/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[tool.pytest.ini_options]
minversion = "2.0"
addopts = """
-rfEX -p pytester --strict-markers --verbosity=3
--capture=no
"""
log_level = "DEBUG"
log_cli_level = "DEBUG"
testpaths = ["*.py"]
xfail_strict = true
markers = [
]
2 changes: 2 additions & 0 deletions framework/gradio/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gradio-client==1.0.*
pytest<9
2 changes: 2 additions & 0 deletions framework/gradio/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gradio==4.*
sqlalchemy-cratedb==0.38.0
84 changes: 84 additions & 0 deletions framework/gradio/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import logging
import shlex
import subprocess
import sys
import threading
import time
from urllib.error import HTTPError
from urllib.request import urlopen

import pytest
from gradio_client import Client


logger = logging.getLogger()

GRADIO_SERVER_PORT = "7861"
GRADIO_API_URL = f"http://localhost:{GRADIO_SERVER_PORT}"


def run(command: str, env=None, timeout: int = None):
"""
Invoke a command in a subprocess.
"""
env = env or {}
env["PATH"] = ""
subprocess.call(shlex.split(command), env=env, timeout=timeout)


def check_url(url):
"""
Check if a service is reachable.
Makes a simple GET request to path of the HTTP endpoint. Service is
available if returned status code is < 500.
"""
try:
r = urlopen(url)
return r.code < 500
except HTTPError as e:
# If service returns e.g. a 404 it's ok
return e.code < 500
except Exception:
# Possible service not yet started
return False


@pytest.fixture(scope="session", autouse=True)
def run_server():

def server_thread():
print("sys.exec:", sys.executable)
try:
run(f"{sys.executable} basic_sys_summits.py", env={"GRADIO_SERVER_PORT": GRADIO_SERVER_PORT}, timeout=5)
except subprocess.TimeoutExpired:
pass

thread = threading.Thread(target=server_thread)
try:
thread.start()
except KeyboardInterrupt:
pass

while not check_url(GRADIO_API_URL):
logger.info("Waiting for Gradio API to come up")
time.sleep(0.2)


def test_read_sys_summits():
"""
Verify reading CrateDB's built-in `sys.summits` database table through the Gradio API.
- https://www.gradio.app/guides/getting-started-with-the-python-client
- https://www.gradio.app/docs/python-client/
- https://www.gradio.app/docs/gradio/dataframe
"""

# Connect to Gradio API, and submit a request.
client = Client(GRADIO_API_URL)
result = client.predict(api_name="/predict")

# Verify result.
assert "mountain" in result["value"]["headers"]
assert len(result["value"]["data"]) > 80
assert result["value"]["data"][0][5] == "Mont Blanc"

0 comments on commit 0f17840

Please sign in to comment.