From db7d7c88a0b69e18313f1fb404294f4fa5a29019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ekespong?= <43400207+aekespong@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:40:33 +0100 Subject: [PATCH 1/4] Changed Table[User] to use data_model=User --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0c1e0014..91708000 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,9 @@ def users_table() -> list[AnyComponent]: c.Page( # Page provides a basic container for components components=[ c.Heading(text='Users', level=2), # renders `

Users

` - c.Table[User]( # c.Table is a generic component parameterized with the model used for rows + c.Table( data=users, + data_model=User, # data_model defines a model used for rows # define two columns for the table columns=[ # the first is the users, name rendered as a link to their profile @@ -84,7 +85,6 @@ def users_table() -> list[AnyComponent]: ), ] - @app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True) def user_profile(user_id: int) -> list[AnyComponent]: """ From a75b1e0e3f2e8af0841e1b4f5fba03d3278a6631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ekespong?= <43400207+aekespong@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:58:19 +0100 Subject: [PATCH 2/4] Added sample from README. It includes __init__.py which is required. --- Makefile | 4 +++ sample/__init__.py | 43 ++++++++++++++++++++++++ sample/main.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 sample/__init__.py create mode 100644 sample/main.py diff --git a/Makefile b/Makefile index bced37ae..5a9c8e5d 100644 --- a/Makefile +++ b/Makefile @@ -42,5 +42,9 @@ testcov: test dev: uvicorn demo:app --reload --reload-dir . +.PHONY: sample +sample: + uvicorn sample.main:app --reload --reload-dir . + .PHONY: all all: testcov lint diff --git a/sample/__init__.py b/sample/__init__.py new file mode 100644 index 00000000..0494adbf --- /dev/null +++ b/sample/__init__.py @@ -0,0 +1,43 @@ +from __future__ import annotations as _annotations + +import sys +from contextlib import asynccontextmanager + +from fastapi import FastAPI +from fastapi.responses import HTMLResponse, PlainTextResponse +from fastui import prebuilt_html +from fastui.dev import dev_fastapi_app +from httpx import AsyncClient + +# from .app import create_db + + +# @asynccontextmanager +# async def lifespan(app_: FastAPI): +# await create_db() +# async with AsyncClient() as client: +# app_.state.httpx_client = client +# yield + + +frontend_reload = '--reload' in sys.argv +if frontend_reload: + # dev_fastapi_app reloads in the browser when the Python source changes + app = dev_fastapi_app() +else: + app = FastAPI() + + +@app.get('/robots.txt', response_class=PlainTextResponse) +async def robots_txt() -> str: + return 'User-agent: *\nAllow: /' + + +@app.get('/favicon.ico', status_code=404, response_class=PlainTextResponse) +async def favicon_ico() -> str: + return 'page not found' + + +@app.get('/{path:path}') +async def html_landing() -> HTMLResponse: + return HTMLResponse(prebuilt_html(title='FastUI Demo')) diff --git a/sample/main.py b/sample/main.py new file mode 100644 index 00000000..5b3f2f73 --- /dev/null +++ b/sample/main.py @@ -0,0 +1,81 @@ +from datetime import date + +from fastapi import FastAPI, HTTPException +from fastapi.responses import HTMLResponse +from fastui import FastUI, AnyComponent, prebuilt_html, components as c +from fastui.components.display import DisplayMode, DisplayLookup +from fastui.events import GoToEvent, BackEvent +from pydantic import BaseModel, Field + + +class User(BaseModel): + id: int + name: str + dob: date = Field(title="Date of Birth") + + +# define some users +users = [ + User(id=1, name="John", dob=date(1990, 1, 1)), + User(id=2, name="Jack", dob=date(1991, 1, 1)), + User(id=3, name="Jill", dob=date(1992, 1, 1)), + User(id=4, name="Jane", dob=date(1993, 1, 1)), +] + +app = FastAPI() + + +@app.get("/api/", response_model=FastUI, response_model_exclude_none=True) +def users_table() -> list[AnyComponent]: + """ + Show a table of four users, `/api` is the endpoint the frontend will connect to + when a user visits `/` to fetch components to render. + """ + return [ + c.Page( # Page provides a basic container for components + components=[ + c.Heading(text="Users", level=2), # renders `

Users

` + c.Table( # c.Table is a generic component parameterized with the model used for rows + data=users, + data_model=User, # data_model defines a model used for rows + # define two columns for the table + columns=[ + # the first is the users, name rendered as a link to their profile + DisplayLookup( + field="name", on_click=GoToEvent(url="/user/{id}/") + ), + # the second is the date of birth, rendered as a date + DisplayLookup(field="dob", mode=DisplayMode.date), + ], + ), + ] + ), + ] + + +@app.get( + "/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True +) +def user_profile(user_id: int) -> list[AnyComponent]: + """ + User profile page, the frontend will fetch this when the user visits `/user/{id}/`. + """ + try: + user = next(u for u in users if u.id == user_id) + except StopIteration: + raise HTTPException(status_code=404, detail="User not found") + return [ + c.Page( + components=[ + c.Heading(text=user.name, level=2), + c.Link(components=[c.Text(text="Back")], on_click=BackEvent()), + c.Details(data=user), + ] + ), + ] + + +@app.get("/{path:path}") +async def html_landing() -> HTMLResponse: + """Simple HTML page which serves the React app, comes last as it matches all paths.""" + return HTMLResponse(prebuilt_html(title="FastUI Demo")) From 769b978bf4ac9514fdfb81d63c691c1f269b1ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ekespong?= <43400207+aekespong@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:16:07 +0000 Subject: [PATCH 3/4] Fixed lint error --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 91708000..505f5e2f 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ def users_table() -> list[AnyComponent]: c.Heading(text='Users', level=2), # renders `

Users

` c.Table( data=users, - data_model=User, # data_model defines a model used for rows + # data_model defines a model used for rows + data_model=User, # define two columns for the table columns=[ # the first is the users, name rendered as a link to their profile From 9973f4df926d0d2fadeeb2ec7364b76bbb86ae15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ekespong?= <43400207+aekespong@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:45:04 +0100 Subject: [PATCH 4/4] Revert "Added sample from README. It includes __init__.py which is required." This reverts commit a75b1e0e3f2e8af0841e1b4f5fba03d3278a6631. --- Makefile | 4 --- sample/__init__.py | 43 ------------------------ sample/main.py | 81 ---------------------------------------------- 3 files changed, 128 deletions(-) delete mode 100644 sample/__init__.py delete mode 100644 sample/main.py diff --git a/Makefile b/Makefile index 55cf0a8a..023f415c 100644 --- a/Makefile +++ b/Makefile @@ -46,9 +46,5 @@ typescript-models: dev: uvicorn demo:app --reload --reload-dir . -.PHONY: sample -sample: - uvicorn sample.main:app --reload --reload-dir . - .PHONY: all all: testcov lint diff --git a/sample/__init__.py b/sample/__init__.py deleted file mode 100644 index 0494adbf..00000000 --- a/sample/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import annotations as _annotations - -import sys -from contextlib import asynccontextmanager - -from fastapi import FastAPI -from fastapi.responses import HTMLResponse, PlainTextResponse -from fastui import prebuilt_html -from fastui.dev import dev_fastapi_app -from httpx import AsyncClient - -# from .app import create_db - - -# @asynccontextmanager -# async def lifespan(app_: FastAPI): -# await create_db() -# async with AsyncClient() as client: -# app_.state.httpx_client = client -# yield - - -frontend_reload = '--reload' in sys.argv -if frontend_reload: - # dev_fastapi_app reloads in the browser when the Python source changes - app = dev_fastapi_app() -else: - app = FastAPI() - - -@app.get('/robots.txt', response_class=PlainTextResponse) -async def robots_txt() -> str: - return 'User-agent: *\nAllow: /' - - -@app.get('/favicon.ico', status_code=404, response_class=PlainTextResponse) -async def favicon_ico() -> str: - return 'page not found' - - -@app.get('/{path:path}') -async def html_landing() -> HTMLResponse: - return HTMLResponse(prebuilt_html(title='FastUI Demo')) diff --git a/sample/main.py b/sample/main.py deleted file mode 100644 index 5b3f2f73..00000000 --- a/sample/main.py +++ /dev/null @@ -1,81 +0,0 @@ -from datetime import date - -from fastapi import FastAPI, HTTPException -from fastapi.responses import HTMLResponse -from fastui import FastUI, AnyComponent, prebuilt_html, components as c -from fastui.components.display import DisplayMode, DisplayLookup -from fastui.events import GoToEvent, BackEvent -from pydantic import BaseModel, Field - - -class User(BaseModel): - id: int - name: str - dob: date = Field(title="Date of Birth") - - -# define some users -users = [ - User(id=1, name="John", dob=date(1990, 1, 1)), - User(id=2, name="Jack", dob=date(1991, 1, 1)), - User(id=3, name="Jill", dob=date(1992, 1, 1)), - User(id=4, name="Jane", dob=date(1993, 1, 1)), -] - -app = FastAPI() - - -@app.get("/api/", response_model=FastUI, response_model_exclude_none=True) -def users_table() -> list[AnyComponent]: - """ - Show a table of four users, `/api` is the endpoint the frontend will connect to - when a user visits `/` to fetch components to render. - """ - return [ - c.Page( # Page provides a basic container for components - components=[ - c.Heading(text="Users", level=2), # renders `

Users

` - c.Table( # c.Table is a generic component parameterized with the model used for rows - data=users, - data_model=User, # data_model defines a model used for rows - # define two columns for the table - columns=[ - # the first is the users, name rendered as a link to their profile - DisplayLookup( - field="name", on_click=GoToEvent(url="/user/{id}/") - ), - # the second is the date of birth, rendered as a date - DisplayLookup(field="dob", mode=DisplayMode.date), - ], - ), - ] - ), - ] - - -@app.get( - "/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True -) -def user_profile(user_id: int) -> list[AnyComponent]: - """ - User profile page, the frontend will fetch this when the user visits `/user/{id}/`. - """ - try: - user = next(u for u in users if u.id == user_id) - except StopIteration: - raise HTTPException(status_code=404, detail="User not found") - return [ - c.Page( - components=[ - c.Heading(text=user.name, level=2), - c.Link(components=[c.Text(text="Back")], on_click=BackEvent()), - c.Details(data=user), - ] - ), - ] - - -@app.get("/{path:path}") -async def html_landing() -> HTMLResponse: - """Simple HTML page which serves the React app, comes last as it matches all paths.""" - return HTMLResponse(prebuilt_html(title="FastUI Demo"))