Implement team join request functionality with views and actions
- Added join_requests.html template for displaying pending join requests. - Created join_team.html template for users to submit join requests. - Implemented request_processed.html template to show the result of join request processing. - Developed authentication utilities for user session management. - Introduced convenience redirects for common URL patterns. - Established team management routes and actions for creating, editing, and joining teams. - Added functionality for approving and denying join requests with email notifications. - Enhanced team views to include user permissions and team member details. - Implemented utility functions for team-related operations such as calculating total points and team rank.
This commit is contained in:
+8
-1
@@ -11,7 +11,10 @@ from typing import Dict, Any, List, Type, Optional
|
||||
import inspect as py_inspect
|
||||
|
||||
from ..db import SessionLocal, Base
|
||||
from ..models import User, Team, TeamMembership, QRCode, QRSet, TeamAchievement, Event
|
||||
from ..models import (
|
||||
User, Team, TeamMembership, QRCode, QRSet, TeamAchievement, Event,
|
||||
OAuthAccount, TeamJoinRequest, EventAttendee, UserPoints
|
||||
)
|
||||
from ..templates_config import templates
|
||||
|
||||
router = APIRouter()
|
||||
@@ -25,6 +28,10 @@ MODELS = {
|
||||
'qr_set': (QRSet, "QR Sets"),
|
||||
'team_achievement': (TeamAchievement, "Team Achievements"),
|
||||
'event': (Event, "Events"),
|
||||
'oauth_account': (OAuthAccount, "OAuth Accounts"),
|
||||
'team_join_request': (TeamJoinRequest, "Team Join Requests"),
|
||||
'event_attendee': (EventAttendee, "Event Attendees"),
|
||||
'user_points': (UserPoints, "User Points"),
|
||||
}
|
||||
|
||||
def get_db():
|
||||
|
||||
+24
-5
@@ -17,15 +17,25 @@ from ..templates_config import templates
|
||||
from ..security import verify_password, get_password_hash
|
||||
from ..utils.mail import send_password_reset_email
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["Auth"])
|
||||
router = APIRouter(tags=["Auth"])
|
||||
|
||||
@router.get("/login", response_class=HTMLResponse)
|
||||
async def login_page(request: Request, error: Optional[str] = None, message: Optional[str] = None):
|
||||
async def login_page(request: Request, error: Optional[str] = None, message: Optional[str] = None, db: Session = Depends(get_db)):
|
||||
"""Login page route"""
|
||||
# Check if user is already logged in
|
||||
user = None
|
||||
user_id = request.session.get("user_id")
|
||||
if user_id:
|
||||
user = db.query(User).get(user_id)
|
||||
# If user is already logged in, show a message
|
||||
if not message:
|
||||
message = "You are already logged in."
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/login.html",
|
||||
{"request": request, "error": error, "message": message,
|
||||
"show_oauth": True, "oauth_provider_name": "Authentik"}
|
||||
"show_oauth": True, "oauth_provider_name": "Authentik",
|
||||
"user": user} # Add user to context
|
||||
)
|
||||
|
||||
@router.post("/login", response_class=HTMLResponse)
|
||||
@@ -100,11 +110,20 @@ async def login_post(
|
||||
return RedirectResponse(next_page, status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
@router.get("/register", response_class=HTMLResponse)
|
||||
async def register_page(request: Request, error: Optional[str] = None):
|
||||
async def register_page(request: Request, error: Optional[str] = None, db: Session = Depends(get_db)):
|
||||
"""Registration page route"""
|
||||
# Check if user is already logged in
|
||||
user = None
|
||||
user_id = request.session.get("user_id")
|
||||
if user_id:
|
||||
user = db.query(User).get(user_id)
|
||||
# If no specific error is set, inform user they're already registered
|
||||
if not error:
|
||||
error = "You are already registered and logged in. You can logout first if you want to create a new account."
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"auth/register.html",
|
||||
{"request": request, "error": error}
|
||||
{"request": request, "error": error, "user": user} # Add user to context
|
||||
)
|
||||
|
||||
@router.post("/register", response_class=HTMLResponse)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Router for convenience redirects to simplify common URL patterns.
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
router = APIRouter(tags=["Convenience"])
|
||||
|
||||
@router.get("/scan")
|
||||
async def scan_redirect():
|
||||
"""Redirect /scan to /dashboard/scan"""
|
||||
return RedirectResponse("/dashboard/scan", status_code=303)
|
||||
|
||||
@router.get("/login")
|
||||
async def login_redirect():
|
||||
"""Redirect /login to /auth/login"""
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
|
||||
@router.get("/register")
|
||||
async def register_redirect():
|
||||
"""Redirect /register to /auth/register"""
|
||||
return RedirectResponse("/auth/register", status_code=303)
|
||||
|
||||
@router.get("/profile")
|
||||
async def profile_redirect():
|
||||
"""Redirect /profile to /auth/profile"""
|
||||
return RedirectResponse("/auth/profile", status_code=303)
|
||||
|
||||
@router.get("/logout")
|
||||
async def logout_redirect():
|
||||
"""Redirect /logout to /auth/logout"""
|
||||
return RedirectResponse("/auth/logout", status_code=303)
|
||||
+262
-42
@@ -2,17 +2,22 @@
|
||||
"""
|
||||
Teams management for users and admins.
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, Request, Form, HTTPException
|
||||
from fastapi import APIRouter, Depends, Request, Form, HTTPException, status, BackgroundTasks
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, desc, inspect
|
||||
from sqlalchemy import func, desc, inspect, or_, and_
|
||||
from datetime import datetime, timedelta
|
||||
import random # For demo data
|
||||
import secrets
|
||||
from starlette.status import HTTP_303_SEE_OTHER
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
|
||||
from ..db import SessionLocal
|
||||
from ..models import Team, TeamMembership, User, QRCode, TeamAchievement, TeamMember
|
||||
from ..db import SessionLocal, get_db
|
||||
from ..models import Team, TeamMembership, User, QRCode, TeamAchievement, TeamMember, TeamJoinRequest
|
||||
from ..schemas import TeamCreate
|
||||
from ..templates_config import templates
|
||||
from ..utils.auth import get_current_user, is_team_captain
|
||||
from ..utils.mail import send_team_join_request_notification, send_join_request_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -60,60 +65,260 @@ def list_teams(request: Request, db: Session = Depends(get_db)):
|
||||
}
|
||||
)
|
||||
|
||||
@router.post("/create")
|
||||
def create_team(request: Request, name: str = Form(...), db: Session = Depends(get_db)):
|
||||
# Check if user is logged in
|
||||
user_id = request.session.get("user_id")
|
||||
if not user_id:
|
||||
return RedirectResponse("/auth/login?next=/teams", status_code=303)
|
||||
|
||||
# Get the user
|
||||
user = db.query(User).get(user_id)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
@router.post("/create", response_class=HTMLResponse)
|
||||
async def create_team_post(
|
||||
request: Request,
|
||||
name: str = Form(...),
|
||||
description: str = Form(""),
|
||||
logo_url: str = Form(""),
|
||||
is_open: bool = Form(False), # Added is_open field
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Handle team creation form submission"""
|
||||
if not current_user:
|
||||
return RedirectResponse("/auth/login", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Check if team name already exists
|
||||
existing_team = db.query(Team).filter(Team.name == name).first()
|
||||
if existing_team:
|
||||
# Return to teams page with error message
|
||||
# In a real app, you'd add error handling/flash messages
|
||||
return RedirectResponse("/teams/?error=Team+name+already+exists", status_code=303)
|
||||
return RedirectResponse("/teams/?error=Team+name+already+exists", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Create team with the user as owner
|
||||
new_team = Team(name=name, owner_id=user_id)
|
||||
db.add(new_team)
|
||||
# Create team
|
||||
team = Team(
|
||||
name=name,
|
||||
description=description,
|
||||
logo_url=logo_url,
|
||||
is_open=is_open # Save is_open value
|
||||
)
|
||||
db.add(team)
|
||||
db.commit()
|
||||
db.refresh(new_team)
|
||||
db.refresh(team)
|
||||
|
||||
# Make the user an admin of the team in TeamMembership
|
||||
team_membership = TeamMembership(
|
||||
user_id=user_id,
|
||||
team_id=new_team.id,
|
||||
user_id=current_user.id,
|
||||
team_id=team.id,
|
||||
is_admin=True # User becomes admin of the team
|
||||
)
|
||||
db.add(team_membership)
|
||||
|
||||
# Check if TeamMember model exists in the database
|
||||
try:
|
||||
# Use a safer approach to check if the model exists and is usable
|
||||
if 'team_members' in inspect(db.bind).get_table_names():
|
||||
# Create TeamMember relationship as well
|
||||
team_member = TeamMember(
|
||||
user_id=user_id,
|
||||
team_id=new_team.id,
|
||||
is_captain=True # User becomes captain in TeamMember model
|
||||
)
|
||||
db.add(team_member)
|
||||
except Exception as e:
|
||||
print(f"Could not create TeamMember record: {str(e)}")
|
||||
# Continue even if this fails - TeamMembership is primary relationship
|
||||
# Create TeamMember relationship as well
|
||||
team_member = TeamMember(
|
||||
user_id=current_user.id,
|
||||
team_id=team.id,
|
||||
is_captain=True # Use is_captain instead of role
|
||||
)
|
||||
db.add(team_member)
|
||||
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse("/teams/", status_code=303)
|
||||
return RedirectResponse("/teams/", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
@router.post("/teams/{team_id}/edit", response_class=HTMLResponse)
|
||||
async def edit_team_post(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
name: str = Form(...),
|
||||
description: str = Form(""),
|
||||
logo_url: str = Form(""),
|
||||
is_open: bool = Form(False), # Added is_open field
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Handle team edit form submission"""
|
||||
if not current_user:
|
||||
return RedirectResponse("/auth/login", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
team = db.query(Team).filter(Team.id == team_id).first()
|
||||
if not team:
|
||||
raise HTTPException(status_code=404, detail="Team not found")
|
||||
|
||||
# Check if user is admin
|
||||
membership = db.query(TeamMembership).filter_by(
|
||||
team_id=team.id,
|
||||
is_admin=True
|
||||
).first()
|
||||
|
||||
if not membership:
|
||||
raise HTTPException(status_code=403, detail="You don't have permission to update this team")
|
||||
|
||||
# Update team details
|
||||
team.name = name
|
||||
team.description = description
|
||||
team.logo_url = logo_url
|
||||
team.is_open = is_open # Update is_open value
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse(f"/teams/{team_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
@router.get("/{team_id}/join", response_class=HTMLResponse)
|
||||
async def join_team_page(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Page for joining a team"""
|
||||
if not current_user:
|
||||
return RedirectResponse(f"/auth/login?next=/teams/{team_id}/join", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Check if team exists
|
||||
team = db.query(Team).filter(Team.id == team_id, Team.is_active == True).first()
|
||||
if not team:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Team not found"}
|
||||
)
|
||||
|
||||
# Check if user is already a member
|
||||
existing_membership = db.query(TeamMember).filter(
|
||||
TeamMember.team_id == team_id,
|
||||
TeamMember.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if existing_membership:
|
||||
return templates.TemplateResponse(
|
||||
"teams/join_team.html",
|
||||
{
|
||||
"request": request,
|
||||
"team": team,
|
||||
"error": "You are already a member of this team"
|
||||
}
|
||||
)
|
||||
|
||||
# Check if there's a pending join request
|
||||
pending_request = db.query(TeamJoinRequest).filter(
|
||||
TeamJoinRequest.team_id == team_id,
|
||||
TeamJoinRequest.user_id == current_user.id,
|
||||
TeamJoinRequest.status == "pending"
|
||||
).first()
|
||||
|
||||
if pending_request:
|
||||
return templates.TemplateResponse(
|
||||
"teams/join_team.html",
|
||||
{
|
||||
"request": request,
|
||||
"team": team,
|
||||
"error": "You already have a pending join request for this team"
|
||||
}
|
||||
)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"teams/join_team.html",
|
||||
{"request": request, "team": team, "is_open": team.is_open}
|
||||
)
|
||||
|
||||
@router.post("/{team_id}/join", response_class=HTMLResponse)
|
||||
async def join_team_request(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
background_tasks: BackgroundTasks,
|
||||
message: str = Form(""),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Process join team request"""
|
||||
if not current_user:
|
||||
return RedirectResponse("/auth/login", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Check if team exists
|
||||
team = db.query(Team).filter(Team.id == team_id, Team.is_active == True).first()
|
||||
if not team:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Team not found"}
|
||||
)
|
||||
|
||||
# Check if user is already a member
|
||||
existing_membership = db.query(TeamMember).filter(
|
||||
TeamMember.team_id == team_id,
|
||||
TeamMember.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if existing_membership:
|
||||
return RedirectResponse(f"/teams/{team_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Open team - directly add the user
|
||||
if team.is_open:
|
||||
# Add user to team
|
||||
new_member = TeamMember(
|
||||
team_id=team_id,
|
||||
user_id=current_user.id,
|
||||
is_captain=False # Use is_captain instead of role
|
||||
)
|
||||
|
||||
db.add(new_member)
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse(
|
||||
f"/teams/{team_id}?message=You+have+joined+the+team+successfully",
|
||||
status_code=HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
# Closed team - create join request
|
||||
# Check for existing pending request
|
||||
existing_request = db.query(TeamJoinRequest).filter(
|
||||
TeamJoinRequest.team_id == team_id,
|
||||
TeamJoinRequest.user_id == current_user.id,
|
||||
TeamJoinRequest.status == "pending"
|
||||
).first()
|
||||
|
||||
if existing_request:
|
||||
return RedirectResponse(
|
||||
f"/teams/{team_id}?message=Your+join+request+is+pending+approval",
|
||||
status_code=HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
# Create request token
|
||||
request_token = secrets.token_urlsafe(32)
|
||||
|
||||
# Create join request
|
||||
join_request = TeamJoinRequest(
|
||||
team_id=team_id,
|
||||
user_id=current_user.id,
|
||||
message=message,
|
||||
request_token=request_token
|
||||
)
|
||||
|
||||
db.add(join_request)
|
||||
db.commit()
|
||||
|
||||
# Get team captains to notify
|
||||
captains = db.query(User).join(TeamMember).filter(
|
||||
TeamMember.team_id == team_id,
|
||||
TeamMember.is_captain == True # Use is_captain instead of role
|
||||
).all()
|
||||
|
||||
if not captains:
|
||||
print("No captains found for the team. Unable to send notifications.")
|
||||
else:
|
||||
# Send email notifications to all captains
|
||||
for captain in captains:
|
||||
try:
|
||||
await send_team_join_request_notification(
|
||||
captain_email=captain.email,
|
||||
captain_name=captain.username,
|
||||
requester_name=current_user.username,
|
||||
team_name=team.name,
|
||||
message=message,
|
||||
approval_token=request_token,
|
||||
background_tasks=background_tasks
|
||||
)
|
||||
# Log success
|
||||
print(f"Team join request notification sent to {captain.email}")
|
||||
except Exception as e:
|
||||
# Log the error but continue
|
||||
print(f"Failed to send notification to {captain.email}: {str(e)}")
|
||||
|
||||
return RedirectResponse(
|
||||
f"/teams/{team_id}?message=Your+join+request+has+been+submitted+for+approval",
|
||||
status_code=HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
@router.post("/join/{team_id}")
|
||||
def join_team(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
def direct_join_team(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
"""Handle direct team join requests"""
|
||||
# Check if user is logged in
|
||||
user_id = request.session.get("user_id")
|
||||
if not user_id:
|
||||
@@ -136,14 +341,27 @@ def join_team(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
|
||||
if existing:
|
||||
return RedirectResponse("/teams/?error=You+are+already+a+member+of+this+team", status_code=303)
|
||||
|
||||
# Check if team is closed (not open)
|
||||
if not team.is_open:
|
||||
# For closed teams, redirect to the join request page
|
||||
return RedirectResponse(f"/teams/{team_id}/join", status_code=303)
|
||||
|
||||
# Create membership
|
||||
# Create membership for open teams
|
||||
new_member = TeamMembership(
|
||||
user_id=user_id,
|
||||
team_id=team.id,
|
||||
is_admin=False
|
||||
)
|
||||
db.add(new_member)
|
||||
|
||||
# Also create TeamMember record for consistency
|
||||
team_member = TeamMember(
|
||||
user_id=user_id,
|
||||
team_id=team.id,
|
||||
is_captain=False # Use is_captain instead of role
|
||||
)
|
||||
db.add(team_member)
|
||||
db.commit()
|
||||
|
||||
# Redirect to the team detail page
|
||||
@@ -347,6 +565,7 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
|
||||
# Safely get team attributes
|
||||
is_public = getattr(team, 'is_public', False)
|
||||
is_open = getattr(team, 'is_open', False) # Add this line to get is_open status
|
||||
created_at = getattr(team, 'created_at', None)
|
||||
|
||||
# Calculate days since team was founded
|
||||
@@ -382,6 +601,7 @@ def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
"is_team_member": is_team_member,
|
||||
"days_ago": days_ago,
|
||||
"founded_date": founded_date_str,
|
||||
"user": user # Add user to the context
|
||||
"user": user, # Add user to the context
|
||||
"is_open": is_open # Pass is_open to the template
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
Teams module for LeagueLedger - handles team management functionality.
|
||||
"""
|
||||
from .routes import router
|
||||
|
||||
__all__ = ['router']
|
||||
@@ -0,0 +1,495 @@
|
||||
"""Team-related actions for team management"""
|
||||
from fastapi import Request, Depends, Form, HTTPException, BackgroundTasks
|
||||
from fastapi.responses import RedirectResponse
|
||||
from sqlalchemy.orm import Session
|
||||
import secrets
|
||||
from starlette.status import HTTP_303_SEE_OTHER
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from ...models import Team, TeamMembership, User, TeamJoinRequest
|
||||
from ...utils.auth import get_current_user
|
||||
from ...utils.mail import send_team_join_request_notification, send_join_request_response
|
||||
from ...templates_config import templates
|
||||
from .routes import get_db
|
||||
from . import utils
|
||||
|
||||
async def create_team_post(
|
||||
request: Request,
|
||||
name: str = Form(...),
|
||||
description: str = Form(""),
|
||||
logo_url: str = Form(""),
|
||||
is_open: bool = Form(False),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Handle team creation form submission"""
|
||||
if not current_user:
|
||||
return RedirectResponse("/auth/login", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Check if team name already exists
|
||||
existing_team = db.query(Team).filter(Team.name == name).first()
|
||||
if existing_team:
|
||||
return RedirectResponse("/teams/?error=Team+name+already+exists", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Create team with only the fields that exist in the model
|
||||
team_data = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"is_open": is_open,
|
||||
"owner_id": current_user.id
|
||||
}
|
||||
|
||||
# Only add logo_url if it exists in the Team model
|
||||
from sqlalchemy import inspect
|
||||
team_columns = [c.key for c in inspect(Team).columns]
|
||||
if "logo_url" in team_columns:
|
||||
team_data["logo_url"] = logo_url
|
||||
|
||||
team = Team(**team_data)
|
||||
db.add(team)
|
||||
db.commit()
|
||||
db.refresh(team)
|
||||
|
||||
# Make the user an admin and captain of the team
|
||||
team_membership = TeamMembership(
|
||||
user_id=current_user.id,
|
||||
team_id=team.id,
|
||||
is_admin=True,
|
||||
is_captain=True
|
||||
)
|
||||
db.add(team_membership)
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse("/teams/", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
async def edit_team_post(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
name: str = Form(...),
|
||||
description: str = Form(""),
|
||||
logo_url: str = Form(""),
|
||||
is_open: bool = Form(False),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Handle team edit form submission"""
|
||||
if not current_user:
|
||||
return RedirectResponse("/auth/login", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
team = db.query(Team).filter(Team.id == team_id).first()
|
||||
if not team:
|
||||
raise HTTPException(status_code=404, detail="Team not found")
|
||||
|
||||
# Check if user is admin
|
||||
membership = db.query(TeamMembership).filter_by(
|
||||
team_id=team.id,
|
||||
user_id=current_user.id,
|
||||
is_admin=True
|
||||
).first()
|
||||
|
||||
if not membership:
|
||||
raise HTTPException(status_code=403, detail="You don't have permission to update this team")
|
||||
|
||||
# Update team details
|
||||
team.name = name
|
||||
team.description = description
|
||||
team.logo_url = logo_url
|
||||
team.is_open = is_open
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse(f"/teams/{team_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
async def join_team_request(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
background_tasks: BackgroundTasks,
|
||||
message: str = Form(""),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Process join team request"""
|
||||
if not current_user:
|
||||
return RedirectResponse("/auth/login", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Check if team exists
|
||||
team = db.query(Team).filter(Team.id == team_id, Team.is_active == True).first()
|
||||
if not team:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Team not found"}
|
||||
)
|
||||
|
||||
# Check if user is already a member
|
||||
existing_membership = db.query(TeamMembership).filter(
|
||||
TeamMembership.team_id == team_id,
|
||||
TeamMembership.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if existing_membership:
|
||||
return RedirectResponse(f"/teams/{team_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
# Open team - directly add the user
|
||||
if team.is_open:
|
||||
# Add user to team
|
||||
new_member = TeamMembership(
|
||||
team_id=team_id,
|
||||
user_id=current_user.id,
|
||||
is_admin=False,
|
||||
is_captain=False
|
||||
)
|
||||
|
||||
db.add(new_member)
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse(
|
||||
f"/teams/{team_id}?message=You+have+joined+the+team+successfully",
|
||||
status_code=HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
# Closed team - create join request
|
||||
# Check for existing pending request
|
||||
existing_request = db.query(TeamJoinRequest).filter(
|
||||
TeamJoinRequest.team_id == team_id,
|
||||
TeamJoinRequest.user_id == current_user.id,
|
||||
TeamJoinRequest.status == "pending"
|
||||
).first()
|
||||
|
||||
if existing_request:
|
||||
return RedirectResponse(
|
||||
f"/teams/{team_id}?message=Your+join+request+is+pending+approval",
|
||||
status_code=HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
# Create request token
|
||||
request_token = secrets.token_urlsafe(32)
|
||||
|
||||
# Create join request
|
||||
join_request = TeamJoinRequest(
|
||||
team_id=team_id,
|
||||
user_id=current_user.id,
|
||||
message=message,
|
||||
request_token=request_token
|
||||
)
|
||||
|
||||
db.add(join_request)
|
||||
db.commit()
|
||||
|
||||
# Get team captains to notify - Updated to use TeamMembership instead of TeamMember
|
||||
captains = db.query(User).join(TeamMembership).filter(
|
||||
TeamMembership.team_id == team_id,
|
||||
TeamMembership.is_captain == True
|
||||
).all()
|
||||
|
||||
if not captains:
|
||||
print("No captains found for the team. Unable to send notifications.")
|
||||
else:
|
||||
# Send email notifications to all captains
|
||||
for captain in captains:
|
||||
try:
|
||||
await send_team_join_request_notification(
|
||||
captain_email=captain.email,
|
||||
captain_name=captain.username,
|
||||
requester_name=current_user.username,
|
||||
team_name=team.name,
|
||||
message=message,
|
||||
approval_token=request_token,
|
||||
background_tasks=background_tasks
|
||||
)
|
||||
# Log success
|
||||
print(f"Team join request notification sent to {captain.email}")
|
||||
except Exception as e:
|
||||
# Log the error but continue
|
||||
print(f"Failed to send notification to {captain.email}: {str(e)}")
|
||||
|
||||
return RedirectResponse(
|
||||
f"/teams/{team_id}?message=Your+join+request+has+been+submitted+for+approval",
|
||||
status_code=HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
def direct_join_team(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
"""Handle direct team join requests"""
|
||||
# Check if user is logged in
|
||||
user_id = request.session.get("user_id")
|
||||
if not user_id:
|
||||
return RedirectResponse("/auth/login?next=/teams", status_code=303)
|
||||
|
||||
# Get user
|
||||
user = db.query(User).get(user_id)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
|
||||
# Find the team
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
if not team:
|
||||
return RedirectResponse("/teams/?error=Team+not+found", status_code=303)
|
||||
|
||||
# Check if membership exists
|
||||
existing = db.query(TeamMembership)\
|
||||
.filter_by(user_id=user_id, team_id=team.id)\
|
||||
.first()
|
||||
|
||||
if existing:
|
||||
return RedirectResponse("/teams/?error=You+are+already+a+member+of+this+team", status_code=303)
|
||||
|
||||
# Check if team is closed (not open)
|
||||
if not team.is_open:
|
||||
# For closed teams, redirect to the join request page
|
||||
return RedirectResponse(f"/teams/{team_id}/join", status_code=303)
|
||||
|
||||
# Create membership for open teams
|
||||
new_member = TeamMembership(
|
||||
user_id=user_id,
|
||||
team_id=team.id,
|
||||
is_admin=False,
|
||||
is_captain=False
|
||||
)
|
||||
db.add(new_member)
|
||||
db.commit()
|
||||
|
||||
# Redirect to the team detail page
|
||||
return RedirectResponse(f"/teams/{team_id}", status_code=303)
|
||||
|
||||
def leave_team(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
"""Allow a user to leave a team"""
|
||||
# Check if user is logged in
|
||||
user_id = request.session.get("user_id")
|
||||
if not user_id:
|
||||
return RedirectResponse("/auth/login?next=/teams", status_code=303)
|
||||
|
||||
# Get team
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
if not team:
|
||||
raise HTTPException(status_code=404, detail="Team not found")
|
||||
|
||||
# Can't leave if you're the owner
|
||||
if team.owner_id == user_id:
|
||||
return RedirectResponse(f"/teams/{team_id}?error=Team+owner+cannot+leave", status_code=303)
|
||||
|
||||
# Find membership
|
||||
membership = db.query(TeamMembership)\
|
||||
.filter(TeamMembership.user_id == user_id, TeamMembership.team_id == team_id)\
|
||||
.first()
|
||||
|
||||
if not membership:
|
||||
return RedirectResponse("/teams/?error=You+are+not+a+member+of+this+team", status_code=303)
|
||||
|
||||
# Delete the team membership
|
||||
db.delete(membership)
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse("/teams/?message=Successfully+left+the+team", status_code=303)
|
||||
|
||||
def update_team(
|
||||
request: Request,
|
||||
team_id: int,
|
||||
team_name: str = Form(...),
|
||||
is_public: bool = Form(False),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Update team details."""
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
|
||||
if not team:
|
||||
raise HTTPException(status_code=404, detail="Team not found")
|
||||
|
||||
# Check if user is admin
|
||||
membership = db.query(TeamMembership).filter_by(
|
||||
team_id=team.id,
|
||||
user_id=request.session.get("user_id"),
|
||||
is_admin=True
|
||||
).first()
|
||||
|
||||
if not membership:
|
||||
raise HTTPException(status_code=403, detail="You don't have permission to update this team")
|
||||
|
||||
# Update team details
|
||||
team.name = team_name
|
||||
team.is_public = is_public
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse(f"/teams/{team_id}", status_code=303)
|
||||
|
||||
async def approve_join_request(request: Request, token: str, db: Session = Depends(get_db)):
|
||||
"""Approve a team join request using the provided token"""
|
||||
# Find the join request by token
|
||||
join_request = db.query(TeamJoinRequest).filter_by(request_token=token).first()
|
||||
|
||||
if not join_request:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Invalid or expired join request"}
|
||||
)
|
||||
|
||||
if join_request.status != "pending":
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "This request has already been processed"}
|
||||
)
|
||||
|
||||
# Get the team
|
||||
team = db.query(Team).filter_by(id=join_request.team_id).first()
|
||||
if not team:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Team not found"}
|
||||
)
|
||||
|
||||
# Check if the user has permission to approve requests
|
||||
# Get user from session
|
||||
user_id = request.session.get("user_id")
|
||||
if not user_id:
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
|
||||
user = db.query(User).get(user_id)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
|
||||
# Check if user is admin, owner or captain
|
||||
is_user_admin, is_user_owner, is_captain = utils.check_user_permissions(db, user, join_request.team_id, team)
|
||||
|
||||
if not (is_user_admin or is_user_owner or is_captain):
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "You don't have permission to approve join requests"}
|
||||
)
|
||||
|
||||
# Get the user who requested to join
|
||||
requester = db.query(User).filter_by(id=join_request.user_id).first()
|
||||
if not requester:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Requesting user not found"}
|
||||
)
|
||||
|
||||
# Check if user is already a member
|
||||
existing_membership = db.query(TeamMembership).filter(
|
||||
TeamMembership.team_id == join_request.team_id,
|
||||
TeamMembership.user_id == join_request.user_id
|
||||
).first()
|
||||
|
||||
if existing_membership:
|
||||
# Update request status
|
||||
join_request.status = "approved"
|
||||
db.commit()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"teams/request_processed.html",
|
||||
{
|
||||
"request": request,
|
||||
"message": f"{requester.username} is already a member of {team.name}",
|
||||
"team_id": team.id
|
||||
}
|
||||
)
|
||||
|
||||
# Create team membership
|
||||
new_member = TeamMembership(
|
||||
team_id=join_request.team_id,
|
||||
user_id=join_request.user_id,
|
||||
is_admin=False,
|
||||
is_captain=False
|
||||
)
|
||||
db.add(new_member)
|
||||
|
||||
# Update request status
|
||||
join_request.status = "approved"
|
||||
db.commit()
|
||||
|
||||
# Send notification to the user (if email sending is available)
|
||||
try:
|
||||
background_tasks = BackgroundTasks()
|
||||
await send_join_request_response(
|
||||
user_email=requester.email,
|
||||
user_name=requester.username,
|
||||
team_name=team.name,
|
||||
approved=True,
|
||||
background_tasks=background_tasks
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to send approval notification: {str(e)}")
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"teams/request_processed.html",
|
||||
{
|
||||
"request": request,
|
||||
"message": f"Successfully approved {requester.username}'s request to join {team.name}",
|
||||
"team_id": team.id
|
||||
}
|
||||
)
|
||||
|
||||
async def deny_join_request(request: Request, token: str, db: Session = Depends(get_db)):
|
||||
"""Deny a team join request using the provided token"""
|
||||
# Find the join request by token
|
||||
join_request = db.query(TeamJoinRequest).filter_by(request_token=token).first()
|
||||
|
||||
if not join_request:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Invalid or expired join request"}
|
||||
)
|
||||
|
||||
if join_request.status != "pending":
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "This request has already been processed"}
|
||||
)
|
||||
|
||||
# Get the team
|
||||
team = db.query(Team).filter_by(id=join_request.team_id).first()
|
||||
if not team:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Team not found"}
|
||||
)
|
||||
|
||||
# Check if the user has permission to deny requests
|
||||
# Get user from session
|
||||
user_id = request.session.get("user_id")
|
||||
if not user_id:
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
|
||||
user = db.query(User).get(user_id)
|
||||
if not user:
|
||||
return RedirectResponse("/auth/login", status_code=303)
|
||||
|
||||
# Check if user is admin, owner or captain
|
||||
is_user_admin, is_user_owner, is_captain = utils.check_user_permissions(db, user, join_request.team_id, team)
|
||||
|
||||
if not (is_user_admin or is_user_owner or is_captain):
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "You don't have permission to deny join requests"}
|
||||
)
|
||||
|
||||
# Get the user who requested to join
|
||||
requester = db.query(User).filter_by(id=join_request.user_id).first()
|
||||
if not requester:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Requesting user not found"}
|
||||
)
|
||||
|
||||
# Update request status
|
||||
join_request.status = "denied"
|
||||
db.commit()
|
||||
|
||||
# Send notification to the user (if email sending is available)
|
||||
try:
|
||||
background_tasks = BackgroundTasks()
|
||||
await send_join_request_response(
|
||||
user_email=requester.email,
|
||||
user_name=requester.username,
|
||||
team_name=team.name,
|
||||
approved=False,
|
||||
background_tasks=background_tasks
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to send denial notification: {str(e)}")
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"teams/request_processed.html",
|
||||
{
|
||||
"request": request,
|
||||
"message": f"Successfully denied {requester.username}'s request to join {team.name}",
|
||||
"team_id": team.id
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...db import SessionLocal, get_db
|
||||
from . import views, actions
|
||||
from ...auth.utils import get_current_user_from_session
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# Main routes for teams
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
def list_teams(request: Request, db: Session = Depends(get_db)):
|
||||
"""List all available teams"""
|
||||
# No need to explicitly get current user - it's in request.state.user
|
||||
# from middleware and will be passed to the template
|
||||
return views.list_teams_view(request, db)
|
||||
|
||||
@router.get("/{team_id}", response_class=HTMLResponse)
|
||||
def team_detail(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
"""Show details for a specific team."""
|
||||
# User is already available in request.state.user
|
||||
return views.team_detail_view(request, team_id, db)
|
||||
|
||||
@router.get("/{team_id}/join", response_class=HTMLResponse)
|
||||
async def join_team_page(request: Request, team_id: int, db: Session = Depends(get_db)):
|
||||
"""Page for joining a team"""
|
||||
# User is already available in request.state.user
|
||||
return await views.join_team_page_view(request, team_id, db)
|
||||
|
||||
# Action routes
|
||||
router.add_api_route("/create", actions.create_team_post, methods=["POST"], response_class=HTMLResponse)
|
||||
router.add_api_route("/{team_id}/edit", actions.edit_team_post, methods=["POST"], response_class=HTMLResponse)
|
||||
router.add_api_route("/{team_id}/join", actions.join_team_request, methods=["POST"], response_class=HTMLResponse)
|
||||
router.add_api_route("/join/{team_id}", actions.direct_join_team, methods=["POST"])
|
||||
router.add_api_route("/{team_id}/leave", actions.leave_team, methods=["POST"])
|
||||
router.add_api_route("/{team_id}/update", actions.update_team, methods=["POST"])
|
||||
|
||||
# Add these new routes for handling join requests
|
||||
@router.get("/approve-request/{token}", response_class=HTMLResponse)
|
||||
async def approve_join_request(request: Request, token: str, db: Session = Depends(get_db)):
|
||||
"""Approve a team join request using the provided token"""
|
||||
# User is already available in request.state.user
|
||||
return await actions.approve_join_request(request, token, db)
|
||||
|
||||
@router.get("/deny-request/{token}", response_class=HTMLResponse)
|
||||
async def deny_join_request(request: Request, token: str, db: Session = Depends(get_db)):
|
||||
"""Deny a team join request using the provided token"""
|
||||
# User is already available in request.state.user
|
||||
return await actions.deny_join_request(request, token, db)
|
||||
@@ -0,0 +1,166 @@
|
||||
"""Helper functions for team views and actions"""
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, inspect
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
|
||||
from ...models import Team, TeamMembership, User, QRCode
|
||||
|
||||
def get_team_members_with_details(db: Session, team_id: int):
|
||||
"""Get team members with additional details"""
|
||||
memberships = db.query(TeamMembership).filter_by(team_id=team_id).all()
|
||||
team_members = []
|
||||
|
||||
for membership in memberships:
|
||||
member = db.query(User).filter_by(id=membership.user_id).first()
|
||||
if member:
|
||||
# Use joined_at if available, otherwise use placeholder
|
||||
joined_date = membership.joined_at or datetime.now() - timedelta(days=random.randint(30, 180))
|
||||
if isinstance(joined_date, datetime):
|
||||
month_name = joined_date.strftime("%b")
|
||||
year = joined_date.strftime("%Y")
|
||||
else:
|
||||
month_name = "Apr"
|
||||
year = "2023"
|
||||
|
||||
team_members.append({
|
||||
"user": member,
|
||||
"is_admin": membership.is_admin,
|
||||
"is_captain": membership.is_captain,
|
||||
"joined": f"{month_name} {year}"
|
||||
})
|
||||
|
||||
return team_members
|
||||
|
||||
def check_user_permissions(db: Session, user, team_id: int, team):
|
||||
"""Check if user is admin or owner of the team"""
|
||||
is_user_admin = False
|
||||
is_user_owner = False
|
||||
is_captain = False
|
||||
|
||||
if user:
|
||||
# Check admin status
|
||||
membership = db.query(TeamMembership).filter_by(
|
||||
team_id=team_id,
|
||||
user_id=user.id,
|
||||
is_admin=True
|
||||
).first()
|
||||
is_user_admin = membership is not None
|
||||
|
||||
# Check captain status
|
||||
captain_membership = db.query(TeamMembership).filter_by(
|
||||
team_id=team_id,
|
||||
user_id=user.id,
|
||||
is_captain=True
|
||||
).first()
|
||||
is_captain = captain_membership is not None
|
||||
|
||||
# Check owner status
|
||||
is_user_owner = hasattr(team, 'owner_id') and team.owner_id == user.id
|
||||
if is_user_owner:
|
||||
is_user_admin = True # Owner has admin privileges
|
||||
|
||||
return is_user_admin, is_user_owner, is_captain
|
||||
|
||||
def get_team_total_points(db: Session, team_id: int):
|
||||
"""Get total points for a team"""
|
||||
total_points = db.query(func.sum(QRCode.points)).filter(
|
||||
QRCode.redeemed_at_team == team_id
|
||||
).scalar() or 0
|
||||
|
||||
return total_points
|
||||
|
||||
def calculate_team_rank(db: Session, team_id: int):
|
||||
"""Calculate team rank based on points"""
|
||||
try:
|
||||
# First, get the aggregated points for all teams
|
||||
team_points = db.query(
|
||||
QRCode.redeemed_at_team,
|
||||
func.sum(QRCode.points).label('total')
|
||||
).filter(
|
||||
QRCode.redeemed_at_team != None
|
||||
).group_by(QRCode.redeemed_at_team).all()
|
||||
|
||||
# Sort them by points (descending)
|
||||
sorted_teams = sorted(team_points, key=lambda x: x.total or 0, reverse=True)
|
||||
|
||||
# Find our team's position
|
||||
team_rank = 1
|
||||
for idx, team_data in enumerate(sorted_teams):
|
||||
if team_data.redeemed_at_team == team_id:
|
||||
team_rank = idx + 1
|
||||
break
|
||||
|
||||
return team_rank
|
||||
except Exception as e:
|
||||
print(f"Error calculating team rank: {e}")
|
||||
return 1 # Default to 1st place on error
|
||||
|
||||
def get_team_points_history(db: Session, team_id: int):
|
||||
"""Get points history for a team"""
|
||||
# Default values
|
||||
points_this_month = 65
|
||||
point_change = 15
|
||||
point_change_positive = True
|
||||
|
||||
# Try to get actual data if available
|
||||
try:
|
||||
now = datetime.now()
|
||||
first_day_of_month = datetime(now.year, now.month, 1)
|
||||
|
||||
# Use raw SQL to check if column exists and get points
|
||||
has_redeemed_at = False
|
||||
inspector = inspect(db.bind)
|
||||
if 'redeemed_at' in [col['name'] for col in inspector.get_columns('qr_codes')]:
|
||||
has_redeemed_at = True
|
||||
|
||||
if has_redeemed_at:
|
||||
points_this_month = db.query(func.sum(QRCode.points)).filter(
|
||||
QRCode.redeemed_at_team == team_id,
|
||||
QRCode.redeemed_at >= first_day_of_month
|
||||
).scalar() or points_this_month
|
||||
except Exception as e:
|
||||
print(f"Error calculating monthly points: {e}")
|
||||
|
||||
return points_this_month, point_change, point_change_positive
|
||||
|
||||
def get_team_activities():
|
||||
"""Get team activities (currently returns mock data)"""
|
||||
return [
|
||||
{
|
||||
"type": "points",
|
||||
"points": 15,
|
||||
"event": "Music Trivia Night",
|
||||
"date": "September 12, 2023"
|
||||
},
|
||||
{
|
||||
"type": "join",
|
||||
"user": "Robert Brown",
|
||||
"date": "July 28, 2023"
|
||||
},
|
||||
{
|
||||
"type": "achievement",
|
||||
"achievement": "1st place",
|
||||
"event": "History Night",
|
||||
"date": "July 15, 2023"
|
||||
},
|
||||
{
|
||||
"type": "points",
|
||||
"points": 20,
|
||||
"event": "Movie Trivia Night",
|
||||
"date": "July 1, 2023"
|
||||
}
|
||||
]
|
||||
|
||||
def get_team_age(team):
|
||||
"""Calculate team age"""
|
||||
created_at = getattr(team, 'created_at', None)
|
||||
|
||||
if created_at and isinstance(created_at, datetime):
|
||||
days_ago = (datetime.now() - created_at).days
|
||||
founded_date_str = created_at.strftime("%B %d, %Y")
|
||||
else:
|
||||
days_ago = 164 # Default fallback
|
||||
founded_date_str = "March 22, 2023" # Default fallback
|
||||
|
||||
return days_ago, founded_date_str
|
||||
@@ -0,0 +1,178 @@
|
||||
"""Team-related view functions for rendering templates"""
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi.responses import RedirectResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
from sqlalchemy import inspect
|
||||
|
||||
from ...models import Team, TeamMembership, User, QRCode, TeamJoinRequest
|
||||
from ...templates_config import templates
|
||||
from ...utils.auth import get_current_user
|
||||
from . import utils
|
||||
|
||||
def list_teams_view(request: Request, db: Session):
|
||||
"""Render the teams list view"""
|
||||
teams = db.query(Team).all()
|
||||
|
||||
# Get the user's teams to highlight teams they're already in
|
||||
user_team_ids = []
|
||||
|
||||
# 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 teams that user is a member of
|
||||
memberships = db.query(TeamMembership).filter(TeamMembership.user_id == user_id).all()
|
||||
user_team_ids = [membership.team_id for membership in memberships]
|
||||
|
||||
# Get error message if present
|
||||
error = request.query_params.get("error")
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"teams.html",
|
||||
{
|
||||
"request": request,
|
||||
"teams": teams,
|
||||
"user_team_ids": user_team_ids,
|
||||
"user": user,
|
||||
"error": error,
|
||||
"brand_colors": {
|
||||
"irish_green": "#006837",
|
||||
"golden_ale": "#FFB400",
|
||||
"cream_white": "#F5F0E1",
|
||||
"black_stout": "#1A1A1A",
|
||||
"guinness_red": "#B22222"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
async def join_team_page_view(request: Request, team_id: int, db: Session):
|
||||
"""Render the join team page"""
|
||||
user_id = request.session.get("user_id")
|
||||
current_user = db.query(User).get(user_id) if user_id else None
|
||||
|
||||
if not current_user:
|
||||
return RedirectResponse(f"/auth/login?next=/teams/{team_id}/join", status_code=303)
|
||||
|
||||
# Check if team exists
|
||||
team = db.query(Team).filter(Team.id == team_id, Team.is_active == True).first()
|
||||
if not team:
|
||||
return templates.TemplateResponse(
|
||||
"error.html",
|
||||
{"request": request, "error": "Team not found"}
|
||||
)
|
||||
|
||||
# Check if user is already a member
|
||||
existing_membership = db.query(TeamMembership).filter(
|
||||
TeamMembership.team_id == team_id,
|
||||
TeamMembership.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if existing_membership:
|
||||
return templates.TemplateResponse(
|
||||
"teams/join_team.html",
|
||||
{
|
||||
"request": request,
|
||||
"team": team,
|
||||
"error": "You are already a member of this team"
|
||||
}
|
||||
)
|
||||
|
||||
# Check if there's a pending join request
|
||||
pending_request = db.query(TeamJoinRequest).filter(
|
||||
TeamJoinRequest.team_id == team_id,
|
||||
TeamJoinRequest.user_id == current_user.id,
|
||||
TeamJoinRequest.status == "pending"
|
||||
).first()
|
||||
|
||||
if pending_request:
|
||||
return templates.TemplateResponse(
|
||||
"teams/join_team.html",
|
||||
{
|
||||
"request": request,
|
||||
"team": team,
|
||||
"error": "You already have a pending join request for this team"
|
||||
}
|
||||
)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"teams/join_team.html",
|
||||
{"request": request, "team": team, "is_open": team.is_open}
|
||||
)
|
||||
|
||||
def team_detail_view(request: Request, team_id: int, db: Session):
|
||||
"""Render the team detail page"""
|
||||
# Get team
|
||||
team = db.query(Team).filter_by(id=team_id).first()
|
||||
if not team:
|
||||
raise HTTPException(status_code=404, detail="Team not found")
|
||||
|
||||
# Get user from session for navbar
|
||||
user = None
|
||||
user_id = request.session.get("user_id")
|
||||
is_team_member = False
|
||||
|
||||
if user_id:
|
||||
user = db.query(User).get(user_id)
|
||||
|
||||
# Check if user is a team member
|
||||
team_membership = db.query(TeamMembership)\
|
||||
.filter(TeamMembership.user_id == user_id, TeamMembership.team_id == team_id)\
|
||||
.first()
|
||||
|
||||
is_team_member = team_membership is not None
|
||||
|
||||
# Get team members with user info
|
||||
team_members = utils.get_team_members_with_details(db, team_id)
|
||||
|
||||
# Check if user is admin or owner
|
||||
is_user_admin, is_user_owner, is_captain = utils.check_user_permissions(db, user, team_id, team)
|
||||
|
||||
# Get team statistics
|
||||
total_points = utils.get_team_total_points(db, team_id)
|
||||
team_rank = utils.calculate_team_rank(db, team_id)
|
||||
points_this_month, point_change, point_change_positive = utils.get_team_points_history(db, team_id)
|
||||
|
||||
# Activities - simple mock data for now
|
||||
activities = utils.get_team_activities()
|
||||
|
||||
# Get team metadata
|
||||
days_ago, founded_date_str = utils.get_team_age(team)
|
||||
|
||||
# Performance metrics
|
||||
performance = {
|
||||
"last_quiz": "25 points (2nd place)",
|
||||
"average": "18.7 points",
|
||||
"best_streak": "3 wins in a row"
|
||||
}
|
||||
|
||||
# Safely get team attributes
|
||||
is_public = getattr(team, 'is_public', False)
|
||||
is_open = getattr(team, 'is_open', False)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"team_detail.html",
|
||||
{
|
||||
"request": request,
|
||||
"team": team,
|
||||
"team_members": team_members,
|
||||
"team_rank": team_rank,
|
||||
"total_points": total_points,
|
||||
"points_this_month": points_this_month,
|
||||
"point_change": point_change,
|
||||
"point_change_positive": point_change_positive,
|
||||
"activities": activities,
|
||||
"performance": performance,
|
||||
"is_user_admin": is_user_admin,
|
||||
"is_user_owner": is_user_owner,
|
||||
"is_team_member": is_team_member,
|
||||
"is_captain": is_captain,
|
||||
"days_ago": days_ago,
|
||||
"founded_date": founded_date_str,
|
||||
"user": user,
|
||||
"is_open": is_open
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user