diff --git a/app/api/api_v1/api.py b/app/api/api_v1/api.py index 93e29a7..4d9a457 100644 --- a/app/api/api_v1/api.py +++ b/app/api/api_v1/api.py @@ -4,9 +4,13 @@ from app.api.api_v1.endpoints import users from app.api.api_v1.endpoints import login from app.api.api_v1.endpoints import player from app.api.api_v1.endpoints import team +from app.api.api_v1.endpoints import match +from app.api.api_v1.endpoints import matchday api_router = APIRouter() api_router.include_router(login.router, tags=["login"]) api_router.include_router(users.router, prefix="/users", tags=["users"]) api_router.include_router(player.router, prefix="/player", tags=["player"]) api_router.include_router(team.router, prefix="/team", tags=["team"]) +api_router.include_router(matchday.router, prefix="/matchday", tags=["matchday"]) +#api_router.include_router(match.router, prefix="/match", tags=["match"]) diff --git a/app/api/api_v1/endpoints/login.py b/app/api/api_v1/endpoints/login.py index 783971d..8765553 100644 --- a/app/api/api_v1/endpoints/login.py +++ b/app/api/api_v1/endpoints/login.py @@ -5,11 +5,14 @@ from fastapi import APIRouter, Body, Depends, HTTPException from fastapi.security import OAuth2PasswordRequestForm from sqlalchemy.orm import Session -from app import crud, models, schemas +from app import crud from app.api import deps from app.core import security from app.core.config import settings from app.core.security import get_password_hash +from app.models.msg import Msg +from app.models.token import Token +from app.models.user import User from app.utils import ( generate_password_reset_token, send_reset_password_email, @@ -19,7 +22,7 @@ from app.utils import ( router = APIRouter() -@router.post("/login/access-token", response_model=schemas.Token) +@router.post("/login/access-token", response_model=Token) def login_access_token( db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends(), @@ -47,9 +50,9 @@ def login_access_token( } -@router.post("/login/test-token", response_model=schemas.User) +@router.post("/login/test-token", response_model=User) def test_token( - current_user: models.User = Depends(deps.get_current_user), + current_user: User = Depends(deps.get_current_user), ) -> Any: """ Test access token @@ -57,7 +60,7 @@ def test_token( return current_user -@router.post("/password-recovery/{email}", response_model=schemas.Msg) +@router.post("/password-recovery/{email}", response_model=Msg) def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any: """ Password Recovery @@ -76,7 +79,7 @@ def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any: return {"msg": "Password recovery email sent"} -@router.post("/reset-password/", response_model=schemas.Msg) +@router.post("/reset-password/", response_model=Msg) def reset_password( token: str = Body(...), new_password: str = Body(...), diff --git a/app/api/api_v1/endpoints/match.py b/app/api/api_v1/endpoints/match.py new file mode 100644 index 0000000..1f43fed --- /dev/null +++ b/app/api/api_v1/endpoints/match.py @@ -0,0 +1,96 @@ +from typing import Any, List + +from fastapi import APIRouter, Body, Depends, HTTPException +from fastapi.encoders import jsonable_encoder +from pydantic.networks import EmailStr +from sqlalchemy.orm import Session + +from app import crud +from app.api import deps + +from app.models.player import Player, PlayerCreate, PlayerUpdate +from app.models.user import User + +router = APIRouter() + + +@router.get("/{id}", response_model=Player) +def read_player( + *, + db: Session = Depends(deps.get_db), + firstname: str, + lastname: str, + current_user: User = Depends(deps.get_current_active_user), +) -> Any: + """ + Get player by firstname and lastname. + """ + player = crud.player.get_player_by_name( + db=db, firstname=firstname, lastname=lastname + ) + if not player: + raise HTTPException(status_code=404, detail="player not found") + if not crud.user.is_superuser(current_user): + raise HTTPException(status_code=400, detail="Not enough permissions") + return player + + +@router.post("/", response_model=List[Player]) +def create_player( + *, + db: Session = Depends(deps.get_db), + players_in: list[PlayerCreate], + current_user: User = Depends(deps.get_current_active_superuser), +) -> Any: + """ + Create new user. + """ + player_out = [] + for player_in in players_in: + player = crud.player.get_player_by_name( + db, firstname=player_in.firstname, lastname=player_in.lastname + ) + if player: + raise HTTPException( + status_code=400, + detail=f"The user with this username already exists in the " + f"system. Player is {player}", + ) + player = crud.player.create(db, obj_in=player_in) + player_out.append(player) + + return player_out + + +@router.get("/", response_model=List[Player]) +def get_players( + db: Session = Depends(deps.get_db), + skip: int = 0, + limit: int = 100, + current_user: User = Depends(deps.get_current_active_superuser), +) -> Any: + """ + Retrieve all players. + """ + player = crud.player.get_multi(db, skip=skip, limit=limit) + return player + + +@router.post("/{id}", response_model=Player) +def update_player( + *, + db: Session = Depends(deps.get_db), + id: int, + player_in: PlayerUpdate, + current_user: User = Depends(deps.get_current_active_user), +) -> Any: + """ + Update player. + """ + + player = crud.player.get(db=db, id=id) + if not player: + raise HTTPException(status_code=404, detail="Player not found") + + player = crud.player.update(db=db, db_obj=player, obj_in=player_in) + return player diff --git a/app/api/api_v1/endpoints/matchday.py b/app/api/api_v1/endpoints/matchday.py new file mode 100644 index 0000000..ce32531 --- /dev/null +++ b/app/api/api_v1/endpoints/matchday.py @@ -0,0 +1,114 @@ +from typing import Any, List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session + +from app import crud +from app.api import deps + +from app.models.matchday import Matchday, MatchdayCreate, MatchdayUpdate, \ + MatchdayWithPlayers +from app.models.user import User + +router = APIRouter() + + +@router.get("/{matchday_id}", response_model=MatchdayWithPlayers) +def read_matchday( + *, + db: Session = Depends(deps.get_db), + matchday_id: int, + current_user: User = Depends(deps.get_current_active_user), +) -> Any: + """ + Get matchday by firstname and lastname. + """ + matchday = crud.matchday.get( + db=db, id=matchday_id + ) + if not matchday: + raise HTTPException(status_code=404, detail="matchday not found") + if not crud.user.is_superuser(current_user): + raise HTTPException(status_code=400, detail="Not enough permissions") + return matchday + + +@router.post("/", response_model=Matchday) +def create_matchday( + *, + db: Session = Depends(deps.get_db), + matchday_in: MatchdayCreate, + current_user: User = Depends(deps.get_current_active_superuser), +) -> Any: + """ + Create new user. + """ + matchday = crud.matchday.get_unique_day(db, obj_in=matchday_in) + if matchday: + raise HTTPException( + status_code=400, + detail=f"The matchday with this day already exists in the " + f"system. Matchday is {matchday}", + ) + matchday = crud.matchday.create(db, obj_in=matchday_in) + + return matchday + + +@router.get("/", response_model=List[MatchdayWithPlayers]) +def get_matchdays( + db: Session = Depends(deps.get_db), + skip: int = 0, + limit: int = 100, + current_user: User = Depends(deps.get_current_active_superuser), +) -> Any: + """ + Retrieve all matchdays. + """ + matchday = crud.matchday.get_multi(db, skip=skip, limit=limit) + return matchday + + +@router.post("/{matchday_id}", response_model=Matchday) +def update_matchday( + *, + db: Session = Depends(deps.get_db), + matchday_id: int, + matchday_in: MatchdayUpdate, + current_user: User = Depends(deps.get_current_active_user), +) -> Any: + """ + Update matchday. + """ + + matchday = crud.matchday.get(db=db, id=matchday_id) + if not matchday: + raise HTTPException(status_code=404, detail="Matchday not found") + + matchday = crud.matchday.update(db=db, db_obj=matchday, obj_in=matchday_in) + return matchday + + +@router.put("/players/{matchday_id}", response_model=Matchday) +def add_player_matchday( + *, + db: Session = Depends(deps.get_db), + player_id: int, + matchday_id: int, + current_user: User = Depends(deps.get_current_active_superuser), +) -> Any: + """ + Add player to matchday. + """ + if crud.player.get(db, id=player_id) is None: + raise HTTPException(status_code=404, detail="Player not found") + if crud.matchday.get(db=db, id=matchday_id) is None: + raise HTTPException(status_code=404, detail="Matchday not found") + if crud.matchday.is_player_in_matchday( + db=db, player_id=player_id, matchday_id=matchday_id + ): + raise HTTPException( + status_code=404, detail="Player is already in team" + ) + matchday = crud.matchday.add_player_in_matchday(db, matchday_id, player_id) + return matchday diff --git a/app/api/api_v1/endpoints/player.py b/app/api/api_v1/endpoints/player.py index 3a53ea5..0078c02 100644 --- a/app/api/api_v1/endpoints/player.py +++ b/app/api/api_v1/endpoints/player.py @@ -1,31 +1,30 @@ from typing import Any, List -from fastapi import APIRouter, Body, Depends, HTTPException -from fastapi.encoders import jsonable_encoder -from pydantic.networks import EmailStr +from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session -from app import crud, models, schemas +from app import crud from app.api import deps -from app.core.config import settings -from app.schemas.player import PlayerUpdate + +from app.models.player import Player, PlayerCreate, PlayerUpdate, \ + PlayerTeamsMatchdays +from app.models.user import User router = APIRouter() -@router.get("/{id}", response_model=schemas.Player) +@router.get("/{player_id}", response_model=PlayerTeamsMatchdays) def read_player( *, db: Session = Depends(deps.get_db), - firstname: str, - lastname: str, - current_user: models.User = Depends(deps.get_current_active_user), + player_id: int, + current_user: User = Depends(deps.get_current_active_user), ) -> Any: """ - Get player by firstname and lastname. + Get player by id. """ - player = crud.player.get_player_by_name( - db=db, firstname=firstname, lastname=lastname + player = crud.player.get( + db=db, id=player_id ) if not player: raise HTTPException(status_code=404, detail="player not found") @@ -34,56 +33,60 @@ def read_player( return player -@router.post("/", response_model=schemas.Player) +@router.post("/", response_model=List[Player]) def create_player( *, db: Session = Depends(deps.get_db), - player_in: schemas.PlayerCreate, - current_user: models.User = Depends(deps.get_current_active_superuser), + players_in: list[PlayerCreate], + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Create new user. """ - player = crud.player.get_player_by_name( - db, firstname=player_in.firstname, lastname=player_in.lastname - ) - if player: - raise HTTPException( - status_code=400, - detail="The user with this username already exists in the system.", + player_out = [] + for player_in in players_in: + player = crud.player.get_player_by_name( + db, firstname=player_in.firstname, lastname=player_in.lastname ) - player = crud.player.create(db, obj_in=player_in) + if player: + raise HTTPException( + status_code=400, + detail=f"The user with this username already exists in the " + f"system. Player is {player}", + ) + player = crud.player.create(db, obj_in=player_in) + player_out.append(player) - return player + return player_out -@router.get("/", response_model=List[schemas.Player]) +@router.get("/", response_model=List[PlayerTeamsMatchdays]) def get_players( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, - current_user: models.User = Depends(deps.get_current_active_superuser), + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Retrieve all players. """ - player = crud.player.get_players(db, skip=skip, limit=limit) + player = crud.player.get_multi(db, skip=skip, limit=limit) return player -@router.post("/{id}", response_model=schemas.Player) +@router.post("/{player_id}", response_model=Player) def update_player( *, db: Session = Depends(deps.get_db), - id: int, + player_id: int, player_in: PlayerUpdate, - current_user: models.User = Depends(deps.get_current_active_user), + current_user: User = Depends(deps.get_current_active_user), ) -> Any: """ Update player. """ - player = crud.player.get(db=db, id=id) + player = crud.player.get(db=db, id=player_id) if not player: raise HTTPException(status_code=404, detail="Player not found") diff --git a/app/api/api_v1/endpoints/team.py b/app/api/api_v1/endpoints/team.py index 0d87c92..2650111 100644 --- a/app/api/api_v1/endpoints/team.py +++ b/app/api/api_v1/endpoints/team.py @@ -4,24 +4,25 @@ from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session -from app import crud, models, schemas +from app import crud from app.api import deps - +from app.models.team import Team, TeamCreate, TeamWithPlayers +from app.models.user import User router = APIRouter() -@router.get("/{id}", response_model=schemas.Team) +@router.get("/{team_id}", response_model=Team) def get_team( *, db: Session = Depends(deps.get_db), - id: int, - current_user: models.User = Depends(deps.get_current_active_user), + team_id: int, + current_user: User = Depends(deps.get_current_active_user), ) -> Any: """ Get team by id. """ - team = crud.team.get_team(db=db, team_id=id) + team = crud.team.get_team(db=db, team_id=team_id) if not team: raise HTTPException(status_code=404, detail="player not found") if not crud.user.is_superuser(current_user): @@ -29,26 +30,26 @@ def get_team( return team -@router.get("/", response_model=List[schemas.Team]) +@router.get("/", response_model=List[TeamWithPlayers]) def get_teams( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, - current_user: models.User = Depends(deps.get_current_active_superuser), + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Retrieve teams. """ - team = crud.team.get_teams(db, skip=skip, limit=limit) + team = crud.team.get_multi( db, skip=skip, limit=limit) return team -@router.post("/", response_model=schemas.Team) +@router.post("/", response_model=Team) def create_team( *, db: Session = Depends(deps.get_db), - team_in: schemas.TeamCreate, - current_user: models.User = Depends(deps.get_current_active_superuser), + team_in: TeamCreate, + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Create team. @@ -59,20 +60,20 @@ def create_team( return team -@router.put("/players/{team_id}", response_model=schemas.Team) +@router.put("/players/{team_id}", response_model=Team) def add_player_team( *, db: Session = Depends(deps.get_db), player_id: int, team_id: int, - current_user: models.User = Depends(deps.get_current_active_superuser), + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Add player to team. """ - if crud.player.get_player(db, player_id=player_id) is None: + if crud.player.get(db, id=player_id) is None: raise HTTPException(status_code=404, detail="Player not found") - if crud.team.get_team(db=db, team_id=team_id) is None: + if crud.team.get(db=db, id=team_id) is None: raise HTTPException(status_code=404, detail="Team not found") if crud.team.is_player_in_team( db=db, player_id=player_id, team_id=team_id diff --git a/app/api/api_v1/endpoints/users.py b/app/api/api_v1/endpoints/users.py index ef5e9a0..6e4c92d 100644 --- a/app/api/api_v1/endpoints/users.py +++ b/app/api/api_v1/endpoints/users.py @@ -5,20 +5,20 @@ from fastapi.encoders import jsonable_encoder from pydantic.networks import EmailStr from sqlalchemy.orm import Session -from app import crud, models, schemas +from app import crud, models from app.api import deps from app.core.config import settings - +from app.models.user import UserUpdate, User, UserCreate router = APIRouter() -@router.get("/", response_model=List[schemas.User]) +@router.get("/", response_model=List[User]) def read_users( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, - current_user: models.User = Depends(deps.get_current_active_superuser), + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Retrieve users. @@ -27,12 +27,12 @@ def read_users( return users -@router.post("/", response_model=schemas.User) +@router.post("/", response_model=User) def create_user( *, db: Session = Depends(deps.get_db), - user_in: schemas.UserCreate, - current_user: models.User = Depends(deps.get_current_active_superuser), + user_in: UserCreate, + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Create new user. @@ -48,20 +48,20 @@ def create_user( return user -@router.put("/me", response_model=schemas.User) +@router.put("/me", response_model=User) def update_user_me( *, db: Session = Depends(deps.get_db), password: str = Body(None), full_name: str = Body(None), email: EmailStr = Body(None), - current_user: models.User = Depends(deps.get_current_active_user), + current_user: User = Depends(deps.get_current_active_user), ) -> Any: """ Update own user. """ current_user_data = jsonable_encoder(current_user) - user_in = schemas.UserUpdate(**current_user_data) + user_in = UserUpdate(**current_user_data) if password is not None: user_in.password = password if full_name is not None: @@ -72,10 +72,10 @@ def update_user_me( return user -@router.get("/me", response_model=schemas.User) +@router.get("/me", response_model=User) def read_user_me( db: Session = Depends(deps.get_db), - current_user: models.User = Depends(deps.get_current_active_user), + current_user: User = Depends(deps.get_current_active_user), ) -> Any: """ Get current user. @@ -83,7 +83,7 @@ def read_user_me( return current_user -@router.post("/open", response_model=schemas.User) +@router.post("/open", response_model=User) def create_user_open( *, db: Session = Depends(deps.get_db), @@ -105,17 +105,17 @@ def create_user_open( status_code=400, detail="The user with this username already exists in the system", ) - user_in = schemas.UserCreate( + user_in = UserCreate( password=password, email=email, full_name=full_name ) user = crud.user.create(db, obj_in=user_in) return user -@router.get("/{user_id}", response_model=schemas.User) +@router.get("/{user_id}", response_model=User) def read_user_by_id( user_id: int, - current_user: models.User = Depends(deps.get_current_active_user), + current_user: User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), ) -> Any: """ @@ -131,13 +131,13 @@ def read_user_by_id( return user -@router.put("/{user_id}", response_model=schemas.User) +@router.put("/{user_id}", response_model=User) def update_user( *, db: Session = Depends(deps.get_db), user_id: int, - user_in: schemas.UserUpdate, - current_user: models.User = Depends(deps.get_current_active_superuser), + user_in: UserUpdate, + current_user: User = Depends(deps.get_current_active_superuser), ) -> Any: """ Update a user. diff --git a/app/api/deps.py b/app/api/deps.py index 304650e..59d2b78 100644 --- a/app/api/deps.py +++ b/app/api/deps.py @@ -6,7 +6,9 @@ from jose import jwt from pydantic import ValidationError from sqlalchemy.orm import Session -from app import crud, models, schemas +from app import crud +from app.models.token import TokenPayload +from app.models.user import User from app.core import security from app.core.config import settings from app.db.session import SessionLocal @@ -26,12 +28,12 @@ def get_db() -> Generator: def get_current_user( db: Session = Depends(get_db), token: str = Depends(reusable_oauth2) -) -> models.User: +) -> User: try: payload = jwt.decode( token, settings.SECRET_KEY, algorithms=[security.ALGORITHM] ) - token_data = schemas.TokenPayload(**payload) + token_data = TokenPayload(**payload) except (jwt.JWTError, ValidationError): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, @@ -44,16 +46,17 @@ def get_current_user( def get_current_active_user( - current_user: models.User = Depends(get_current_user), -) -> models.User: + current_user: User = Depends(get_current_user), +) -> User: if not crud.user.is_active(current_user): raise HTTPException(status_code=400, detail="Inactive user") return current_user def get_current_active_superuser( - current_user: models.User = Depends(get_current_user), -) -> models.User: + current_user: User = Depends(get_current_user), +) -> User: + if not crud.user.is_superuser(current_user): raise HTTPException( status_code=400, detail="The user doesn't have enough privileges" diff --git a/app/crud/__init__.py b/app/crud/__init__.py index fa14f0f..7275570 100644 --- a/app/crud/__init__.py +++ b/app/crud/__init__.py @@ -1,3 +1,4 @@ from .crud_user import user from .crud_player import player from .crud_team import team +from .crud_matchday import matchday diff --git a/app/crud/base.py b/app/crud/base.py index aff4e9c..83ef4a0 100644 --- a/app/crud/base.py +++ b/app/crud/base.py @@ -3,6 +3,7 @@ from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union from fastapi.encoders import jsonable_encoder from pydantic import BaseModel from sqlalchemy.orm import Session +from sqlmodel import select from app.db.base_class import Base @@ -22,12 +23,14 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): self.model = model def get(self, db: Session, id: Any) -> Optional[ModelType]: - return db.query(self.model).filter(self.model.id == id).first() + statement = select(self.model).where(self.model.id == id) + return db.execute(statement).scalar_one_or_none() def get_multi( self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[ModelType]: - return db.query(self.model).offset(skip).limit(limit).all() + statement = select(self.model).offset(skip).limit(limit) + return db.execute(statement).scalars().all() def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType: obj_in_data = jsonable_encoder(obj_in) @@ -58,7 +61,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): return db_obj def remove(self, db: Session, *, id: int) -> ModelType: - obj = db.query(self.model).get(id) + obj = db.execute(self.model).get(id) db.delete(obj) db.commit() return obj diff --git a/app/crud/crud_matchday.py b/app/crud/crud_matchday.py new file mode 100644 index 0000000..5ae875e --- /dev/null +++ b/app/crud/crud_matchday.py @@ -0,0 +1,45 @@ +from typing import Any, Dict, Union, Optional + +from sqlmodel import select +from sqlalchemy.orm import Session + +from app.crud import player as crud_player +from app.crud.base import CRUDBase +from app.models.matchday import Matchday, MatchdayCreate, MatchdayUpdate + + +class CRUDMatchday(CRUDBase[Matchday, MatchdayCreate, MatchdayUpdate]): + + def create(self, db: Session, *, obj_in: MatchdayCreate) -> Matchday: + db_obj = Matchday( + day=obj_in.day) + + db.add(db_obj) + db.commit() + db.refresh(db_obj) + return db_obj + + def get_unique_day(self, db: Session, *, obj_in: Matchday) -> Any: + day = obj_in.day + statement = select(Matchday).where(Matchday.day == day) + result = db.execute(statement).all() + return result + + def add_player_in_matchday(self, db: Session, matchday_id: int, player_id: int + ) -> Matchday: + matchday_in = self.get(db=db, id=matchday_id) + db_player = crud_player.get(db=db, id=player_id) + matchday_in.players.append(db_player) + db.commit() + return matchday_in + + def is_player_in_matchday( + self, db: Session, *, player_id: int, matchday_id: int + ) -> bool: + matchday = self.get(db=db, id=matchday_id) + if matchday is None: + return False + db_player = crud_player.get(db=db, id=player_id) + return db_player in matchday.players + +matchday = CRUDMatchday(Matchday) \ No newline at end of file diff --git a/app/crud/crud_player.py b/app/crud/crud_player.py index a098b2e..5a60cef 100644 --- a/app/crud/crud_player.py +++ b/app/crud/crud_player.py @@ -3,8 +3,7 @@ from typing import Any, Dict, Optional, Union from sqlalchemy.orm import Session from app.crud.base import CRUDBase -from app.models.player import Player -from app.schemas.player import PlayerCreate, PlayerUpdate, PlayerUpdateTeam +from app.models.player import Player, PlayerCreate, PlayerUpdate class CRUDPlayer(CRUDBase[Player, PlayerCreate, PlayerUpdate]): @@ -21,12 +20,6 @@ class CRUDPlayer(CRUDBase[Player, PlayerCreate, PlayerUpdate]): .first() ) - def get_player(self, db: Session, *, player_id: int) -> Optional[Player]: - return db.query(Player).filter(Player.id == player_id).first() - - def get_players(self, db: Session, skip: int = 0, limit: int = 100): - return db.query(Player).offset(skip).limit(limit).all() - def create(self, db: Session, *, obj_in: PlayerCreate) -> Player: db_obj = Player( firstname=obj_in.firstname, @@ -38,4 +31,5 @@ class CRUDPlayer(CRUDBase[Player, PlayerCreate, PlayerUpdate]): return db_obj + player = CRUDPlayer(Player) diff --git a/app/crud/crud_team.py b/app/crud/crud_team.py index cf5c0ba..6e7162c 100644 --- a/app/crud/crud_team.py +++ b/app/crud/crud_team.py @@ -1,22 +1,14 @@ from typing import Any, Dict, Optional, Union, List from sqlalchemy.orm import Session +from sqlmodel import select from app.crud.base import CRUDBase from app.crud import player as crud_player -from app.models.team import Team - -from app.schemas.team import TeamCreate, TeamUpdate +from app.models.team import Team, TeamCreate, TeamUpdate class CRUDTeam(CRUDBase[Team, TeamCreate, TeamUpdate]): - def get_team(self, db: Session, *, team_id: int) -> Optional[Team]: - return db.query(Team).filter(Team.team_id == team_id).first() - - def get_teams( - self, db: Session, skip: int = 0, limit: int = 100 - ) -> List[Team]: - return db.query(Team).offset(skip).limit(limit).all() def create(self, db: Session, *, obj_in: TeamCreate) -> Team: db_obj = Team(teamname=obj_in.teamname) @@ -28,19 +20,19 @@ class CRUDTeam(CRUDBase[Team, TeamCreate, TeamUpdate]): def add_player_in_team( self, db: Session, team_id: int, player_id: int ) -> Team: - team = self.get_team(db=db, team_id=team_id) - db_player = crud_player.get_player(db=db, player_id=player_id) - team.players.append(db_player) + team_in = self.get(db=db, id=team_id) + db_player = crud_player.get(db=db, id=player_id) + team_in.players.append(db_player) db.commit() - return team + return team_in def is_player_in_team( self, db: Session, *, player_id: int, team_id: int ) -> bool: - team = self.get_team(db=db, team_id=team_id) + team = self.get(db=db, id=team_id) if team is None: return False - db_player = crud_player.get_player(db=db, player_id=player_id) + db_player = crud_player.get(db=db, id=player_id) return db_player in team.players diff --git a/app/crud/crud_user.py b/app/crud/crud_user.py index 0e99461..514182c 100644 --- a/app/crud/crud_user.py +++ b/app/crud/crud_user.py @@ -4,8 +4,8 @@ from sqlalchemy.orm import Session from app.core.security import get_password_hash, verify_password from app.crud.base import CRUDBase -from app.models.user import User -from app.schemas.user import UserCreate, UserUpdate +from app.models.user import User, UserCreate, UserUpdate + class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): diff --git a/app/db/base_class.py b/app/db/base_class.py index 29f272d..e78f140 100644 --- a/app/db/base_class.py +++ b/app/db/base_class.py @@ -1,13 +1,12 @@ -from typing import Any +from typing import Optional -from sqlalchemy.ext.declarative import as_declarative, declared_attr +from sqlalchemy.ext.declarative import declared_attr +from sqlmodel import Field, SQLModel -@as_declarative() -class Base: - id: Any +class Base(SQLModel): + id: Optional[int] = Field(default=None, primary_key=True, index=True) __name__: str # Generate __tablename__ automatically - @declared_attr - def __tablename__(cls) -> str: - return cls.__name__.lower() + + diff --git a/app/db/init_db.py b/app/db/init_db.py index 5d553e7..78d8445 100644 --- a/app/db/init_db.py +++ b/app/db/init_db.py @@ -1,6 +1,6 @@ from sqlalchemy.orm import Session -from app import crud, schemas +from app import crud from app.core.config import settings from app.db import base # noqa: F401 @@ -9,6 +9,9 @@ from app.db import base # noqa: F401 # for more details: https://github.com/tiangolo/full-stack-fastapi-postgresql/issues/28 from app.db.base_class import Base from app.db.session import engine +from app.models.player import Player, PlayerCreate +from app.models.team import TeamCreate, TeamWithPlayers +from app.models.user import UserCreate def init_db(db: Session) -> None: @@ -19,9 +22,34 @@ def init_db(db: Session) -> None: user = crud.user.get_by_email(db, email=settings.FIRST_SUPERUSER) if not user: - user_in = schemas.UserCreate( + user_in = UserCreate( email=settings.FIRST_SUPERUSER, password=settings.FIRST_SUPERUSER_PASSWORD, is_superuser=True, ) user = crud.user.create(db, obj_in=user_in) # noqa: F841 + + players = crud.player.get_multi(db=db) + + if not players: + + player_in = PlayerCreate( + firstname="Simon", + lastname="Milvert", + ) + + player = crud.player.create(db=db, obj_in=player_in) # noqa: F841 + player_in = PlayerCreate( + firstname="Johan", + lastname="Moden") + + + player = crud.player.create(db=db, obj_in=player_in) # noqa: F841 + + teams = crud.team.get_multi(db=db) + + if not True: + print("Create Team") + player = crud.player.get(db, id=1) + team_in = TeamWithPlayers(teamname='1', players=[player]) + team = crud.team.create(db, obj_in=team_in) # noqa: F841 \ No newline at end of file diff --git a/app/db/session.py b/app/db/session.py index 0c38a58..1d434f2 100644 --- a/app/db/session.py +++ b/app/db/session.py @@ -1,7 +1,13 @@ -from sqlalchemy import create_engine + from sqlalchemy.orm import sessionmaker from app.core.config import settings +from sqlmodel import create_engine, SQLModel + engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True) +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/app/main.py b/app/main.py index ce938c1..3a1b06d 100644 --- a/app/main.py +++ b/app/main.py @@ -1,12 +1,18 @@ +import logging + +import uvicorn from fastapi import FastAPI +from loguru import logger from starlette.middleware.cors import CORSMiddleware from app.api.api_v1.api import api_router from app.core.config import settings from app.db.init_db import init_db -from app.db.session import SessionLocal +from app.db.session import SessionLocal, create_db_and_tables +from app.utils import configure_log_handler db = SessionLocal() +configure_log_handler(log_level=logging.DEBUG) init_db(db) @@ -14,6 +20,10 @@ app = FastAPI( title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json", ) +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + # Set all CORS enabled origins if settings.BACKEND_CORS_ORIGINS: @@ -28,3 +38,4 @@ if settings.BACKEND_CORS_ORIGINS: ) app.include_router(api_router, prefix=settings.API_V1_STR) + diff --git a/app/models/__init__.py b/app/models/__init__.py index a7cfcae..1631f42 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -1,5 +1,13 @@ -# from .match import Match -# from .matchday import Matchday -from .player import Player -from .team import Team -from .user import User +from app.models.matchday import Matchday, MatchdayWithPlayers +from app.models.player import Player, PlayerTeamsMatchdays +from app.models.team import Team, TeamWithPlayers + + +Player.update_forward_refs(Team=Team, Matchday=Matchday) +PlayerTeamsMatchdays.update_forward_refs(Team=Team, Matchday=Matchday) + +Team.update_forward_refs(Player=Player) +TeamWithPlayers.update_forward_refs(Player=Player) + +Matchday.update_forward_refs(Player=Player) +MatchdayWithPlayers.update_forward_refs(Player=Player) \ No newline at end of file diff --git a/app/models/match.py b/app/models/match.py index 8474189..9f014f5 100644 --- a/app/models/match.py +++ b/app/models/match.py @@ -1,11 +1,14 @@ -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.orm import relationship +from typing import Optional -from app.db.database import Base +from sqlmodel import SQLModel, Field + +from app.db.base_class import Base -class Match(Base): +class Match(SQLModel, Base, table=True): + matchname: Optional[str] = Field(nullable=False) +""" match_id = Column(Integer, primary_key=True) team_1 = Column(ForeignKey("team.id"), nullable=False) team_2 = Column(ForeignKey("team.id"), nullable=False) @@ -18,3 +21,4 @@ class Match(Base): team = relationship("Team", primaryjoin="Match.team_1 == Team.team_id") team1 = relationship("Team", primaryjoin="Match.team_2 == Team.team_id") team2 = relationship("Team", primaryjoin="Match.winner == Team.team_id") +""" \ No newline at end of file diff --git a/app/models/matchday.py b/app/models/matchday.py index d43da4a..46fce98 100644 --- a/app/models/matchday.py +++ b/app/models/matchday.py @@ -1,17 +1,36 @@ -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.orm import relationship +from datetime import date, datetime +from typing import List, Optional, TYPE_CHECKING + +from sqlalchemy import Date +from sqlmodel import SQLModel, Relationship, Field, Column, func from app.db.base_class import Base +from app.models.matchdayplayer import MatchdayPlayerLink + +if TYPE_CHECKING: + from app.models.player import Player -class MatchdayPlayer(Base): - - player_id = Column("players_id", ForeignKey("players.id"), primary_key=True) - matchday_id = Column("teams_id", ForeignKey("teams.id"), primary_key=True) +class MatchdayBase(SQLModel): + day: date -class Matchday(Base): +class Matchday(MatchdayBase, Base, table=True): + day: date = Field( + default=None, + sa_column=Column(Date, server_default=func.now())) + players: List["Player"] = Relationship(back_populates="matchdays", + link_model=MatchdayPlayerLink) - matchday_id = Column(Integer, primary_key=True) - day = Column(DateTime, nullable=False) - players = relationship("Player", secondary="matchdayplayer") \ No newline at end of file + +class MatchdayCreate(MatchdayBase): + day: date + + +class MatchdayUpdate(MatchdayBase): + day: date + + +class MatchdayWithPlayers(MatchdayBase): + day: date + players: Optional[List["Player"]] diff --git a/app/models/matchdayplayer.py b/app/models/matchdayplayer.py new file mode 100644 index 0000000..876fbfa --- /dev/null +++ b/app/models/matchdayplayer.py @@ -0,0 +1,11 @@ +from typing import Optional +from sqlmodel import SQLModel, Field + +class MatchdayPlayerLink(SQLModel, table=True): + + player_id: Optional[int] = Field( + default=None, foreign_key="player.id", primary_key=True + ) + matchday_id: Optional[int] = Field( + default=None, foreign_key="matchday.id", primary_key=True + ) diff --git a/app/schemas/msg.py b/app/models/msg.py similarity index 100% rename from app/schemas/msg.py rename to app/models/msg.py diff --git a/app/models/player.py b/app/models/player.py index 1e9b3e7..8964612 100644 --- a/app/models/player.py +++ b/app/models/player.py @@ -1,16 +1,35 @@ -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.orm import relationship +from typing import Optional, List, TYPE_CHECKING +from sqlmodel import SQLModel, Field, Relationship from app.db.base_class import Base +from app.models.matchdayplayer import MatchdayPlayerLink +from app.models.teamplayers import TeamPlayerLink -class Player(Base): +if TYPE_CHECKING: + from app.models import Team, Matchday +class PlayerBase(SQLModel): + firstname: str = Field(nullable=False) + lastname: str = Field(nullable=False) - id = Column(Integer, primary_key=True, index=True) - firstname = Column(String, nullable=True) - lastname = Column(String, nullable=True) - team_id = Column(Integer, ForeignKey("team.team_id")) - team = relationship("Team", back_populates="players") - team_id = Column(Integer, ForeignKey("team.team_id")) - team = relationship("Team", back_populates="players") +class Player(PlayerBase, Base, table=True): + teams: List["Team"] = Relationship(back_populates="players", + link_model=TeamPlayerLink) + matchdays: List["Matchday"] = Relationship(back_populates="players", + link_model=MatchdayPlayerLink) + + +class PlayerCreate(PlayerBase): + firstname: str + lastname: str + + +class PlayerUpdate(PlayerBase): + firstname: str + lastname: str + +class PlayerTeamsMatchdays(PlayerBase): + matchdays: List["Matchday"] + teams: List["Team"] + diff --git a/app/models/team.py b/app/models/team.py index b925a5c..14d53cd 100644 --- a/app/models/team.py +++ b/app/models/team.py @@ -1,11 +1,34 @@ -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.orm import relationship +from typing import Optional, List, TYPE_CHECKING + +from sqlmodel import SQLModel, Field, Relationship from app.db.base_class import Base +from app.models.teamplayers import TeamPlayerLink + +if TYPE_CHECKING: + from app.models.player import Player + +class TeamBase(SQLModel): + teamname: Optional[str] = Field(nullable=False) + + +class Team(TeamBase, Base, table=True): + players: List["Player"] = Relationship(back_populates="teams", + link_model=TeamPlayerLink + ) + + +class TeamCreate(TeamBase): + teamname: str + + +class TeamWithPlayers(TeamBase): + teamname: str + players: List["Player"] + + +class TeamUpdate(TeamBase): + teamname: str -class Team(Base): - team_id = Column(Integer, primary_key=True, index=True) - teamname = Column(String, nullable=False) - players = relationship("Player", back_populates="team") diff --git a/app/models/teamplayers.py b/app/models/teamplayers.py new file mode 100644 index 0000000..575bb74 --- /dev/null +++ b/app/models/teamplayers.py @@ -0,0 +1,12 @@ +from typing import Optional + +from sqlmodel import SQLModel, Field, Relationship + + +class TeamPlayerLink(SQLModel, table=True): + team_id: Optional[int] = Field( + default=None, foreign_key="team.id", primary_key=True + ) + player_id: Optional[int] = Field( + default=None, foreign_key="player.id", primary_key=True + ) diff --git a/app/schemas/token.py b/app/models/token.py similarity index 93% rename from app/schemas/token.py rename to app/models/token.py index c7fd517..980924a 100644 --- a/app/schemas/token.py +++ b/app/models/token.py @@ -1,8 +1,6 @@ from typing import Optional - from pydantic import BaseModel - class Token(BaseModel): access_token: str token_type: str diff --git a/app/models/user.py b/app/models/user.py index 5c42cbf..6f5b089 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -1,12 +1,29 @@ -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Boolean +from typing import Optional +from pydantic import EmailStr +from sqlmodel import SQLModel, Field from app.db.base_class import Base -class User(Base): - id = Column(Integer, primary_key=True, index=True) - full_name = Column(String, index=True) - email = Column(String, unique=True, index=True, nullable=False) - hashed_password = Column(String, nullable=False) - is_active = Column(Boolean(), default=True) - is_superuser = Column(Boolean(), default=False) +class UserBase(SQLModel): + + full_name: Optional[str] = Field(index=True) + email: Optional[EmailStr] = Field(unique=True, index=True, nullable=False) + hashed_password: Optional[str] = Field(nullable=False) + is_active: Optional[bool] = Field(default=True) + is_superuser: bool = Field(default=False) + + +class User(UserBase, Base, table=True): + pass + + +class UserCreate(UserBase): + email: EmailStr + password: str + + +# Properties to receive via API on update +class UserUpdate(UserBase): + password: Optional[str] = None + diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py index e69031b..3bd4c33 100644 --- a/app/schemas/__init__.py +++ b/app/schemas/__init__.py @@ -1,5 +1,3 @@ -from .user import User, UserCreate, UserInDB, UserUpdate -from .token import Token, TokenPayload -from .msg import Msg -from .player import Player, PlayerCreate, PlayerInDB -from .team import Team, TeamCreate, TeamInDBBase +#from .user import User, UserCreate, UserInDB, UserUpdate +#from .player import Player, PlayerCreate, PlayerInDB +#from .team import Team, TeamCreate, TeamInDBBase diff --git a/app/schemas/player.py b/app/schemas/player.py deleted file mode 100644 index e6a0f16..0000000 --- a/app/schemas/player.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel, EmailStr - - -# Shared properties -class PlayerBase(BaseModel): - firstname: Optional[str] = None - lastname: Optional[str] = None - - -# Properties to receive via API on creation -class PlayerCreate(PlayerBase): - firstname: str - lastname: str - - -class PlayerInDBBase(PlayerBase): - id: Optional[int] = None - - class Config: - orm_mode = True - - -class PlayerUpdate(PlayerBase): - firstname: str - lastname: str - - -class PlayerUpdateTeam(PlayerBase): - team_id: Optional[int] - - -# Additional properties to return via API -class Player(PlayerInDBBase): - pass - - -# Additional properties stored in DB -class PlayerInDB(PlayerInDBBase): - pass diff --git a/app/schemas/team.py b/app/schemas/team.py deleted file mode 100644 index bf7abb7..0000000 --- a/app/schemas/team.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel - - -# Shared properties -from app.schemas import Player - - -class TeamBase(BaseModel): - teamname: Optional[str] = None - - -# Properties to receive via API on creation -class TeamCreate(TeamBase): - teamname: Optional[str] - - -class TeamInDBBase(TeamBase): - players: list[Player] = [] - - class Config: - orm_mode = True - - -class TeamUpdate(TeamBase): - pass - - -# Additional properties to return via API -class Team(TeamInDBBase): - - team_id: Optional[int] = None - players: list[Player] = [] - - -# Additional properties stored in DB -class TeamInDB(TeamInDBBase): - pass diff --git a/app/schemas/user.py b/app/schemas/user.py deleted file mode 100644 index aa18316..0000000 --- a/app/schemas/user.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel, EmailStr - - -# Shared properties -class UserBase(BaseModel): - email: Optional[EmailStr] = None - is_active: Optional[bool] = True - is_superuser: bool = False - full_name: Optional[str] = None - - -# Properties to receive via API on creation -class UserCreate(UserBase): - email: EmailStr - password: str - - -# Properties to receive via API on update -class UserUpdate(UserBase): - password: Optional[str] = None - - -class UserInDBBase(UserBase): - id: Optional[int] = None - - class Config: - orm_mode = True - - -# Additional properties to return via API -class User(UserInDBBase): - pass - - -# Additional properties stored in DB -class UserInDB(UserInDBBase): - hashed_password: str diff --git a/app/utils.py b/app/utils.py index 723e9d8..9577423 100644 --- a/app/utils.py +++ b/app/utils.py @@ -8,6 +8,44 @@ from emails.template import JinjaTemplate from jose import jwt from app.core.config import settings +import sys +import logging +from loguru import logger + +logger.remove() +logger.add( + sys.stdout, + format="{time:YYYY/MM/DD HH:mm:ss} {level: <5} {name} {message}", + level="DEBUG", +) + + +class InterceptHandler(logging.Handler): + def emit(self, record): + # Get corresponding Loguru level if it exists. + try: + level = logger.level(record.levelname).name + except ValueError: + level = record.levelno + + # Find caller from where originated the logged message. + frame, depth = sys._getframe(6), 6 + while frame and frame.f_code.co_filename == logging.__file__: + frame = frame.f_back + depth += 1 + + logger.opt( + depth=depth, exception=record.exc_info + ).log( + "DEBUG", record.getMessage() + ) + + +def configure_log_handler(*, log_level=logging.WARNING): + logging.basicConfig(handlers=[InterceptHandler()], + level=logging.NOTSET, force=True) + for target in ['sqlalchemy.engine.Engine', 'sqlalchemy.engine']: + logging.getLogger(target).setLevel(log_level) def send_email( diff --git a/debug_run.py b/debug_run.py new file mode 100644 index 0000000..c68ab44 --- /dev/null +++ b/debug_run.py @@ -0,0 +1,13 @@ +import uvicorn +from loguru import logger + +from app.main import app + + +def main(): + uvicorn.run("debug_run:app", host="0.0.0.0", port=1234, reload=True) + +if __name__ == "__main__": + logger.info("begin") + main() + logger.info("done") diff --git a/floorball.db b/floorball.db index 929cf18..66e9dff 100644 Binary files a/floorball.db and b/floorball.db differ diff --git a/poetry.lock b/poetry.lock index f18f0c8..9720289 100644 --- a/poetry.lock +++ b/poetry.lock @@ -383,6 +383,21 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "loguru" +version = "0.6.0" +description = "Python logging made (stupidly) simple" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"] + [[package]] name = "lxml" version = "4.9.1" @@ -754,6 +769,30 @@ postgresql_psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] sqlcipher = ["sqlcipher3-binary"] +[[package]] +name = "sqlalchemy2-stubs" +version = "0.0.2a27" +description = "Typing Stubs for SQLAlchemy 1.4" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=3.7.4" + +[[package]] +name = "sqlmodel" +version = "0.0.8" +description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." +category = "main" +optional = false +python-versions = ">=3.6.1,<4.0.0" + +[package.dependencies] +pydantic = ">=1.8.2,<2.0.0" +SQLAlchemy = ">=1.4.17,<=1.4.41" +sqlalchemy2-stubs = "*" + [[package]] name = "starlette" version = "0.19.1" @@ -868,6 +907,17 @@ platformdirs = ">=2.4,<3" docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + [[package]] name = "wrapt" version = "1.14.1" @@ -879,7 +929,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "875e82447867f92eb96260ad048da2e4967d2c8285ecaac9dca37dd839d7d78f" +content-hash = "f51b93ad173b06640d39b5143150dd0219b20bfcd5c3c9a0a4036b5638d12925" [metadata.files] anyio = [ @@ -1257,6 +1307,10 @@ lazy-object-proxy = [ {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, ] +loguru = [ + {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, + {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, +] lxml = [ {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"}, {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"}, @@ -1536,6 +1590,14 @@ sqlalchemy = [ {file = "SQLAlchemy-1.4.40-cp39-cp39-win_amd64.whl", hash = "sha256:bf073c619b5a7f7cd731507d0fdc7329bee14b247a63b0419929e4acd24afea8"}, {file = "SQLAlchemy-1.4.40.tar.gz", hash = "sha256:44a660506080cc975e1dfa5776fe5f6315ddc626a77b50bf0eee18b0389ea265"}, ] +sqlalchemy2-stubs = [ + {file = "sqlalchemy2-stubs-0.0.2a27.tar.gz", hash = "sha256:f79bce50b7837a2c2374ef4480b41e2b8a8226f313f347dc2a70526a4191db93"}, + {file = "sqlalchemy2_stubs-0.0.2a27-py3-none-any.whl", hash = "sha256:6cea12fec3c261f6e0e14a95d2cc4914e373095e68ec4fc2eb473183ac2b17a2"}, +] +sqlmodel = [ + {file = "sqlmodel-0.0.8-py3-none-any.whl", hash = "sha256:0fd805719e0c5d4f22be32eb3ffc856eca3f7f20e8c7aa3e117ad91684b518ee"}, + {file = "sqlmodel-0.0.8.tar.gz", hash = "sha256:3371b4d1ad59d2ffd0c530582c2140b6c06b090b32af9b9c6412986d7b117036"}, +] starlette = [ {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"}, {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"}, @@ -1572,6 +1634,10 @@ virtualenv = [ {file = "virtualenv-20.16.3-py2.py3-none-any.whl", hash = "sha256:4193b7bc8a6cd23e4eb251ac64f29b4398ab2c233531e66e40b19a6b7b0d30c1"}, {file = "virtualenv-20.16.3.tar.gz", hash = "sha256:d86ea0bb50e06252d79e6c241507cb904fcd66090c3271381372d6221a3970f9"}, ] +win32-setctime = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] wrapt = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, diff --git a/pyproject.toml b/pyproject.toml index 26852b0..e3ae939 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,8 @@ python-jose = {extras = ["cryptography"], version = "^3.3.0"} pydantic = {extras = ["email"], version = "^1.10.0"} python-multipart = "^0.0.5" emails = "^0.6" +sqlmodel = "^0.0.8" +loguru = "^0.6.0" [tool.poetry.dev-dependencies] pytest = "^7.1.2" @@ -30,6 +32,8 @@ isort = "^5.10.1" line-length = 79 target-version = [ "py39",] +[tool.poetry.scripts] +web = "app.main:" [build-system] requires = ["poetry-core>=1.0.0"]