"""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 ...league_context import resolve_selected_league 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), league_id: int = Form(None), 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) selected_league = resolve_selected_league(db, league_id) # Check if team name already exists in this league existing_team = db.query(Team).filter( Team.name == name, Team.league_id == selected_league.id ).first() if existing_team: return RedirectResponse( f"/teams/?league_id={selected_league.id}&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, "league_id": selected_league.id, "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(f"/teams/?league_id={selected_league.id}", 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 } )