Skip to content

Commit

Permalink
example: serve notebooks from github (#3072)
Browse files Browse the repository at this point in the history
  • Loading branch information
mscolnick authored Dec 6, 2024
1 parent b0dea05 commit 84e5f35
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
14 changes: 14 additions & 0 deletions examples/frameworks/fastapi-github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Server marimo notebooks from a GitHub repository

In this example, we programmatically create multiple marimo apps from a GitHub repository, and then serve them as a single FastAPI app.

This example includes:

- Serving multiple marimo apps from a GitHub repository
- A home page listing all the apps
- Basic logging

## Running the app

1. [Install `uv`](https://github.com/astral-sh/uv/?tab=readme-ov-file#installation)
2. Run the app with `uv run --no-project main.py`
108 changes: 108 additions & 0 deletions examples/frameworks/fastapi-github/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "fastapi",
# "marimo",
# "starlette",
# "requests",
# "pydantic",
# "jinja2",
# ]
# ///
import tempfile
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
import marimo
import os
import logging
import requests
from pathlib import Path

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Constants
GITHUB_REPO = "marimo-team/marimo"
ROOT_DIR = "examples/ui"
templates_dir = os.path.join(os.path.dirname(__file__), "templates")

# Set up templates
templates = Jinja2Templates(directory=templates_dir)


def download_github_files(repo: str, path: str = "") -> list[tuple[str, str]]:
"""Download files from GitHub repo, returns list of (file_path, content)"""
api_url = f"https://api.github.com/repos/{repo}/contents/{path}"
response = requests.get(api_url)
response.raise_for_status()

files: list[tuple[str, str]] = []
for item in response.json():
print(item)
if item["type"] == "file" and item["name"].endswith(".py"):
content_response = requests.get(item["download_url"])
files.append(
(os.path.join(path, item["name"]), content_response.text)
)
elif item["type"] == "dir":
files.extend(
download_github_files(repo, os.path.join(path, item["name"]))
)
return files


tmp_dir = tempfile.TemporaryDirectory()


def setup_apps():
"""Download and setup marimo apps from GitHub"""
files = download_github_files(GITHUB_REPO, ROOT_DIR)
server = marimo.create_asgi_app()
app_names: list[str] = []

for file_path, content in files:
app_name = Path(file_path).stem
local_path = Path(tmp_dir.name) / file_path

# Create directories if they don't exist
local_path.parent.mkdir(parents=True, exist_ok=True)

# Write file content
local_path.write_text(content)

# Add to marimo server
server = server.with_app(path=f"/{app_name}", root=str(local_path))
app_names.append(app_name)
logger.info(f"Added app: {app_name} from {file_path}")

return server, app_names


# Create a FastAPI app
app = FastAPI()

# Setup marimo apps
server, app_names = setup_apps()


@app.get("/")
async def home(request: Request):
return templates.TemplateResponse(
"home.html", {"request": request, "app_names": app_names}
)


@app.get("/ping")
async def root():
return {"message": "pong"}


# Mount the marimo server
app.mount("/", server.build())

# Run the server
if __name__ == "__main__":
import uvicorn

uvicorn.run(app, host="localhost", port=8000, log_level="info")
27 changes: 27 additions & 0 deletions examples/frameworks/fastapi-github/templates/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold">Welcome, these apps are loaded dynamically from a GitHub repository!</h1>
</div>
<h2 class="text-2xl font-semibold mb-4">Available Applications:</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{% for app_name in app_names %}
<a href="/{{ app_name }}" class="block" target="{{ app_name }}">
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300 p-4">
<h3 class="text-lg font-semibold text-blue-600 hover:text-blue-800">{{ app_name }}</h3>
<p class="text-gray-600 mt-2">Click to open application</p>
</div>
</a>
{% endfor %}
</div>
</div>
</body>
</html>

0 comments on commit 84e5f35

Please sign in to comment.