From 7ddb0b4fee47b9be063ad1f616de500dda8f2cef Mon Sep 17 00:00:00 2001 From: Reginaldo-Neto Date: Wed, 7 Jun 2023 01:12:12 -0300 Subject: [PATCH] #103 - CRUD de matches --- backend/api/models/championships.py | 6 +- backend/api/models/matches.py | 16 ++ backend/api/routers/championships_routes.py | 4 +- backend/api/routers/matches_routes.py | 206 ++++++++++++++++++++ backend/api/routers/routes.py | 3 +- backend/api/schemas/championships.py | 2 +- backend/api/schemas/matches.py | 33 ++-- 7 files changed, 242 insertions(+), 28 deletions(-) create mode 100644 backend/api/models/matches.py create mode 100644 backend/api/routers/matches_routes.py diff --git a/backend/api/models/championships.py b/backend/api/models/championships.py index 29676e0..ed1a757 100644 --- a/backend/api/models/championships.py +++ b/backend/api/models/championships.py @@ -2,9 +2,6 @@ from sqlalchemy.orm import relationship from api.database.config import Base import enum -from datetime import datetime -from typing import Optional -from pydantic import BaseModel class EnumFormat(enum.Enum): @@ -32,4 +29,5 @@ class Championship(Base): created_at = Column(DateTime) admin_id = Column(Integer, ForeignKey("users.id")) game_id = Column(Integer, ForeignKey("games.id")) - teams = relationship("Team", secondary="championship_has_teams", backref="teams") + matches = Column(Integer, ForeignKey("matches.id")) + teams = relationship("Team", secondary="championship_has_teams", backref="teams") \ No newline at end of file diff --git a/backend/api/models/matches.py b/backend/api/models/matches.py new file mode 100644 index 0000000..dec3f2f --- /dev/null +++ b/backend/api/models/matches.py @@ -0,0 +1,16 @@ +from sqlalchemy import Column, Integer, String, ForeignKey +from api.database.config import Base + + +class Match(Base): + __tablename__ = "matches" + + id = Column(Integer, primary_key=True, autoincrement=True) + championship_id = Column(Integer, ForeignKey("championships.id")) + team_1_id = Column(Integer, ForeignKey("teams.id")) + team_2_id = Column(Integer, ForeignKey("teams.id")) + winner_team_id = Column(Integer, ForeignKey("teams.id")) + bracket = Column(Integer) + round = Column(Integer) + result = Column(String) + \ No newline at end of file diff --git a/backend/api/routers/championships_routes.py b/backend/api/routers/championships_routes.py index 5e7fdfb..51e89f3 100644 --- a/backend/api/routers/championships_routes.py +++ b/backend/api/routers/championships_routes.py @@ -168,11 +168,11 @@ async def update(id: int, update_request: ChampionshipUpdateRequest, token: Anno if championship_exists: raise HTTPException(status_code=400, detail="Championship with this name already exists") - if update_request.max_teams and update_request.min_teams == None: + if update_request.max_teams and update_request.min_teams is not None: if championship.min_teams > update_request.max_teams: raise HTTPException(status_code=400, detail="Max Teams cannot be less than Min Teams") - if update_request.min_teams and update_request.max_teams == None: + if update_request.min_teams and update_request.max_teams is not None: if championship.max_teams < update_request.min_teams: raise HTTPException(status_code=400, detail="Min Teams cannot be greater than Max Teams") diff --git a/backend/api/routers/matches_routes.py b/backend/api/routers/matches_routes.py new file mode 100644 index 0000000..24f60a9 --- /dev/null +++ b/backend/api/routers/matches_routes.py @@ -0,0 +1,206 @@ +from api.database.config import Session, engine + +from api.schemas.matches import ( + Response, + MatchInput, + MatchSchema, + MatchUpdateRequest +) +from api.models.matches import Match +from api.models.championships import Championship +from api.models.teams import Team +from api.utils.auth_services import get_password_hash, oauth2_scheme, get_current_user +from fastapi import APIRouter, HTTPException, Depends +from typing import Annotated +from fastapi.encoders import jsonable_encoder +from sqlalchemy import Enum, func +from sqlalchemy.orm import joinedload + + +router = APIRouter( + prefix="/matches", + tags=["matches"], + # dependencies=[Depends(get_token_header)], + responses={404: {"description": "Matches Not Found"}}, +) + +session = Session(bind=engine) + + +@router.get( + "/", + response_model=list[MatchSchema], + response_description="Sucesso de resposta da aplicação.", +) +async def getAll( + + id: int = None, + championship_id: int = None, + team_1_id: int = None, + team_2_id: int = None, + winner_team_id: int = None, + bracket: int = None, + round: int = None, + result: str = None, + skip: int = 0, + limit: int = 100, +): + query = session.query(Match) + + if id is not None: + query = query.filter(Match.id == id) + if championship_id is not None: + query = query.filter(Match.championship_id == championship_id) + if team_1_id is not None: + query = query.filter(Match.team_1_id == team_1_id) + if team_2_id is not None: + query = query.filter(Match.team_2_id == team_2_id) + if winner_team_id is not None: + query = query.filter(Match.winner_team_id == winner_team_id) + if result is not None: + query = query.filter(func.lower(Match.result).like(f"%{result.lower()}%")) + if round is not None: + query = query.filter(Match.round == round) + if bracket is not None: + query = query.filter(Match.bracket == bracket) + + Matches = query.offset(skip).limit(limit).all() + + return jsonable_encoder(Matches) + + +@router.get( + "/{id}", + response_model=MatchSchema, + response_description="Sucesso de resposta da aplicação.", +) +async def getById(id: int): + match = ( + session.query(Match).options(joinedload(Match.team_1_id), joinedload(Match.team_2_id)). + filter(Match.id == id). + first() + ) + if match == None: + raise HTTPException(status_code=404, detail="Match not found") + return jsonable_encoder(match) + +@router.get( + "/championship/{championship_id}", + response_model=MatchSchema, + response_description="Sucesso de resposta da aplicação.", +) + +async def getByChampionshipId(championship_id: int, skip: int = 0, limit: int = 100,): + camp = session.query(Championship).filter(Championship.id == championship_id).first() + + if camp == None: + raise HTTPException(status_code=404, detail="Championship not found") + matches = (session.query + .options(joinedload(Championship.matches)) + .filter(Match.championship_id == championship_id) + .offset(skip) + .limit(limit) + .all() + ) + if matches == None: + raise HTTPException(status_code=404, detail="Match not found") + return jsonable_encoder(matches) + + +@router.post( + "/create", + status_code=201, + response_model=MatchSchema, + response_description="Sucesso de resposta da aplicação.", +) +async def create(data: MatchInput, token: Annotated[str, Depends(oauth2_scheme)]): + part = session.query(Match).filter(Match.id == data.id).first() + if part != None: + raise HTTPException(status_code=400, detail="Match with this ID already exists") + user = await get_current_user(token) + time1 = session.query(Team).filter(Team.id == data.team_1_id).first() + if time1 == None: + raise HTTPException(status_code=404, detail="Team 1 not found") + time2 = session.query(Team).filter(Team.id == data.team_2_id).first() + if time2 == None: + raise HTTPException(status_code=404, detail="Team 2 not found") + championship = session.query(Championship).filter(Championship.id == data.championship_id).first() + if championship == None: + raise HTTPException(status_code=404, detail="Championship not found") + + match_input = Match( + id=data.id, + championship_id=data.championship_id, + team_1_id=data.team_1_id, + team_2_id=data.team_2_id, + winner_team_id=data.winner_team_id, + bracket=data.bracket, + round=data.round, + result=data.result + ) + session.add(match_input) + session.commit() + session.refresh(match_input) + response = { + "id": match_input.id, + "championship_id": match_input.championship_id, + "team_1_id": match_input.team_1_id, + "team_2_id": match_input.team_2_id, + "winner_team_id": match_input.winner_team_id, + "bracket": match_input.bracket, + "round": match_input.round, + "result": match_input.result + } + return jsonable_encoder(response) + +#Vou deixar o delete comentado pq o Rafa disse que nao ia precisar, mas se no futuro precisar ja ta pronto +#@router.delete( +# "/delete/{id}", +# status_code=200, +# response_model=MatchSchema, +# response_description="Sucesso de resposta da aplicação.", +#) +#async def delete(id: int): +# match = session.query(Match).filter(Match.id == id).first() +# if match == None: +# raise HTTPException(status_code=404, detail="Match not found") + +# session.delete(match) +# session.commit() + +# return jsonable_encoder(match) + + +@router.put( + "/update/{id}", + status_code=200, + response_model=MatchSchema, + response_description="Sucesso de resposta da aplicação.", +) +async def update(id: int, update_request: MatchUpdateRequest, token: Annotated[str, Depends(oauth2_scheme)]): + user = await get_current_user(token) + match = session.query(Match).filter(Match.id == id).first() + + if match is None: + raise HTTPException( + status_code=404, detail="Match not found" + ) + #winner round result + venc = session.query(Team).filter(Match.team_1_id == update_request.winner_team_id and Match.team_2_id == update_request.winner_team_id).first() + if venc is None: + raise HTTPException( + status_code=404, detail="Team not found" + ) + + if update_request.result == None: + raise HTTPException(status_code=400, detail="Result cannot be None") + + update_data = update_request.dict(exclude_unset=True) + for key, value in update_data.items(): + if getattr(match, key) != value: + setattr(match, key, value) + + session.commit() + session.refresh(match) + + return jsonable_encoder(match) \ No newline at end of file diff --git a/backend/api/routers/routes.py b/backend/api/routers/routes.py index b903380..988136a 100644 --- a/backend/api/routers/routes.py +++ b/backend/api/routers/routes.py @@ -3,4 +3,5 @@ from .auth_routes import router as auth_routes from .championships_routes import router as championships_routes from .games_routes import router as games_routes -routes = [users_router, teams_router, auth_routes, championships_routes, games_routes] +from .matches_routes import router as matches_routes +routes = [users_router, teams_router, auth_routes, championships_routes, games_routes, matches_routes] diff --git a/backend/api/schemas/championships.py b/backend/api/schemas/championships.py index 27cb1af..c5275ed 100644 --- a/backend/api/schemas/championships.py +++ b/backend/api/schemas/championships.py @@ -134,7 +134,7 @@ def min_teams_greater_than_zero(cls, v): @validator("max_teams") def max_teams_greater_than_min_teams(cls, v, values): - print(values) + #print(values) if values["min_teams"] != None and v < values["min_teams"]: raise ValueError("The maximum number of teams must be greater than or equal to the minimum number of teams") return v diff --git a/backend/api/schemas/matches.py b/backend/api/schemas/matches.py index 9194e3e..1a48789 100644 --- a/backend/api/schemas/matches.py +++ b/backend/api/schemas/matches.py @@ -18,27 +18,13 @@ class Config: orm_mode = True class MatchInput(MatchSchema): - id: int championship_id: int - game_id: int team_1_id: int team_2_id: int - winner_team_id: int bracket: int round: int - resul: str #ver se é possivel criar partida sem resultado inicial - @validator("resul") - def check_empty_fields(cls, v): - assert v != "", "Empty strings are not allowed." - return v - - @validator("resul") - def check_spaced_fields(cls, v): - assert v.strip(), "Empty strings are not allowed." - return v - - @validator("id", "championship_id", "team_1_id", "team_2_id", "winner_team_id", "game_id", "bracket", "round") + @validator("championship_id", "team_1_id", "team_2_id", "bracket", "round") def check_positive_numbers(cls, v): assert v >= 0, "Negative numbers are not allowed." return v @@ -47,18 +33,25 @@ class Config: orm_mode = True class MatchUpdateRequest(BaseModel): - winner_team_id: Optional[int] = Field(default=None, foreign_key="teams.id") #ver se fica a chave estrangeira + winner_team_id: Optional[int] = Field(default=None, foreign_key="teams.id") result: Optional[str] = None - @validator("*") + @validator("result") def check_empty_fields(cls, v): assert v != "", "Empty strings are not allowed." return v - @validator("*") + @validator("result") def check_spaced_fields(cls, v): assert v.strip(), "Empty strings are not allowed." return v - + + class Config: - extra = "forbid" \ No newline at end of file + orm_mode = True + +class Response(GenericModel, Generic[T]): + code: str + status: str + message: str + result: Optional[T] \ No newline at end of file