From 66451fe05a77c8b26faaf3dbdf825ea0f3e31cf5 Mon Sep 17 00:00:00 2001 From: Zichao Lin Date: Sun, 5 Jul 2026 19:22:10 +0800 Subject: [PATCH] feat(ui): rich dashboard cards with countries and recent postcards --- app/routers/web.py | 9 +- app/static/style.css | 5 + app/templates/dashboard.html | 16 + server.log | 619 +++++++++++++++++++++++++++++++++++ 4 files changed, 646 insertions(+), 3 deletions(-) diff --git a/app/routers/web.py b/app/routers/web.py index e97328c..45c2f62 100644 --- a/app/routers/web.py +++ b/app/routers/web.py @@ -102,17 +102,20 @@ def root(user: User = Depends(get_current_user_web)): @router.get("/dashboard", response_class=HTMLResponse) def dashboard(request: Request, user: User = Depends(get_current_user_web), db: Session = Depends(get_db)): profiles = db.query(Profile).filter(Profile.user_id == user.id).all() - # stats per profile stats = {} + extras = {} for p in profiles: - cards = db.query(Postcard).filter(Postcard.profile_id == p.id).all() + cards = db.query(Postcard).filter(Postcard.profile_id == p.id).order_by(Postcard.created_at.desc()).all() stats[p.id] = { "total": len(cards), "sent": sum(1 for c in cards if c.status == "sent"), "delivered": sum(1 for c in cards if c.status == "delivered"), "received": sum(1 for c in cards if c.status == "received"), } - return templates.TemplateResponse("dashboard.html", {"request": request, "user": user, "profiles": profiles, "stats": stats}) + countries = sorted({c.country for c in cards if c.country}) + recent = cards[:3] + extras[p.id] = {"countries": countries, "recent": recent} + return templates.TemplateResponse("dashboard.html", {"request": request, "user": user, "profiles": profiles, "stats": stats, "extras": extras}) # ---------- Profiles ---------- diff --git a/app/static/style.css b/app/static/style.css index 180a2e1..581dd06 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -104,6 +104,11 @@ code { background: var(--bg); padding: .15rem .4rem; border-radius: 4px; font-si .dc-sent { color: var(--sent); } .dc-delivered { color: var(--delivered); } .dc-received { color: var(--received); } +.dc-tags { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .75rem; } +.dc-tag { display: inline-block; padding: .15rem .5rem; background: var(--bg); border: 1px solid var(--border); border-radius: 999px; font-size: .7rem; color: var(--text-muted); font-weight: 500; } +.dc-recent { display: flex; align-items: center; gap: .4rem; margin-top: .6rem; font-size: .75rem; color: var(--text-muted); } +.dc-recent-label { flex-shrink: 0; } +.dc-recent-item { padding: .1rem .4rem; background: var(--bg); border-radius: 4px; font-family: monospace; font-size: .7rem; } /* Postcard grid */ .postcard-card { display: flex; gap: 1rem; background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; padding: .75rem; transition: box-shadow .15s; } diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 2b0a5f8..bf371e7 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -7,6 +7,7 @@ {% if profiles %}
{% for p in profiles %} + {% set ex = extras[p.id] %}
📮 @@ -30,6 +31,21 @@ 收到
+ {% if ex.countries %} +
+ {% for c in ex.countries %} + {{ c }} + {% endfor %} +
+ {% endif %} + {% if ex.recent %} +
+ 最近 + {% for c in ex.recent %} + {{ c.card_number }} + {% endfor %} +
+ {% endif %}
{% endfor %} diff --git a/server.log b/server.log index c1d8043..fece9dd 100644 --- a/server.log +++ b/server.log @@ -291,3 +291,622 @@ INFO: 127.0.0.1:64612 - "GET /profiles HTTP/1.1" 200 OK INFO: 127.0.0.1:63763 - "GET /profiles HTTP/1.1" 200 OK INFO: 127.0.0.1:63763 - "GET /static/style.css HTTP/1.1" 304 Not Modified INFO: 127.0.0.1:63763 - "GET /profiles/3/postcards HTTP/1.1" 200 OK +INFO: 127.0.0.1:58913 - "GET /profiles HTTP/1.1" 200 OK +INFO: 127.0.0.1:58913 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:54850 - "GET /profiles HTTP/1.1" 200 OK +INFO: 127.0.0.1:54850 - "GET /static/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:54850 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:59821 - "GET /profiles HTTP/1.1" 200 OK +INFO: 127.0.0.1:59821 - "GET /static/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:59821 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:59821 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:59821 - "GET /logout HTTP/1.1" 303 See Other +INFO: 127.0.0.1:59821 - "GET /login HTTP/1.1" 200 OK +INFO: 127.0.0.1:59821 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:59821 - "POST /login HTTP/1.1" 303 See Other +INFO: 127.0.0.1:59821 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:59821 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:59821 - "GET /profiles HTTP/1.1" 200 OK +INFO: 127.0.0.1:59821 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:59821 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:55813 - "GET /profiles/3/postcards HTTP/1.1" 200 OK +INFO: 127.0.0.1:55813 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:55813 - "GET /profiles/3/postcards/new HTTP/1.1" 200 OK +INFO: 127.0.0.1:55813 - "GET /profiles/3/postcards HTTP/1.1" 200 OK +INFO: 127.0.0.1:55813 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:62477 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:62477 - "GET /static/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:62477 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:61813 - "GET /profiles/3/postcards HTTP/1.1" 200 OK +INFO: 127.0.0.1:61813 - "GET /static/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:61813 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:61813 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:61813 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:61818 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:61818 - "GET /static/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:61818 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:50400 - "GET /dashboard HTTP/1.1" 200 OK +INFO: 127.0.0.1:50400 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:50400 - "GET /profiles/3/postcards HTTP/1.1" 200 OK +INFO: 127.0.0.1:50400 - "GET /profiles/3/postcards/new HTTP/1.1" 200 OK +INFO: 127.0.0.1:50400 - "GET /profiles/3/postcards HTTP/1.1" 200 OK +INFO: 127.0.0.1:50400 - "GET /static/style.css HTTP/1.1" 304 Not Modified +INFO: 127.0.0.1:50400 - "GET /api-keys HTTP/1.1" 200 OK +INFO: 127.0.0.1:50400 - "GET /profiles HTTP/1.1" 200 OK +INFO: 127.0.0.1:50400 - "POST /profiles/3/rename HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:58000 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:50400 - "POST /profiles/3/rename HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:58000 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:50400 - "POST /profiles/3/rename HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:58000 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:49883 - "GET /dashboard HTTP/1.1" 500 Internal Server Error +ERROR: Exception in ASGI application +Traceback (most recent call last): + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi + result = await app( # type: ignore[func-returns-value] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + self.scope, self.receive, self.send + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__ + return await self.app(scope, receive, send) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\applications.py", line 1054, in __call__ + await super().__call__(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\applications.py", line 113, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__ + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__ + await self.app(scope, receive, _send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__ + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 715, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 735, in app + await route.handle(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle + await self.app(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 76, in app + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 73, in app + response = await f(request) + ^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 301, in app + raw_response = await run_endpoint_function( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ...<3 lines>... + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 214, in run_endpoint_function + return await run_in_threadpool(dependant.call, **values) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool + return await anyio.to_thread.run_sync(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2596, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 1029, in run + result = context.run(func, *args) + File "J:\Projects\postcard-manager\app\routers\web.py", line 115, in dashboard + countries = sorted({c.country for c in cards if c.country}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 209, in TemplateResponse + return _TemplateResponse( + template, + ...<4 lines>... + background=background, + ) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 40, in __init__ + content = template.render(context) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 1304, in render + self.environment.handle_exception() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 939, in handle_exception + raise rewrite_traceback_stack(source=source) + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 1, in top-level template code + {% extends "base.html" %} + File "J:\Projects\postcard-manager\app\templates\base.html", line 21, in top-level template code + {% block content %}{% endblock %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 10, in block 'content' + {% set ex = extras[p.id] %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 468, in getitem + return obj[argument] + ~~~^^^^^^^^^^ +jinja2.exceptions.UndefinedError: 'extras' is undefined +INFO: 127.0.0.1:59726 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:50231 - "GET /login HTTP/1.1" 303 See Other +INFO: 127.0.0.1:50231 - "GET /dashboard HTTP/1.1" 500 Internal Server Error +ERROR: Exception in ASGI application +Traceback (most recent call last): + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi + result = await app( # type: ignore[func-returns-value] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + self.scope, self.receive, self.send + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__ + return await self.app(scope, receive, send) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\applications.py", line 1054, in __call__ + await super().__call__(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\applications.py", line 113, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__ + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__ + await self.app(scope, receive, _send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__ + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 715, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 735, in app + await route.handle(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle + await self.app(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 76, in app + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 73, in app + response = await f(request) + ^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 301, in app + raw_response = await run_endpoint_function( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ...<3 lines>... + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 214, in run_endpoint_function + return await run_in_threadpool(dependant.call, **values) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool + return await anyio.to_thread.run_sync(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2596, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 1029, in run + result = context.run(func, *args) + File "J:\Projects\postcard-manager\app\routers\web.py", line 115, in dashboard + countries = sorted({c.country for c in cards if c.country}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 209, in TemplateResponse + return _TemplateResponse( + template, + ...<4 lines>... + background=background, + ) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 40, in __init__ + content = template.render(context) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 1304, in render + self.environment.handle_exception() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 939, in handle_exception + raise rewrite_traceback_stack(source=source) + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 1, in top-level template code + {% extends "base.html" %} + File "J:\Projects\postcard-manager\app\templates\base.html", line 21, in top-level template code + {% block content %}{% endblock %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 10, in block 'content' + {% set ex = extras[p.id] %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 468, in getitem + return obj[argument] + ~~~^^^^^^^^^^ +jinja2.exceptions.UndefinedError: 'extras' is undefined +INFO: 127.0.0.1:64510 - "GET /login HTTP/1.1" 200 OK +INFO: 127.0.0.1:51438 - "GET /dashboard HTTP/1.1" 500 Internal Server Error +ERROR: Exception in ASGI application +Traceback (most recent call last): + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi + result = await app( # type: ignore[func-returns-value] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + self.scope, self.receive, self.send + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__ + return await self.app(scope, receive, send) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\applications.py", line 1054, in __call__ + await super().__call__(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\applications.py", line 113, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__ + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__ + await self.app(scope, receive, _send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__ + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 715, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 735, in app + await route.handle(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle + await self.app(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 76, in app + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 73, in app + response = await f(request) + ^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 301, in app + raw_response = await run_endpoint_function( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ...<3 lines>... + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 214, in run_endpoint_function + return await run_in_threadpool(dependant.call, **values) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool + return await anyio.to_thread.run_sync(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2596, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 1029, in run + result = context.run(func, *args) + File "J:\Projects\postcard-manager\app\routers\web.py", line 115, in dashboard + countries = sorted({c.country for c in cards if c.country}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 209, in TemplateResponse + return _TemplateResponse( + template, + ...<4 lines>... + background=background, + ) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 40, in __init__ + content = template.render(context) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 1304, in render + self.environment.handle_exception() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 939, in handle_exception + raise rewrite_traceback_stack(source=source) + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 1, in top-level template code + {% extends "base.html" %} + File "J:\Projects\postcard-manager\app\templates\base.html", line 21, in top-level template code + {% block content %}{% endblock %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 10, in block 'content' + {% set ex = extras[p.id] %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 468, in getitem + return obj[argument] + ~~~^^^^^^^^^^ +jinja2.exceptions.UndefinedError: 'extras' is undefined +INFO: 127.0.0.1:64876 - "GET /login HTTP/1.1" 303 See Other +INFO: 127.0.0.1:64876 - "GET /dashboard HTTP/1.1" 500 Internal Server Error +ERROR: Exception in ASGI application +Traceback (most recent call last): + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi + result = await app( # type: ignore[func-returns-value] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + self.scope, self.receive, self.send + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__ + return await self.app(scope, receive, send) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\applications.py", line 1054, in __call__ + await super().__call__(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\applications.py", line 113, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__ + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__ + await self.app(scope, receive, _send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__ + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 715, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 735, in app + await route.handle(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle + await self.app(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 76, in app + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 73, in app + response = await f(request) + ^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 301, in app + raw_response = await run_endpoint_function( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ...<3 lines>... + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 214, in run_endpoint_function + return await run_in_threadpool(dependant.call, **values) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool + return await anyio.to_thread.run_sync(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2596, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 1029, in run + result = context.run(func, *args) + File "J:\Projects\postcard-manager\app\routers\web.py", line 115, in dashboard + countries = sorted({c.country for c in cards if c.country}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 209, in TemplateResponse + return _TemplateResponse( + template, + ...<4 lines>... + background=background, + ) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 40, in __init__ + content = template.render(context) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 1304, in render + self.environment.handle_exception() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 939, in handle_exception + raise rewrite_traceback_stack(source=source) + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 1, in top-level template code + {% extends "base.html" %} + File "J:\Projects\postcard-manager\app\templates\base.html", line 21, in top-level template code + {% block content %}{% endblock %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 10, in block 'content' + {% set ex = extras[p.id] %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 468, in getitem + return obj[argument] + ~~~^^^^^^^^^^ +jinja2.exceptions.UndefinedError: 'extras' is undefined +INFO: 127.0.0.1:57404 - "GET /login HTTP/1.1" 200 OK +INFO: 127.0.0.1:57404 - "GET /static/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:59212 - "POST /login HTTP/1.1" 303 See Other +INFO: 127.0.0.1:59212 - "GET /dashboard HTTP/1.1" 500 Internal Server Error +ERROR: Exception in ASGI application +Traceback (most recent call last): + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi + result = await app( # type: ignore[func-returns-value] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + self.scope, self.receive, self.send + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__ + return await self.app(scope, receive, send) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\applications.py", line 1054, in __call__ + await super().__call__(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\applications.py", line 113, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__ + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__ + await self.app(scope, receive, _send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__ + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 715, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 735, in app + await route.handle(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle + await self.app(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 76, in app + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 73, in app + response = await f(request) + ^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 301, in app + raw_response = await run_endpoint_function( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ...<3 lines>... + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 214, in run_endpoint_function + return await run_in_threadpool(dependant.call, **values) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool + return await anyio.to_thread.run_sync(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2596, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 1029, in run + result = context.run(func, *args) + File "J:\Projects\postcard-manager\app\routers\web.py", line 115, in dashboard + countries = sorted({c.country for c in cards if c.country}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 209, in TemplateResponse + return _TemplateResponse( + template, + ...<4 lines>... + background=background, + ) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 40, in __init__ + content = template.render(context) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 1304, in render + self.environment.handle_exception() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 939, in handle_exception + raise rewrite_traceback_stack(source=source) + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 1, in top-level template code + {% extends "base.html" %} + File "J:\Projects\postcard-manager\app\templates\base.html", line 21, in top-level template code + {% block content %}{% endblock %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 10, in block 'content' + {% set ex = extras[p.id] %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 468, in getitem + return obj[argument] + ~~~^^^^^^^^^^ +jinja2.exceptions.UndefinedError: 'extras' is undefined +INFO: 127.0.0.1:54873 - "GET /dashboard HTTP/1.1" 500 Internal Server Error +ERROR: Exception in ASGI application +Traceback (most recent call last): + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi + result = await app( # type: ignore[func-returns-value] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + self.scope, self.receive, self.send + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__ + return await self.app(scope, receive, send) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\applications.py", line 1054, in __call__ + await super().__call__(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\applications.py", line 113, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__ + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__ + await self.app(scope, receive, _send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__ + await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 715, in __call__ + await self.middleware_stack(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 735, in app + await route.handle(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 288, in handle + await self.app(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 76, in app + await wrap_app_handling_exceptions(app, request)(scope, receive, send) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 62, in wrapped_app + raise exc + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\_exception_handler.py", line 51, in wrapped_app + await app(scope, receive, sender) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\routing.py", line 73, in app + response = await f(request) + ^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 301, in app + raw_response = await run_endpoint_function( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ...<3 lines>... + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\fastapi\routing.py", line 214, in run_endpoint_function + return await run_in_threadpool(dependant.call, **values) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool + return await anyio.to_thread.run_sync(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ) + ^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2596, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 1029, in run + result = context.run(func, *args) + File "J:\Projects\postcard-manager\app\routers\web.py", line 115, in dashboard + countries = sorted({c.country for c in cards if c.country}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 209, in TemplateResponse + return _TemplateResponse( + template, + ...<4 lines>... + background=background, + ) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\starlette\templating.py", line 40, in __init__ + content = template.render(context) + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 1304, in render + self.environment.handle_exception() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 939, in handle_exception + raise rewrite_traceback_stack(source=source) + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 1, in top-level template code + {% extends "base.html" %} + File "J:\Projects\postcard-manager\app\templates\base.html", line 21, in top-level template code + {% block content %}{% endblock %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\app\templates\dashboard.html", line 10, in block 'content' + {% set ex = extras[p.id] %} + ^^^^^^^^^^^^^^^^^ + File "J:\Projects\postcard-manager\.venv\Lib\site-packages\jinja2\environment.py", line 468, in getitem + return obj[argument] + ~~~^^^^^^^^^^ +jinja2.exceptions.UndefinedError: 'extras' is undefined