From e952630295fc6140349c6bdd6c218118d2a25828 Mon Sep 17 00:00:00 2001 From: Christian Krakau-Louis Date: Mon, 14 Apr 2025 05:54:21 +0200 Subject: [PATCH] Update redirect paths and enhance user context in templates and views --- app/main.py | 4 +- app/templates/base.html | 6 +-- app/templates/leaderboard.html | 6 ++- app/templates/redeem_success.html | 10 ++--- app/views/admin.py | 51 ++++++++++++++++++++++-- app/views/dashboard.py | 55 ++++++++++++++++++++++++-- app/views/qr.py | 29 ++++++++++++-- app/views/redeem.py | 65 ++++++++++++++++++++++++------- 8 files changed, 189 insertions(+), 37 deletions(-) diff --git a/app/main.py b/app/main.py index c8b437a..9938f87 100644 --- a/app/main.py +++ b/app/main.py @@ -90,10 +90,10 @@ app.include_router(leaderboard.router, prefix="/leaderboard", tags=["Leaderboard app.include_router(dashboard.router, prefix="/dashboard", tags=["Dashboard"]) app.include_router(static.router, tags=["Static"]) # Include the static router -# Add a direct route for /scan that redirects to /redeem/scan +# Add a direct route for /scan that redirects to /dashboard/scan @app.get("/scan") async def scan_redirect(): - return RedirectResponse("/redeem/scan", status_code=303) + return RedirectResponse("/dashboard/scan", status_code=303) # Add convenience routes for auth paths @app.get("/login") diff --git a/app/templates/base.html b/app/templates/base.html index a48464c..4a67cba 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -68,7 +68,7 @@ Home Teams Leaderboard - Scan QR Code + Scan QR Code {% if user %} diff --git a/app/templates/leaderboard.html b/app/templates/leaderboard.html index a166e82..f214177 100644 --- a/app/templates/leaderboard.html +++ b/app/templates/leaderboard.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% block content %} -
+

League Leaderboard

@@ -82,7 +82,9 @@ {% for team in teams %} {{ team.rank }} - {{ team.name }} + + {{ team.name }} + {{ team.points }} {% if team.change > 0 %} diff --git a/app/templates/redeem_success.html b/app/templates/redeem_success.html index 34757f0..6595c46 100644 --- a/app/templates/redeem_success.html +++ b/app/templates/redeem_success.html @@ -50,7 +50,7 @@

What's Next?

-
+
@@ -71,13 +71,13 @@
- - diff --git a/app/views/admin.py b/app/views/admin.py index ae39915..6ebad5e 100644 --- a/app/views/admin.py +++ b/app/views/admin.py @@ -67,12 +67,22 @@ def get_relationships(model_class: Type[Base]) -> Dict[str, str]: return relationships @router.get("/", response_class=HTMLResponse) -async def admin_home(request: Request): +async def admin_home(request: Request, db: Session = Depends(get_db)): """Admin dashboard home.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + + # Check admin status + if not user or not user.is_admin: + raise HTTPException(status_code=403, detail="Forbidden: Admin access required") + model_list = [(key, name) for key, (_, name) in MODELS.items()] return templates.TemplateResponse( "admin/index.html", - {"request": request, "models": model_list} + {"request": request, "models": model_list, "user": user} ) @router.get("/{model_name}", response_class=HTMLResponse) @@ -84,6 +94,16 @@ async def list_records( db: Session = Depends(get_db) ): """List records for a model with pagination.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + + # Check admin status + if not user or not user.is_admin: + raise HTTPException(status_code=403, detail="Forbidden: Admin access required") + if model_name not in MODELS: raise HTTPException(status_code=404, detail=f"Model {model_name} not found") @@ -123,6 +143,7 @@ async def list_records( "per_page": per_page, "total_pages": total_pages, "total_records": total_records, + "user": user # Add user to the context } ) @@ -133,6 +154,16 @@ async def create_record_form( db: Session = Depends(get_db) ): """Show form for creating a new record.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + + # Check admin status + if not user or not user.is_admin: + raise HTTPException(status_code=403, detail="Forbidden: Admin access required") + if model_name not in MODELS: raise HTTPException(status_code=404, detail=f"Model {model_name} not found") @@ -162,7 +193,8 @@ async def create_record_form( "columns_info": columns_info, "record": None, # No record for new form "foreign_key_options": foreign_key_options, - "is_new": True + "is_new": True, + "user": user # Add user to the context } ) @@ -222,6 +254,16 @@ async def edit_record_form( db: Session = Depends(get_db) ): """Show form for editing an existing record.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + + # Check admin status + if not user or not user.is_admin: + raise HTTPException(status_code=403, detail="Forbidden: Admin access required") + if model_name not in MODELS: raise HTTPException(status_code=404, detail=f"Model {model_name} not found") @@ -261,7 +303,8 @@ async def edit_record_form( "columns_info": columns_info, "record": record_data, "foreign_key_options": foreign_key_options, - "is_new": False + "is_new": False, + "user": user # Add user to the context } ) diff --git a/app/views/dashboard.py b/app/views/dashboard.py index d5b813e..344dab9 100644 --- a/app/views/dashboard.py +++ b/app/views/dashboard.py @@ -92,6 +92,45 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)): .limit(5)\ .all() + # Get user teams with enhanced data for display + user_teams_enhanced = [] + + if user_teams: + # Get all team points to calculate ranks + team_points_query = db.query( + models.Team.id, + func.sum(models.QRCode.points).label('total_points') + ).outerjoin( + models.QRCode, + models.QRCode.redeemed_at_team == models.Team.id + ).group_by(models.Team.id) + + # Get results and sort by points in descending order + team_points = {row.id: row.total_points or 0 for row in team_points_query.all()} + ranked_teams = sorted(team_points.items(), key=lambda x: x[1], reverse=True) + + # Create a dictionary mapping team ID to rank + team_ranks = {team_id: i+1 for i, (team_id, _) in enumerate(ranked_teams)} + + # Enhance user teams with rank and points data + for team in user_teams: + points = team_points.get(team.id, 0) + rank = team_ranks.get(team.id, len(team_ranks) + 1) + + user_teams_enhanced.append({ + 'id': team.id, + 'name': team.name, + 'points': points, + 'rank': rank, + 'description': getattr(team, 'description', None) + }) + + # Find best rank if the user has any teams + best_rank = None + if user_teams_enhanced: + ranks = [team['rank'] for team in user_teams_enhanced] + best_rank = min(ranks) if ranks else None + return templates.TemplateResponse( "dashboard/index.html", { @@ -101,7 +140,8 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)): "total_points": total_points, "event_count": event_count, "recent_events": recent_events, - "user_teams": user_teams + "user_teams": user_teams_enhanced, + "best_rank": best_rank } ) except Exception as e: @@ -109,6 +149,15 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)): raise HTTPException(status_code=500, detail=f"Dashboard error: {str(e)}") @router.get("/scan", response_class=HTMLResponse) -def scan_qr_page(request: Request): +def scan_qr_page(request: Request, db: Session = Depends(get_db)): """Render the QR code scanning page.""" - return templates.TemplateResponse("scan_qr.html", {"request": request}) + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(models.User).get(user_id) + + return templates.TemplateResponse("scan_qr.html", { + "request": request, + "user": user # Add user to the context + }) diff --git a/app/views/qr.py b/app/views/qr.py index f168afa..7ff35f0 100644 --- a/app/views/qr.py +++ b/app/views/qr.py @@ -20,7 +20,7 @@ from reportlab.lib.enums import TA_CENTER from pydantic import BaseModel from ..db import SessionLocal -from ..models import QRCode, QRSet, Event +from ..models import QRCode, QRSet, Event, User from ..templates_config import templates router = APIRouter() @@ -55,6 +55,12 @@ class QRCodeRequest(BaseModel): @router.get("/", response_class=HTMLResponse) async def qr_dashboard(request: Request, db: Session = Depends(get_db)): """QR code management dashboard.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + # Get all QR sets qr_sets = db.query(QRSet).all() @@ -64,7 +70,8 @@ async def qr_dashboard(request: Request, db: Session = Depends(get_db)): return templates.TemplateResponse("qr/dashboard.html", { "request": request, "qr_sets": qr_sets, - "events": events + "events": events, + "user": user # Add user to the context }) @@ -94,6 +101,12 @@ async def create_qr_set( @router.get("/sets/{set_id}", response_class=HTMLResponse) async def view_qr_set(request: Request, set_id: int, db: Session = Depends(get_db)): """View details of a QR set.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + qr_set = db.query(QRSet).filter(QRSet.id == set_id).first() if not qr_set: raise HTTPException(status_code=404, detail="QR Set not found") @@ -105,7 +118,8 @@ async def view_qr_set(request: Request, set_id: int, db: Session = Depends(get_d "request": request, "qr_set": qr_set, "qr_codes": qr_codes, - "base_url": BASE_URL + "base_url": BASE_URL, + "user": user # Add user to the context }) @@ -208,6 +222,12 @@ def generate_admin_qr(set_id: int, db: Session = Depends(get_db)): @router.get("/admin-link/{admin_code}", response_class=HTMLResponse) async def admin_link_page(request: Request, admin_code: str, db: Session = Depends(get_db)): """Page for linking QR sets to events via admin code.""" + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + # Extract set_id from admin code try: set_id = int(admin_code.split("-")[1]) @@ -225,7 +245,8 @@ async def admin_link_page(request: Request, admin_code: str, db: Session = Depen "request": request, "qr_set": qr_set, "admin_code": admin_code, - "events": events + "events": events, + "user": user # Add user to the context }) diff --git a/app/views/redeem.py b/app/views/redeem.py index c60e765..8c3adbc 100644 --- a/app/views/redeem.py +++ b/app/views/redeem.py @@ -29,6 +29,12 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)): Display a page to let the user choose which team to apply points to. No login required. """ + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + # Find the QR code record qr_code = db.query(QRCode).filter_by(code=code).first() @@ -39,7 +45,8 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)): { "request": request, "error_title": "Invalid Code", - "error_message": "This QR code is invalid or does not exist." + "error_message": "This QR code is invalid or does not exist.", + "user": user # Add user to the context } ) @@ -49,7 +56,8 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)): { "request": request, "error_title": "Code Already Used", - "error_message": "This QR code has already been redeemed." + "error_message": "This QR code has already been redeemed.", + "user": user # Add user to the context } ) @@ -60,7 +68,8 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)): { "request": request, "error_title": "Expired Code", - "error_message": "This QR code has expired and can no longer be redeemed." + "error_message": "This QR code has expired and can no longer be redeemed.", + "user": user # Add user to the context } ) @@ -72,16 +81,24 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)): "ticket": qr_code, # Using the same template variable name for compatibility "user_teams": all_teams, "has_achievement": bool(qr_code.achievement_name), - "base_url": BASE_URL + "base_url": BASE_URL, + "user": user # Add user to the context }) @router.get("/scan", response_class=HTMLResponse) -def scan_qr_code(request: Request): +def scan_qr_code(request: Request, db: Session = Depends(get_db)): """ Display the QR code scanner page """ + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + return templates.TemplateResponse("scan_qr.html", { - "request": request + "request": request, + "user": user # Add user to the context }) @router.post("/apply/{code}") @@ -93,6 +110,12 @@ async def apply_code( """ Apply the QR code to a selected team and award points and/or achievements """ + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + # Get form data form_data = await request.form() team_id = int(form_data.get("team_id", 0)) @@ -103,7 +126,8 @@ async def apply_code( { "request": request, "error_title": "Team Selection Required", - "error_message": "Please select a team to redeem this code." + "error_message": "Please select a team to redeem this code.", + "user": user # Add user to the context } ) @@ -115,7 +139,8 @@ async def apply_code( { "request": request, "error_title": "Invalid Code", - "error_message": "This QR code is invalid or does not exist." + "error_message": "This QR code is invalid or does not exist.", + "user": user # Add user to the context } ) @@ -126,7 +151,8 @@ async def apply_code( { "request": request, "error_title": "Code Already Used", - "error_message": "This QR code has already been redeemed." + "error_message": "This QR code has already been redeemed.", + "user": user # Add user to the context } ) @@ -137,7 +163,8 @@ async def apply_code( { "request": request, "error_title": "Expired Code", - "error_message": "This QR code has expired and can no longer be redeemed." + "error_message": "This QR code has expired and can no longer be redeemed.", + "user": user # Add user to the context } ) @@ -148,7 +175,8 @@ async def apply_code( { "request": request, "error_title": "Team Not Found", - "error_message": "The selected team could not be found." + "error_message": "The selected team could not be found.", + "user": user # Add user to the context } ) @@ -180,7 +208,8 @@ async def apply_code( "achievement": qr_code.achievement_name if qr_code.achievement_name else None, "team": team, "event": qr_code.event if qr_code.event else None, - "base_url": BASE_URL + "base_url": BASE_URL, + "user": user # Add user to the context } ) @@ -194,6 +223,12 @@ async def manual_code_entry( Handle manual code entry from the form. This redirects to the normal redeem flow after validating the code. """ + # Get user from session for navbar + user = None + user_id = request.session.get("user_id") + if user_id: + user = db.query(User).get(user_id) + # Check if the code exists qr_code = db.query(QRCode).filter_by(code=code).first() @@ -203,7 +238,8 @@ async def manual_code_entry( { "request": request, "error_title": "Invalid Code", - "error_message": "The code you entered is invalid or does not exist." + "error_message": "The code you entered is invalid or does not exist.", + "user": user # Add user to the context } ) @@ -214,7 +250,8 @@ async def manual_code_entry( { "request": request, "error_title": "Code Already Used", - "error_message": "This code has already been redeemed." + "error_message": "This code has already been redeemed.", + "user": user # Add user to the context } )