Skip to content

Commit

Permalink
(wip)
Browse files Browse the repository at this point in the history
q
  • Loading branch information
oliverlambson committed Aug 25, 2024
1 parent 21d9740 commit 9fe1878
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 31 deletions.
4 changes: 2 additions & 2 deletions bored-charts/boredcharts/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
{% for report in reports %}
<li>
<a
href="{{ url_for("report", report_name=report.name) }}"
href="{{ url_for(report) }}"
class="text-blue-500 underline underline-offset-2 hover:underline-offset-4"
>{{ report.name }}</a
>{{ report }}</a
>
</li>
{% endfor %}
Expand Down
2 changes: 1 addition & 1 deletion bored-charts/boredcharts/templates/report.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="container mx-auto">
<div class="prose">
{% filter markdown %}
{%- include report ~ ".md" %}
{%- include report %}
{% endfilter %}
</div>
</div>
Expand Down
107 changes: 80 additions & 27 deletions bored-charts/boredcharts/webapp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from collections.abc import Awaitable, Callable
from enum import Enum
from pathlib import Path
from typing import NamedTuple

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
Expand All @@ -14,18 +16,24 @@
module_root = Path(__file__).parent.absolute()


class ReportEndpoint(NamedTuple):
name: str
path: str
endpoint: Callable[..., Awaitable[HTMLResponse]]


def boredcharts(
pages: Path,
figures: FigureRouter | list[FigureRouter],
*,
name: str = "bored-charts",
index_name: str = "bored-charts",
) -> FastAPI:
"""Creates a boredcharts app."""
static_root = module_root / "static"
templates_root = module_root / "templates"
Path(static_root / "plotlyjs.min.js").write_text(get_plotlyjs())

app = FastAPI(title=name)
app = FastAPI(title=index_name)

app.mount(
"/static",
Expand All @@ -46,40 +54,81 @@ def boredcharts(
undefined=StrictUndefined,
),
)
templates.env.globals["title"] = name
templates.env.globals["reports"] = [
{"name": f.stem}
for f in sorted(
pages.glob("*.md"),
reverse=True,
)
]
templates.env.globals["title"] = index_name
templates.env.filters["markdown"] = md_to_html
templates.env.globals["figure"] = figure
templates.env.globals["row"] = row

@app.get("/healthz")
async def healthz() -> dict[str, str]:
return {"status": "ok"}
# --- Report indices -------------------------------------------------------
def create_index_endpoint(
reports: list[str],
) -> Callable[[Request], Awaitable[HTMLResponse]]:
async def index_endpoint(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
"index.html",
{
"request": request,
"reports": reports,
},
)

return index_endpoint

def create_report_endpoint(
report: str,
) -> Callable[[Request], Awaitable[HTMLResponse]]:
async def report_endpoint(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
"report.html",
{
"request": request,
"report": report,
},
)

return report_endpoint

@app.get("/", tags=["reports"])
async def index(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
"index.html",
{"request": request},
for dir, dirs, files in pages.walk():
dir = dir.relative_to(pages)
report_files = [dir / file for file in files if file.endswith(".md")]

index_path = f"/{dir}" if dir != Path(".") else "/"
index_name = str(Path("index") / dir).replace("/", ".")
tag = str(Path("reports") / dir).replace("/", ":")

report_endpoints: list[ReportEndpoint] = []
for report_file in report_files:
report_path = f"{Path(index_path) / report_file.stem}"
report_name = report_path.replace("/", ".").strip(".")

report_endpoints.append(
ReportEndpoint(
name=report_name,
path=report_path,
endpoint=create_report_endpoint(str(report_file)),
)
)

subindex_names = [str(Path("index") / d).replace("/", ".") for d in dirs]
index_endpoint = create_index_endpoint(
subindex_names + [r.name for r in report_endpoints]
)

# TODO: pass pages path into framework, auto generate this route
@app.get("/report/{report_name}", name="report", tags=["reports"])
async def report(report_name: str, request: Request) -> HTMLResponse:
return templates.TemplateResponse(
"report.html",
{
"request": request,
"report": report_name,
},
app.router.add_api_route(
path=index_path,
endpoint=index_endpoint,
name=index_name,
tags=[tag],
)
for report_endpoint in report_endpoints:
app.router.add_api_route(
path=report_endpoint.path,
endpoint=report_endpoint.endpoint,
name=report_endpoint.name,
tags=[tag],
)

# --- Figures --------------------------------------------------------------
if not isinstance(figures, list):
figures = [figures]
for router in figures:
Expand All @@ -93,4 +142,8 @@ async def report(report_name: str, request: Request) -> HTMLResponse:

app.include_router(router, prefix="/figure", tags=tags)

@app.get("/healthz")
async def healthz() -> dict[str, str]:
return {"status": "ok"}

return app
5 changes: 4 additions & 1 deletion examples/full/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

RUN useradd --uid 1001 --user-group --home-dir=/home/bored-charts --create-home --shell=/bin/false bored-charts
RUN apt-get update && apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/* &&\
useradd --uid 1001 --user-group --home-dir=/home/bored-charts --create-home --shell=/bin/false bored-charts

USER bored-charts

COPY --chown=bored-charts pyproject.toml /app/pyproject.toml
Expand Down
1 change: 1 addition & 0 deletions examples/full/pages/more/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hi

0 comments on commit 9fe1878

Please sign in to comment.