mirror of
https://github.com/xlorepdarkhelm/numinar-coding-project.git
synced 2026-09-07 08:38:26 -04:00
More restructuring of files to adhere more to how FastAPI does things.
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
@@ -1,10 +1,5 @@
|
||||
"""DB connection and session management for SQLModel."""
|
||||
"""Database configuration and engine setup."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import TypeVar
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
from backend import config
|
||||
@@ -23,15 +18,3 @@ echo = config("ENVIRONMENT", default="development") in {"development"}
|
||||
|
||||
# Create the async engine
|
||||
engine = create_async_engine(db_url, echo=echo)
|
||||
|
||||
# Used in the sessionize decorator
|
||||
T = TypeVar("T")
|
||||
|
||||
# Create an asynchronous session maker for SQLModel, used in the sessionize decorator
|
||||
async_maker = async_sessionmaker(engine)
|
||||
|
||||
|
||||
async def get_session() -> AsyncGenerator[AsyncSession, None]: # pragma: no cover
|
||||
"""Inject a new DB session."""
|
||||
async with async_maker() as session:
|
||||
yield session
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
"""Dependency definitions for FastAPI routes."""
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.db import get_session
|
||||
|
||||
|
||||
type SessionDep = Annotated[AsyncSession, Depends(get_session)]
|
||||
23
backend/src/backend/dependencies/db.py
Normal file
23
backend/src/backend/dependencies/db.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""Dependency definitions for FastAPI routes."""
|
||||
|
||||
from typing import Annotated
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||
|
||||
from backend import db
|
||||
|
||||
|
||||
# Create an asynchronous session maker for SQLModel, used in the sessionize decorator
|
||||
async_maker = async_sessionmaker(db.engine)
|
||||
|
||||
|
||||
async def get_session() -> AsyncGenerator[AsyncSession, None]: # pragma: no cover
|
||||
"""Inject a new DB session."""
|
||||
async with async_maker() as session:
|
||||
yield session
|
||||
|
||||
|
||||
type SessionDep = Annotated[AsyncSession, Depends(get_session)]
|
||||
1
backend/src/backend/services/__init__.py
Normal file
1
backend/src/backend/services/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Services package for the Numinar coding project backend."""
|
||||
130
backend/src/backend/services/bookings.py
Normal file
130
backend/src/backend/services/bookings.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""SQL Database services for managing bookings."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import TypedDict
|
||||
from typing import Unpack
|
||||
from typing import cast
|
||||
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import update
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.models import Room
|
||||
from backend.models import User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
type UserList = list[User]
|
||||
type RoomList = list[Room]
|
||||
type BookingList = list[Booking]
|
||||
|
||||
|
||||
async def get_bookings_for_room(session: AsyncSession, room_id: int) -> BookingList:
|
||||
"""Retrieve all bookings for a specific room."""
|
||||
logger.debug(f"Entering get_bookings_for_room with room_id: {room_id}")
|
||||
try:
|
||||
stmt = select(Booking).where(Booking.room_id == room_id)
|
||||
result = await session.scalars(stmt)
|
||||
bookings = cast(BookingList, result.all())
|
||||
logger.info(
|
||||
f"Successfully retrieved {len(bookings)} bookings for room_id: {room_id}"
|
||||
)
|
||||
return bookings
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve bookings for room_id {room_id}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_bookings_for_room")
|
||||
|
||||
|
||||
async def get_booking(session: AsyncSession, booking_id: int) -> Booking:
|
||||
"""Retrieve a booking by its ID."""
|
||||
logger.debug(f"Entering get_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = select(Booking).where(Booking.id == booking_id)
|
||||
booking = await session.scalar(stmt)
|
||||
logger.info(f"Successfully retrieved booking with id: {booking_id}")
|
||||
return cast(Booking, booking)
|
||||
except NoResultFound:
|
||||
logger.error(f"Booking with id {booking_id} not found")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve booking with id {booking_id}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_booking")
|
||||
|
||||
|
||||
async def new_booking(session: AsyncSession, booking: Booking) -> Booking:
|
||||
"""Create a new booking in the database."""
|
||||
logger.debug("Entering new_booking")
|
||||
try:
|
||||
session.add(booking)
|
||||
await session.commit()
|
||||
logger.info(f"Successfully created new booking with id: {booking.id}")
|
||||
return booking
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create new booking: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting new_booking")
|
||||
|
||||
|
||||
class BookingParams(TypedDict):
|
||||
"""Parameters for updating a booking."""
|
||||
|
||||
room_id: int
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
|
||||
|
||||
async def update_booking(
|
||||
session: AsyncSession,
|
||||
booking_id: int,
|
||||
**kwargs: Unpack[BookingParams],
|
||||
) -> Booking:
|
||||
"""Update an existing booking."""
|
||||
logger.debug(
|
||||
f"Entering update_booking with booking_id: {booking_id}, params: {kwargs}"
|
||||
)
|
||||
try:
|
||||
stmt = update(Booking).where(Booking.id == booking_id).values(**kwargs)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No booking found with id {booking_id} for update")
|
||||
raise ValueError(f"No booking found with id {booking_id}")
|
||||
await session.commit()
|
||||
booking = await get_booking(session, booking_id)
|
||||
logger.info(f"Successfully updated booking with id: {booking_id}")
|
||||
return booking
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update booking with id {booking_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting update_booking")
|
||||
|
||||
|
||||
async def delete_booking(session: AsyncSession, booking_id: int) -> None:
|
||||
"""Delete a booking from the database."""
|
||||
logger.debug(f"Entering delete_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = delete(Booking).where(Booking.id == booking_id)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No booking found with id {booking_id} for deletion")
|
||||
raise ValueError(f"No booking found with id {booking_id}")
|
||||
await session.commit()
|
||||
logger.info(f"Successfully deleted booking with id: {booking_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete booking with id {booking_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting delete_booking")
|
||||
113
backend/src/backend/services/invitees.py
Normal file
113
backend/src/backend/services/invitees.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""SQL Database services for managing bookings."""
|
||||
|
||||
import logging
|
||||
from typing import cast
|
||||
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.models import Invitee
|
||||
from backend.models import Room
|
||||
from backend.models import User
|
||||
from backend.services.users import get_user_by_email
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
type UserList = list[User]
|
||||
type RoomList = list[Room]
|
||||
type BookingList = list[Booking]
|
||||
|
||||
|
||||
async def get_invitees_for_booking(session: AsyncSession, booking_id: int) -> UserList:
|
||||
"""Retrieve all invitees for a specific booking."""
|
||||
logger.debug(f"Entering get_invitees_for_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = select(Invitee.user).where(Invitee.booking_id == booking_id)
|
||||
result = await session.scalars(stmt)
|
||||
invitees = cast(UserList, result.all())
|
||||
logger.info(
|
||||
f"Successfully retrieved {len(invitees)} invitees for booking_id: {booking_id}"
|
||||
)
|
||||
return invitees
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to retrieve invitees for booking_id {booking_id}: {str(e)}"
|
||||
)
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_invitees_for_booking")
|
||||
|
||||
|
||||
async def add_invitee_to_booking(
|
||||
session: AsyncSession, booking_id: int, email: str
|
||||
) -> Invitee:
|
||||
"""Add an invitee to a booking."""
|
||||
logger.debug(
|
||||
f"Entering add_invitee_to_booking with booking_id: {booking_id}, email: {email}"
|
||||
)
|
||||
try:
|
||||
user = await get_user_by_email(session, email)
|
||||
invitee = Invitee(booking_id=booking_id, user_id=user.id)
|
||||
session.add(invitee)
|
||||
await session.commit()
|
||||
logger.info(
|
||||
f"Successfully added invitee with email {email} to booking_id: {booking_id}"
|
||||
)
|
||||
return invitee
|
||||
except NoResultFound as e:
|
||||
logger.error(
|
||||
f"User with email {email} does not exist for booking_id {booking_id}"
|
||||
)
|
||||
raise ValueError(f"User with email {email} does not exist.") from e
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to add invitee with email {email} to booking_id {booking_id}: {str(e)}"
|
||||
)
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting add_invitee_to_booking")
|
||||
|
||||
|
||||
async def remove_invitee_from_booking(
|
||||
session: AsyncSession, booking_id: int, email: str
|
||||
) -> None:
|
||||
"""Remove an invitee from a booking."""
|
||||
logger.debug(
|
||||
f"Entering remove_invitee_from_booking with booking_id: {booking_id}, email: {email}"
|
||||
)
|
||||
try:
|
||||
user = await get_user_by_email(session, email)
|
||||
stmt = delete(Invitee).where(
|
||||
Invitee.booking_id == booking_id, Invitee.user_id == user.id
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(
|
||||
f"No invitee with email {email} found for booking_id {booking_id}"
|
||||
)
|
||||
raise ValueError(
|
||||
f"No invitee with email {email} found for booking_id {booking_id}"
|
||||
)
|
||||
await session.commit()
|
||||
logger.info(
|
||||
f"Successfully removed invitee with email {email} from booking_id: {booking_id}"
|
||||
)
|
||||
except NoResultFound as e:
|
||||
logger.error(
|
||||
f"User with email {email} does not exist for booking_id {booking_id}"
|
||||
)
|
||||
raise ValueError(f"User with email {email} does not exist.") from e
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to remove invitee with email {email}"
|
||||
f" from booking_id {booking_id}: {str(e)}"
|
||||
)
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting remove_invitee_from_booking")
|
||||
124
backend/src/backend/services/rooms.py
Normal file
124
backend/src/backend/services/rooms.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""SQL Database services for managing rooms."""
|
||||
|
||||
import logging
|
||||
from typing import TypedDict
|
||||
from typing import Unpack
|
||||
from typing import cast
|
||||
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import update
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.models import Room
|
||||
from backend.models import User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
type UserList = list[User]
|
||||
type RoomList = list[Room]
|
||||
type BookingList = list[Booking]
|
||||
|
||||
|
||||
async def get_rooms(session: AsyncSession) -> RoomList:
|
||||
"""Retrieve all rooms from the database."""
|
||||
logger.debug("Entering get_rooms")
|
||||
try:
|
||||
stmt = select(Room)
|
||||
result = await session.scalars(stmt)
|
||||
rooms = cast(RoomList, result.all())
|
||||
logger.info(f"Successfully retrieved {len(rooms)} rooms")
|
||||
return rooms
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve rooms: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_rooms")
|
||||
|
||||
|
||||
async def get_room(session: AsyncSession, room_id: int) -> Room:
|
||||
"""Retrieve a room by its ID."""
|
||||
logger.debug(f"Entering get_room with room_id: {room_id}")
|
||||
try:
|
||||
stmt = select(Room).where(Room.id == room_id)
|
||||
room = await session.scalar(stmt)
|
||||
logger.info(f"Successfully retrieved room with id: {room_id}")
|
||||
return cast(Room, room)
|
||||
except NoResultFound:
|
||||
logger.error(f"Room with id {room_id} not found")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve room with id {room_id}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_room")
|
||||
|
||||
|
||||
async def new_room(session: AsyncSession, room: Room) -> Room:
|
||||
"""Create a new room in the database."""
|
||||
logger.debug("Entering new_room")
|
||||
try:
|
||||
session.add(room)
|
||||
await session.commit()
|
||||
logger.info(f"Successfully created new room with id: {room.id}")
|
||||
return room
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create new room: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting new_room")
|
||||
|
||||
|
||||
class RoomParams(TypedDict):
|
||||
"""Parameters for updating a room."""
|
||||
|
||||
name: str
|
||||
location: str
|
||||
equipment: str
|
||||
capacity: int
|
||||
|
||||
|
||||
async def update_room(
|
||||
session: AsyncSession, room_id: int, **kwargs: Unpack[RoomParams]
|
||||
) -> Room:
|
||||
"""Update an existing room."""
|
||||
logger.debug(f"Entering update_room with room_id: {room_id}, params: {kwargs}")
|
||||
try:
|
||||
stmt = update(Room).where(Room.id == room_id).values(**kwargs)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No room found with id {room_id} for update")
|
||||
raise ValueError(f"No room found with id {room_id}")
|
||||
await session.commit()
|
||||
room = await get_room(session, room_id)
|
||||
logger.info(f"Successfully updated room with id: {room_id}")
|
||||
return room
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update room with id {room_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting update_room")
|
||||
|
||||
|
||||
async def delete_room(session: AsyncSession, room_id: int) -> None:
|
||||
"""Delete a room from the database."""
|
||||
logger.debug(f"Entering delete_room with room_id: {room_id}")
|
||||
try:
|
||||
stmt = delete(Room).where(Room.id == room_id)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No room found with id {room_id} for deletion")
|
||||
raise ValueError(f"No room found with id {room_id}")
|
||||
await session.commit()
|
||||
logger.info(f"Successfully deleted room with id: {room_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete room with id {room_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting delete_room")
|
||||
53
backend/src/backend/services/users.py
Normal file
53
backend/src/backend/services/users.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""SQL Database for managing users."""
|
||||
|
||||
import logging
|
||||
from typing import cast
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.models import Room
|
||||
from backend.models import User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
type UserList = list[User]
|
||||
type RoomList = list[Room]
|
||||
type BookingList = list[Booking]
|
||||
|
||||
|
||||
async def get_users(session: AsyncSession) -> UserList:
|
||||
"""Retrieve all users from the database."""
|
||||
logger.debug("Entering get_users")
|
||||
try:
|
||||
stmt = select(User)
|
||||
result = await session.scalars(stmt)
|
||||
users = cast(UserList, result.all())
|
||||
logger.info(f"Successfully retrieved {len(users)} users")
|
||||
return users
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve users: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_users")
|
||||
|
||||
|
||||
async def get_user_by_email(session: AsyncSession, email: str) -> User:
|
||||
"""Retrieve a user by their email address."""
|
||||
logger.debug(f"Entering get_user_by_email with email: {email}")
|
||||
try:
|
||||
stmt = select(User).where(User.email == email)
|
||||
user = await session.scalar(stmt)
|
||||
logger.info(f"Successfully retrieved user with email: {email}")
|
||||
return cast(User, user)
|
||||
except NoResultFound:
|
||||
logger.error(f"User with email {email} not found")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve user with email {email}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_user_by_email")
|
||||
@@ -1,357 +0,0 @@
|
||||
"""SQL Database access functions for the Numinar coding project backend."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import TypedDict
|
||||
from typing import Unpack
|
||||
from typing import cast
|
||||
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import update
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.models import Booking
|
||||
from backend.models import Invitee
|
||||
from backend.models import Room
|
||||
from backend.models import User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
type UserList = list[User]
|
||||
type RoomList = list[Room]
|
||||
type BookingList = list[Booking]
|
||||
|
||||
|
||||
async def get_users(session: AsyncSession) -> UserList:
|
||||
"""Retrieve all users from the database."""
|
||||
logger.debug("Entering get_users")
|
||||
try:
|
||||
stmt = select(User)
|
||||
result = await session.scalars(stmt)
|
||||
users = cast(UserList, result.all())
|
||||
logger.info(f"Successfully retrieved {len(users)} users")
|
||||
return users
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve users: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_users")
|
||||
|
||||
|
||||
async def get_user_by_email(session: AsyncSession, email: str) -> User:
|
||||
"""Retrieve a user by their email address."""
|
||||
logger.debug(f"Entering get_user_by_email with email: {email}")
|
||||
try:
|
||||
stmt = select(User).where(User.email == email)
|
||||
user = await session.scalar(stmt)
|
||||
logger.info(f"Successfully retrieved user with email: {email}")
|
||||
return cast(User, user)
|
||||
except NoResultFound:
|
||||
logger.error(f"User with email {email} not found")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve user with email {email}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_user_by_email")
|
||||
|
||||
|
||||
async def get_rooms(session: AsyncSession) -> RoomList:
|
||||
"""Retrieve all rooms from the database."""
|
||||
logger.debug("Entering get_rooms")
|
||||
try:
|
||||
stmt = select(Room)
|
||||
result = await session.scalars(stmt)
|
||||
rooms = cast(RoomList, result.all())
|
||||
logger.info(f"Successfully retrieved {len(rooms)} rooms")
|
||||
return rooms
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve rooms: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_rooms")
|
||||
|
||||
|
||||
async def get_room(session: AsyncSession, room_id: int) -> Room:
|
||||
"""Retrieve a room by its ID."""
|
||||
logger.debug(f"Entering get_room with room_id: {room_id}")
|
||||
try:
|
||||
stmt = select(Room).where(Room.id == room_id)
|
||||
room = await session.scalar(stmt)
|
||||
logger.info(f"Successfully retrieved room with id: {room_id}")
|
||||
return cast(Room, room)
|
||||
except NoResultFound:
|
||||
logger.error(f"Room with id {room_id} not found")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve room with id {room_id}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_room")
|
||||
|
||||
|
||||
async def new_room(session: AsyncSession, room: Room) -> Room:
|
||||
"""Create a new room in the database."""
|
||||
logger.debug("Entering new_room")
|
||||
try:
|
||||
session.add(room)
|
||||
await session.commit()
|
||||
logger.info(f"Successfully created new room with id: {room.id}")
|
||||
return room
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create new room: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting new_room")
|
||||
|
||||
|
||||
class RoomParams(TypedDict):
|
||||
"""Parameters for updating a room."""
|
||||
|
||||
name: str
|
||||
location: str
|
||||
equipment: str
|
||||
capacity: int
|
||||
|
||||
|
||||
async def update_room(
|
||||
session: AsyncSession, room_id: int, **kwargs: Unpack[RoomParams]
|
||||
) -> Room:
|
||||
"""Update an existing room."""
|
||||
logger.debug(f"Entering update_room with room_id: {room_id}, params: {kwargs}")
|
||||
try:
|
||||
stmt = update(Room).where(Room.id == room_id).values(**kwargs)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No room found with id {room_id} for update")
|
||||
raise ValueError(f"No room found with id {room_id}")
|
||||
await session.commit()
|
||||
room = await get_room(session, room_id)
|
||||
logger.info(f"Successfully updated room with id: {room_id}")
|
||||
return room
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update room with id {room_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting update_room")
|
||||
|
||||
|
||||
async def delete_room(session: AsyncSession, room_id: int) -> None:
|
||||
"""Delete a room from the database."""
|
||||
logger.debug(f"Entering delete_room with room_id: {room_id}")
|
||||
try:
|
||||
stmt = delete(Room).where(Room.id == room_id)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No room found with id {room_id} for deletion")
|
||||
raise ValueError(f"No room found with id {room_id}")
|
||||
await session.commit()
|
||||
logger.info(f"Successfully deleted room with id: {room_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete room with id {room_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting delete_room")
|
||||
|
||||
|
||||
async def get_bookings_for_room(session: AsyncSession, room_id: int) -> BookingList:
|
||||
"""Retrieve all bookings for a specific room."""
|
||||
logger.debug(f"Entering get_bookings_for_room with room_id: {room_id}")
|
||||
try:
|
||||
stmt = select(Booking).where(Booking.room_id == room_id)
|
||||
result = await session.scalars(stmt)
|
||||
bookings = cast(BookingList, result.all())
|
||||
logger.info(
|
||||
f"Successfully retrieved {len(bookings)} bookings for room_id: {room_id}"
|
||||
)
|
||||
return bookings
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve bookings for room_id {room_id}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_bookings_for_room")
|
||||
|
||||
|
||||
async def get_booking(session: AsyncSession, booking_id: int) -> Booking:
|
||||
"""Retrieve a booking by its ID."""
|
||||
logger.debug(f"Entering get_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = select(Booking).where(Booking.id == booking_id)
|
||||
booking = await session.scalar(stmt)
|
||||
logger.info(f"Successfully retrieved booking with id: {booking_id}")
|
||||
return cast(Booking, booking)
|
||||
except NoResultFound:
|
||||
logger.error(f"Booking with id {booking_id} not found")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve booking with id {booking_id}: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_booking")
|
||||
|
||||
|
||||
async def new_booking(session: AsyncSession, booking: Booking) -> Booking:
|
||||
"""Create a new booking in the database."""
|
||||
logger.debug("Entering new_booking")
|
||||
try:
|
||||
session.add(booking)
|
||||
await session.commit()
|
||||
logger.info(f"Successfully created new booking with id: {booking.id}")
|
||||
return booking
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create new booking: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting new_booking")
|
||||
|
||||
|
||||
class BookingParams(TypedDict):
|
||||
"""Parameters for updating a booking."""
|
||||
|
||||
room_id: int
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
|
||||
|
||||
async def update_booking(
|
||||
session: AsyncSession,
|
||||
booking_id: int,
|
||||
**kwargs: Unpack[BookingParams],
|
||||
) -> Booking:
|
||||
"""Update an existing booking."""
|
||||
logger.debug(
|
||||
f"Entering update_booking with booking_id: {booking_id}, params: {kwargs}"
|
||||
)
|
||||
try:
|
||||
stmt = update(Booking).where(Booking.id == booking_id).values(**kwargs)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No booking found with id {booking_id} for update")
|
||||
raise ValueError(f"No booking found with id {booking_id}")
|
||||
await session.commit()
|
||||
booking = await get_booking(session, booking_id)
|
||||
logger.info(f"Successfully updated booking with id: {booking_id}")
|
||||
return booking
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update booking with id {booking_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting update_booking")
|
||||
|
||||
|
||||
async def delete_booking(session: AsyncSession, booking_id: int) -> None:
|
||||
"""Delete a booking from the database."""
|
||||
logger.debug(f"Entering delete_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = delete(Booking).where(Booking.id == booking_id)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(f"No booking found with id {booking_id} for deletion")
|
||||
raise ValueError(f"No booking found with id {booking_id}")
|
||||
await session.commit()
|
||||
logger.info(f"Successfully deleted booking with id: {booking_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete booking with id {booking_id}: {str(e)}")
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting delete_booking")
|
||||
|
||||
|
||||
async def get_invitees_for_booking(session: AsyncSession, booking_id: int) -> UserList:
|
||||
"""Retrieve all invitees for a specific booking."""
|
||||
logger.debug(f"Entering get_invitees_for_booking with booking_id: {booking_id}")
|
||||
try:
|
||||
stmt = select(Invitee.user).where(Invitee.booking_id == booking_id)
|
||||
result = await session.scalars(stmt)
|
||||
invitees = cast(UserList, result.all())
|
||||
logger.info(
|
||||
f"Successfully retrieved {len(invitees)} invitees for booking_id: {booking_id}"
|
||||
)
|
||||
return invitees
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to retrieve invitees for booking_id {booking_id}: {str(e)}"
|
||||
)
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting get_invitees_for_booking")
|
||||
|
||||
|
||||
async def add_invitee_to_booking(
|
||||
session: AsyncSession, booking_id: int, email: str
|
||||
) -> Invitee:
|
||||
"""Add an invitee to a booking."""
|
||||
logger.debug(
|
||||
f"Entering add_invitee_to_booking with booking_id: {booking_id}, email: {email}"
|
||||
)
|
||||
try:
|
||||
user = await get_user_by_email(session, email)
|
||||
invitee = Invitee(booking_id=booking_id, user_id=user.id)
|
||||
session.add(invitee)
|
||||
await session.commit()
|
||||
logger.info(
|
||||
f"Successfully added invitee with email {email} to booking_id: {booking_id}"
|
||||
)
|
||||
return invitee
|
||||
except NoResultFound as e:
|
||||
logger.error(
|
||||
f"User with email {email} does not exist for booking_id {booking_id}"
|
||||
)
|
||||
raise ValueError(f"User with email {email} does not exist.") from e
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to add invitee with email {email} to booking_id {booking_id}: {str(e)}"
|
||||
)
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting add_invitee_to_booking")
|
||||
|
||||
|
||||
async def remove_invitee_from_booking(
|
||||
session: AsyncSession, booking_id: int, email: str
|
||||
) -> None:
|
||||
"""Remove an invitee from a booking."""
|
||||
logger.debug(
|
||||
f"Entering remove_invitee_from_booking with booking_id: {booking_id}, email: {email}"
|
||||
)
|
||||
try:
|
||||
user = await get_user_by_email(session, email)
|
||||
stmt = delete(Invitee).where(
|
||||
Invitee.booking_id == booking_id, Invitee.user_id == user.id
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
logger.error(
|
||||
f"No invitee with email {email} found for booking_id {booking_id}"
|
||||
)
|
||||
raise ValueError(
|
||||
f"No invitee with email {email} found for booking_id {booking_id}"
|
||||
)
|
||||
await session.commit()
|
||||
logger.info(
|
||||
f"Successfully removed invitee with email {email} from booking_id: {booking_id}"
|
||||
)
|
||||
except NoResultFound as e:
|
||||
logger.error(
|
||||
f"User with email {email} does not exist for booking_id {booking_id}"
|
||||
)
|
||||
raise ValueError(f"User with email {email} does not exist.") from e
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to remove invitee with email {email}"
|
||||
f" from booking_id {booking_id}: {str(e)}"
|
||||
)
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
logger.debug("Exiting remove_invitee_from_booking")
|
||||
Reference in New Issue
Block a user