Skip to content

Commit 21c2a4f

Browse files
authored
Merge pull request #206 from grillazz/198-add-simple-caching
bump project deps, inline-snapshot config, add templates
2 parents 5f5b363 + 1fb5ad4 commit 21c2a4f

File tree

7 files changed

+278
-141
lines changed

7 files changed

+278
-141
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ USER panettone
5252
WORKDIR /panettone
5353
COPY /app/ app/
5454
COPY /tests/ tests/
55+
COPY /templates/ templates/
5556
COPY .env app/
5657
COPY alembic.ini app/
5758
COPY alembic/ app/alembic/
5859
COPY logging-uvicorn.json /panettone/logging-uvicorn.json
60+
COPY pyproject.toml /panettone/pyproject.toml
5961

6062
RUN python -V
6163
RUN python -Im site

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ docker-test: ## Run project tests
3131

3232
.PHONY: docker-test-snapshot
3333
docker-test-snapshot: ## Run project tests with inline snapshot
34-
docker compose -f compose.yml -f test-compose.yml run --rm app pytest --inline-snapshot=create
34+
docker compose -f compose.yml -f test-compose.yml run --rm app pytest tests --inline-snapshot=fix
3535

3636
.PHONY: safety
3737
safety: ## Check project and dependencies with safety https://github.com/pyupio/safety

app/main.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
from contextlib import asynccontextmanager
2+
from pathlib import Path
23

34
import asyncpg
4-
from apscheduler import AsyncScheduler
5-
from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
6-
from apscheduler.eventbrokers.redis import RedisEventBroker
7-
from fastapi import Depends, FastAPI
5+
6+
# from apscheduler import AsyncScheduler
7+
# from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
8+
# from apscheduler.eventbrokers.redis import RedisEventBroker
9+
from fastapi import Depends, FastAPI, Request
10+
from fastapi.responses import HTMLResponse
11+
from fastapi.templating import Jinja2Templates
812

913
from app.api.health import router as health_router
1014
from app.api.ml import router as ml_router
@@ -13,14 +17,18 @@
1317
from app.api.stuff import router as stuff_router
1418
from app.api.user import router as user_router
1519
from app.config import settings as global_settings
16-
from app.database import engine
20+
21+
# from app.database import engine
1722
from app.redis import get_redis
1823
from app.services.auth import AuthBearer
19-
from app.services.scheduler import SchedulerMiddleware
24+
25+
# from app.services.scheduler import SchedulerMiddleware
2026
from app.utils.logging import AppLogger
2127

2228
logger = AppLogger().get_logger()
2329

30+
templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")
31+
2432

2533
@asynccontextmanager
2634
async def lifespan(_app: FastAPI):
@@ -46,7 +54,7 @@ async def lifespan(_app: FastAPI):
4654
await _app.postgres_pool.close()
4755

4856

49-
app = FastAPI(title="Stuff And Nonsense API", version="0.18.0", lifespan=lifespan)
57+
app = FastAPI(title="Stuff And Nonsense API", version="0.19.0", lifespan=lifespan)
5058

5159
app.include_router(stuff_router)
5260
app.include_router(nonsense_router)
@@ -63,13 +71,19 @@ async def lifespan(_app: FastAPI):
6371
dependencies=[Depends(AuthBearer())],
6472
)
6573

66-
_scheduler_data_store = SQLAlchemyDataStore(engine, schema="scheduler")
67-
_scheduler_event_broker = RedisEventBroker(
68-
client_or_url=global_settings.redis_url.unicode_string()
69-
)
70-
_scheduler_himself = AsyncScheduler(_scheduler_data_store, _scheduler_event_broker)
7174

72-
app.add_middleware(SchedulerMiddleware, scheduler=_scheduler_himself)
75+
@app.get("/index", response_class=HTMLResponse)
76+
def get_index(request: Request):
77+
return templates.TemplateResponse("index.html", {"request": request})
78+
79+
80+
# _scheduler_data_store = SQLAlchemyDataStore(engine, schema="scheduler")
81+
# _scheduler_event_broker = RedisEventBroker(
82+
# client_or_url=global_settings.redis_url.unicode_string()
83+
# )
84+
# _scheduler_himself = AsyncScheduler(_scheduler_data_store, _scheduler_event_broker)
85+
#
86+
# app.add_middleware(SchedulerMiddleware, scheduler=_scheduler_himself)
7387

7488

7589
# TODO: every not GET meth should reset cache

compose.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ services:
1414
--reload --log-level debug
1515
"
1616
volumes:
17-
- .:/home/code
17+
- ./app:/panettone/app
18+
- ./tests:/panettone/tests
19+
- ./templates:/panettone/templates
1820
ports:
1921
- "8080:8080"
2022
depends_on:

pyproject.toml

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,45 @@
11
[project]
22
name = "fastapi-sqlalchemy-asyncpg"
3-
version = "0.18.0"
3+
version = "0.19.0"
44
description = "A modern FastAPI application with SQLAlchemy 2.0 and AsyncPG for high-performance async database operations. Features include JWT authentication with Redis token storage, password hashing, connection pooling, data processing with Polars, Rich logging, task scheduling with APScheduler, and Shakespeare datasets integration."
55
readme = "README.md"
66
requires-python = ">=3.13"
77
dependencies = [
88
"fastapi[all]>=0.115.12",
9-
"pydantic[email]>=2.11.4",
9+
"pydantic[email]>=2.11.5",
1010
"pydantic-settings>=2.9.1",
11-
"sqlalchemy>=2.0.40",
12-
"uvicorn[standard]>=0.34.2",
11+
"sqlalchemy>=2.0.41",
12+
"uvicorn[standard]>=0.34.3",
1313
"asyncpg>=0.30.0",
14-
"alembic>=1.15.2",
14+
"alembic>=1.16.1",
1515
"httpx>=0.28.1",
16-
"pytest>=8.3.5",
16+
"pytest>=8.4.0",
1717
"pytest-cov>=6.1.1",
1818
"uvloop>=0.21.0",
1919
"httptools>=0.6.4",
2020
"rich>=14.0.0",
2121
"pyjwt>=2.10.1",
22-
"redis>=6.0.0",
22+
"redis>=6.2.0",
2323
"bcrypt>=4.3.0",
24-
"polars>=1.29.0",
24+
"polars>=1.30.0",
2525
"python-multipart>=0.0.20",
2626
"fastexcel>=0.14.0",
27-
"inline-snapshot>=0.23.0",
27+
"inline-snapshot>=0.23.2",
2828
"dirty-equals>=0.9.0",
2929
"polyfactory>=2.21.0",
30-
"granian>=2.2.5",
30+
"granian>=2.3.2",
3131
"apscheduler[redis,sqlalchemy]>=4.0.0a6",
3232
]
3333

3434
[tool.uv]
3535
dev-dependencies = [
36-
"ruff>=0.11.8",
36+
"ruff>=0.11.13",
3737
"devtools[pygments]>=0.12.2",
38-
"pyupgrade>=3.19.1",
39-
"ipython>=9.2.0",
38+
"pyupgrade>=3.20.0",
39+
"ipython>=9.3.0",
4040
"sqlacodegen>=3.0.0",
4141
"tryceratops>=2.4.1",
42-
"locust>=2.36.2"
42+
"locust>=2.37.9"
4343

4444
]
4545

@@ -72,4 +72,15 @@ ignore = [
7272

7373
[tool.ruff.lint.pyupgrade]
7474
# Preserve types, even if a file imports `from __future__ import annotations`.
75-
keep-runtime-typing = true
75+
keep-runtime-typing = true
76+
77+
[tool.inline-snapshot]
78+
hash-length=15
79+
default-flags=["report"]
80+
default-flags-tui=["create", "review"]
81+
show-updates=false
82+
format-command="ruff format --stdin-filename {filename}"
83+
84+
[tool.inline-snapshot.shortcuts]
85+
review=["review"]
86+
fix=["create","fix"]

templates/index.html

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<title>Tabler Demo</title>
8+
<link rel="stylesheet"
9+
href="https://cdn.jsdelivr.net/npm/@tabler/core@1.2.0/dist/css/tabler.min.css" />
10+
</head>
11+
12+
<body>
13+
<header class="navbar navbar-expand-md d-print-none">
14+
<div class="container-xl">
15+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
16+
data-bs-target="#navbar-menu" aria-controls="navbar-menu"
17+
aria-expanded="false" aria-label="Toggle navigation">
18+
<span class="navbar-toggler-icon"></span>
19+
</button>
20+
<!-- BEGIN NAVBAR LOGO --><a href="../../.."
21+
class="navbar-brand navbar-brand-autodark me-3"><svg
22+
xmlns="http://www.w3.org/2000/svg" width="110" height="32"
23+
viewBox="0 0 232 68" class="navbar-brand-image">
24+
<path
25+
d="M64.6 16.2C63 9.9 58.1 5 51.8 3.4 40 1.5 28 1.5 16.2 3.4 9.9 5 5 9.9 3.4 16.2 1.5 28 1.5 40 3.4 51.8 5 58.1 9.9 63 16.2 64.6c11.8 1.9 23.8 1.9 35.6 0C58.1 63 63 58.1 64.6 51.8c1.9-11.8 1.9-23.8 0-35.6zM33.3 36.3c-2.8 4.4-6.6 8.2-11.1 11-1.5.9-3.3.9-4.8.1s-2.4-2.3-2.5-4c0-1.7.9-3.3 2.4-4.1 2.3-1.4 4.4-3.2 6.1-5.3-1.8-2.1-3.8-3.8-6.1-5.3-2.3-1.3-3-4.2-1.7-6.4s4.3-2.9 6.5-1.6c4.5 2.8 8.2 6.5 11.1 10.9 1 1.4 1 3.3.1 4.7zM49.2 46H37.8c-2.1 0-3.8-1-3.8-3s1.7-3 3.8-3h11.4c2.1 0 3.8 1 3.8 3s-1.7 3-3.8 3z"
26+
fill="#066fd1" style="fill: var(--tblr-primary, #066fd1)" />
27+
<path
28+
d="M105.8 46.1c.4 0 .9.2 1.2.6s.6 1 .6 1.7c0 .9-.5 1.6-1.4 2.2s-2 .9-3.2.9c-2 0-3.7-.4-5-1.3s-2-2.6-2-5.4V31.6h-2.2c-.8 0-1.4-.3-1.9-.8s-.9-1.1-.9-1.9c0-.7.3-1.4.8-1.8s1.2-.7 1.9-.7h2.2v-3.1c0-.8.3-1.5.8-2.1s1.3-.8 2.1-.8 1.5.3 2 .8.8 1.3.8 2.1v3.1h3.4c.8 0 1.4.3 1.9.8s.8 1.2.8 1.9-.3 1.4-.8 1.8-1.2.7-1.9.7h-3.4v13c0 .7.2 1.2.5 1.5s.8.5 1.4.5c.3 0 .6-.1 1.1-.2.5-.2.8-.3 1.2-.3zm28-20.7c.8 0 1.5.3 2.1.8.5.5.8 1.2.8 2.1v20.3c0 .8-.3 1.5-.8 2.1-.5.6-1.2.8-2.1.8s-1.5-.3-2-.8-.8-1.2-.8-2.1c-.8.9-1.9 1.7-3.2 2.4-1.3.7-2.8 1-4.3 1-2.2 0-4.2-.6-6-1.7-1.8-1.1-3.2-2.7-4.2-4.7s-1.6-4.3-1.6-6.9c0-2.6.5-4.9 1.5-6.9s2.4-3.6 4.2-4.8c1.8-1.1 3.7-1.7 5.9-1.7 1.5 0 3 .3 4.3.8 1.3.6 2.5 1.3 3.4 2.1 0-.8.3-1.5.8-2.1.5-.5 1.2-.7 2-.7zm-9.7 21.3c2.1 0 3.8-.8 5.1-2.3s2-3.4 2-5.7-.7-4.2-2-5.8c-1.3-1.5-3-2.3-5.1-2.3-2 0-3.7.8-5 2.3-1.3 1.5-2 3.5-2 5.8s.6 4.2 1.9 5.7 3 2.3 5.1 2.3zm32.1-21.3c2.2 0 4.2.6 6 1.7 1.8 1.1 3.2 2.7 4.2 4.7s1.6 4.3 1.6 6.9-.5 4.9-1.5 6.9-2.4 3.6-4.2 4.8c-1.8 1.1-3.7 1.7-5.9 1.7-1.5 0-3-.3-4.3-.9s-2.5-1.4-3.4-2.3v.3c0 .8-.3 1.5-.8 2.1-.5.6-1.2.8-2.1.8s-1.5-.3-2.1-.8c-.5-.5-.8-1.2-.8-2.1V18.9c0-.8.3-1.5.8-2.1.5-.6 1.2-.8 2.1-.8s1.5.3 2.1.8c.5.6.8 1.3.8 2.1v10c.8-1 1.8-1.8 3.2-2.5 1.3-.7 2.8-1 4.3-1zm-.7 21.3c2 0 3.7-.8 5-2.3s2-3.5 2-5.8-.6-4.2-1.9-5.7-3-2.3-5.1-2.3-3.8.8-5.1 2.3-2 3.4-2 5.7.7 4.2 2 5.8c1.3 1.6 3 2.3 5.1 2.3zm23.6 1.9c0 .8-.3 1.5-.8 2.1s-1.3.8-2.1.8-1.5-.3-2-.8-.8-1.3-.8-2.1V18.9c0-.8.3-1.5.8-2.1s1.3-.8 2.1-.8 1.5.3 2 .8.8 1.3.8 2.1v29.7zm29.3-10.5c0 .8-.3 1.4-.9 1.9-.6.5-1.2.7-2 .7h-15.8c.4 1.9 1.3 3.4 2.6 4.4 1.4 1.1 2.9 1.6 4.7 1.6 1.3 0 2.3-.1 3.1-.4.7-.2 1.3-.5 1.8-.8.4-.3.7-.5.9-.6.6-.3 1.1-.4 1.6-.4.7 0 1.2.2 1.7.7s.7 1 .7 1.7c0 .9-.4 1.6-1.3 2.4-.9.7-2.1 1.4-3.6 1.9s-3 .8-4.6.8c-2.7 0-5-.6-7-1.7s-3.5-2.7-4.6-4.6-1.6-4.2-1.6-6.6c0-2.8.6-5.2 1.7-7.2s2.7-3.7 4.6-4.8 3.9-1.7 6-1.7 4.1.6 6 1.7 3.4 2.7 4.5 4.7c.9 1.9 1.5 4.1 1.5 6.3zm-12.2-7.5c-3.7 0-5.9 1.7-6.6 5.2h12.6v-.3c-.1-1.3-.8-2.5-2-3.5s-2.5-1.4-4-1.4zm30.3-5.2c1 0 1.8.3 2.4.8.7.5 1 1.2 1 1.9 0 1-.3 1.7-.8 2.2-.5.5-1.1.8-1.8.7-.5 0-1-.1-1.6-.3-.2-.1-.4-.1-.6-.2-.4-.1-.7-.1-1.1-.1-.8 0-1.6.3-2.4.8s-1.4 1.3-1.9 2.3-.7 2.3-.7 3.7v11.4c0 .8-.3 1.5-.8 2.1-.5.6-1.2.8-2.1.8s-1.5-.3-2.1-.8c-.5-.6-.8-1.3-.8-2.1V28.8c0-.8.3-1.5.8-2.1.5-.6 1.2-.8 2.1-.8s1.5.3 2.1.8c.5.6.8 1.3.8 2.1v.6c.7-1.3 1.8-2.3 3.2-3 1.3-.7 2.8-1 4.3-1z"
29+
fill-rule="evenodd" clip-rule="evenodd" fill="#4a4a4a" />
30+
</svg></a><!-- END NAVBAR LOGO -->
31+
<ul class="navbar-nav">
32+
<li class="nav-item active">
33+
<a class="nav-link" href="#">
34+
<span class="nav-link-icon">
35+
<!-- Download SVG icon from http://tabler.io/icons/icon/home -->
36+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
37+
viewBox="0 0 24 24" fill="none" stroke="currentColor"
38+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
39+
class="icon icon-1">
40+
<path d="M5 12l-2 0l9 -9l9 9l-2 0" />
41+
<path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7" />
42+
<path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6" />
43+
</svg>
44+
</span>
45+
<span class="nav-link-title"> Home </span>
46+
</a>
47+
</li>
48+
<li class="nav-item">
49+
<a class="nav-link" href="#">
50+
<span
51+
class="nav-link-icon"><!-- Download SVG icon from http://tabler.io/icons/icon/checkbox -->
52+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
53+
viewBox="0 0 24 24" fill="none" stroke="currentColor"
54+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
55+
class="icon icon-1">
56+
<path d="M9 11l3 3l8 -8" />
57+
<path
58+
d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" />
59+
</svg>
60+
</span>
61+
<span class="nav-link-title"> Profile </span>
62+
</a>
63+
</li>
64+
<li class="nav-item">
65+
<a class="nav-link" href="#">
66+
<span
67+
class="nav-link-icon"><!-- Download SVG icon from http://tabler.io/icons/icon/checkbox -->
68+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
69+
viewBox="0 0 24 24" fill="none" stroke="currentColor"
70+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
71+
class="icon icon-1">
72+
<path d="M9 11l3 3l8 -8" />
73+
<path
74+
d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" />
75+
</svg>
76+
</span>
77+
<span class="nav-link-title"> Settings </span>
78+
</a>
79+
</li>
80+
</ul>
81+
<div class="navbar-nav flex-row order-md-last ms-auto">
82+
<div class="nav-item dropdown">
83+
<a href="#" class="nav-link d-flex lh-1 text-reset"
84+
data-bs-toggle="dropdown" aria-label="Open user menu">
85+
<span class="avatar avatar-sm"
86+
style="background-image: url(/static/avatars/044m.jpg)"></span>
87+
<div class="d-none d-xl-block ps-2">
88+
<div>Paweł Kuna</div>
89+
<div class="mt-1 small text-secondary">UI Designer</div>
90+
</div>
91+
</a>
92+
<div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
93+
<a href="#" class="dropdown-item">Status</a>
94+
<a href="./profile.html" class="dropdown-item">Profile</a>
95+
<a href="#" class="dropdown-item">Feedback</a>
96+
<div class="dropdown-divider"></div>
97+
<a href="./settings.html" class="dropdown-item">Settings</a>
98+
<a href="./sign-in.html" class="dropdown-item">Logout</a>
99+
</div>
100+
</div>
101+
</div>
102+
</div>
103+
</header>
104+
</body>
105+
106+
</html>

0 commit comments

Comments
 (0)