switched docstring format to one-line-sphinx

This commit is contained in:
Lucas Jensen
2024-05-05 13:32:31 -07:00
parent ab0fae44f7
commit e1a29b398e
12 changed files with 300 additions and 243 deletions

View File

@@ -2,10 +2,16 @@ import smtplib
from email.mime.text import MIMEText
from os import getenv
HOST = "grapefruitswebsite@gmail.com"
from app.constants import HOST
def send_email(subject: str, body: str) -> None:
"""
Sends an email using the Gmail SMTP server.
:param str subject: The subject of the email
:param str body: The body of the email
"""
password = getenv("APP_PASSWORD")
email = getenv("EMAIL")
msg = MIMEText(body)

View File

@@ -1,7 +1,6 @@
# Set your Cloudinary credentials
# ==============================
from pprint import pprint
from dotenv import load_dotenv
@@ -22,10 +21,20 @@ uploader = cloudinary.uploader
class CloudinaryException(Exception):
"""
Custom exception for Cloudinary errors.
"""
pass
def delete_image(public_id: str) -> None:
"""
Deletes an image from the Cloudinary cloud.
:param str public_id: The public ID of the image to delete
:raises CloudinaryException: If the image deletion fails
"""
result = uploader.destroy(public_id)
if result.get("result") != "ok":
@@ -33,16 +42,25 @@ def delete_image(public_id: str) -> None:
def get_image_data(public_id: str) -> dict:
"""
Retrieves the metadata for an image from the Cloudinary cloud.
:param str public_id: The public ID of the image to retrieve
:return dict: The metadata for the image
"""
data = cloudinary.api.resource(public_id)
return data
def get_image_url(public_id: str) -> str:
"""
Retrieves the URL for an image from the Cloudinary cloud.
:param str public_id: The public ID of the image to retrieve
:raises CloudinaryException: If the image URL retrieval fails
:return str: The URL of the image
"""
url = cloudinary.utils.cloudinary_url(public_id)[0]
if url is None:
raise CloudinaryException("Failed to get image URL")
return url
if __name__ == "__main__":
image_id = "coco_copy_jywbxm"

View File

@@ -7,3 +7,6 @@ EVENT_TABLE = "events"
GROUP_TABLE = "group_table"
MUSICIAN_TABLE = "musicians"
USER_TABLE = "users"
# contact form email
HOST = "grapefruitswebsite@gmail.com"

View File

@@ -17,21 +17,21 @@ class BaseController:
"""
def __init__(self) -> None:
"""
Initializes the BaseController with a BaseQueries object.
"""
self.db: BaseQueries = None # type: ignore
self.ALL_FILES = ALLOWED_FILES_TYPES
self.MAX_FILE_SIZE = ONE_MB
def verify_image(self, file: UploadFile) -> bytes:
"""Verifies that the file is an image and is within the maximum file size.
"""
Verifies that the file is an image and is within the maximum file size.
Args:
file (UploadFile): The file to be verified
Raises:
HTTPException: If the file is not an image or exceeds the maximum file size (status code 400)
Returns:
bytes: The file contents as bytes
:param UploadFile file: The file to be verified
:raises HTTPException: If the file type is not allowed (status code 400)
:raises HTTPException: If the file size exceeds the maximum (status code 400)
:return bytes: The image file as bytes
"""
if file.content_type not in self.ALL_FILES:
raise HTTPException(
@@ -48,10 +48,10 @@ class BaseController:
return image_file
def log_error(self, e: Exception) -> None:
"""Logs an error to a timestamped text file in the logs directory.
"""
Logs an error to a timestamped text file in the logs directory.
Args:
e (Exception): Any exception object
:param Exception e: The exception to be logged
"""
curr_dir = Path(__file__).parent
log_dir = curr_dir / "logs"

View File

@@ -1,3 +1,5 @@
from typing import Optional
from fastapi import HTTPException, UploadFile, status
from fastapi.security import HTTPAuthorizationCredentials
from icecream import ic
@@ -22,19 +24,11 @@ from app.models.user import User
class MainController:
"""
The main controller and entry point for all API requests.
All methods are either pass-throughs to the appropriate controller or
are used to coordinate multiple controllers.
All methods are asynchronous to facilitate asynchronous calls from the Router layer.
token-based authentication is handled here as needed per the nature of the data being accessed.
Testing: pass mocked sub-controllers to the constructor.
"""
def __init__(
self,
event_controller=event_controller,
event_controller: EventController = event_controller,
musicians_controller=musicians_controller,
user_controller=user_controller,
group_controller=group_controller,
@@ -47,9 +41,20 @@ class MainController:
self.oauth_token = oauth_token
async def get_musicians(self) -> list[Musician]:
"""
Retrieves all musicians and returns them as a list.
:return list[Musician]: _description_
"""
return self.musician_controller.get_musicians()
async def get_musician(self, musician_id: int) -> Musician:
"""
Retrieves a single musician by numeric ID.
:param int musician_id: The ID of the musician to retrieve
:return Musician: The musician object for a response body
"""
return self.musician_controller.get_musician(musician_id)
async def update_musician(
@@ -57,8 +62,18 @@ class MainController:
musician: Musician,
url_param_id: int,
token: HTTPAuthorizationCredentials,
file: UploadFile | None = None,
file: Optional[UploadFile] = None,
) -> Musician:
"""
Updates a musician in the database and returns the updated musician object.
:param Musician musician: The musician object to update
:param int url_param_id: The ID of the musician in the URL
:param HTTPAuthorizationCredentials token: The OAuth token
:param Optional[UploadFile] file: The new headshot file, defaults to None
:raises HTTPException: If the ID in the URL does not match the ID in the request body (status code 400)
:return Musician: The updated musician object which is suitable for a response body
"""
if musician.id != url_param_id:
raise HTTPException(
@@ -74,14 +89,32 @@ class MainController:
)
async def get_events(self) -> list[EventSeries]:
"""
Retrieves all event series and returns them as a list.
:return list[EventSeries]: a list of EventSeries objects for a response body
"""
return self.event_controller.get_all_series()
async def get_event(self, series_id: int) -> EventSeries:
"""
Retrieves a single event series by numeric ID.
:param int series_id: The ID of the event series to retrieve
:return EventSeries: The event series object for a response body
"""
return self.event_controller.get_one_series_by_id(series_id)
async def create_event(
self, series: NewEventSeries, token: HTTPAuthorizationCredentials
) -> EventSeries:
"""
Creates a new event series and returns the created event series object.
:param NewEventSeries series: The new event series object
:param HTTPAuthorizationCredentials token: The OAuth token
:return EventSeries: The newly created event series object which is suitable for a response body
"""
_, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub)
return self.event_controller.create_series(series)
@@ -89,6 +122,14 @@ class MainController:
async def add_series_poster(
self, series_id: int, poster: UploadFile, token: HTTPAuthorizationCredentials
) -> EventSeries:
"""
Adds a poster to an event series and returns the updated event series object.
:param int series_id: The ID of the event series to update
:param UploadFile poster: The image file to upload
:param HTTPAuthorizationCredentials token: The OAuth token
:return EventSeries: The updated event series object which is suitable for a response body
"""
_, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub)
return self.event_controller.add_series_poster(series_id, poster)
@@ -96,6 +137,12 @@ class MainController:
async def delete_series(
self, series_id: int, token: HTTPAuthorizationCredentials
) -> None:
"""
Deletes an event series by numeric ID.
:param int series_id: The ID of the event series to delete
:param HTTPAuthorizationCredentials token: The OAuth token
"""
_, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub)
self.event_controller.delete_series(series_id)
@@ -103,35 +150,63 @@ class MainController:
async def update_series(
self, route_id: int, series: EventSeries, token: HTTPAuthorizationCredentials
) -> EventSeries:
"""
Updates an event series and returns the updated event series object.
:param int route_id: The ID of the event series in the URL
:param EventSeries series: The updated event series object
:param HTTPAuthorizationCredentials token: The OAuth token
:return EventSeries: The updated event series object which is suitable for a response body
"""
_, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub)
return self.event_controller.update_series(route_id, series)
async def get_users(self) -> list[User]:
"""
Retrieves all users and returns them as a list.
:return list[User]: a list of User objects for a response body
"""
return self.user_controller.get_users()
async def get_user(self, user_id: int) -> User:
"""
Retrieves a single user by numeric ID.
:param int user_id: The ID of the user to retrieve
:return User: The user object for a response body
"""
return self.user_controller.get_user_by_id(user_id)
async def create_user(self, token: HTTPAuthorizationCredentials) -> User:
"""This method does NOT post a user to the database.
Instead, it retrieves the user's information from the OAuth token,
updates the user's sub in the db if needed, and returns the user object.
"""
Creates a new user and returns the created user object.
Does NOT post a user to the database (this is not supported).
Args:
token (HTTPAuthorizationCredentials): The OAuth token
Returns:
User: The User object
:param HTTPAuthorizationCredentials token: The OAuth token
:return User: The newly created user object which is suitable for a response body
"""
return self.user_controller.create_user(token)
async def get_group(self) -> Group:
"""
Retrieves the group object and returns it.
:return Group: The group object for a response body
"""
return self.group_controller.get_group()
async def update_group_bio(
self, bio: str, token: HTTPAuthorizationCredentials
) -> Group:
"""
Updates the group's bio and returns the updated group object.
:param str bio: The new bio for the group
:param HTTPAuthorizationCredentials token: The OAuth token
:return Group: The updated group object which is suitable for a response body
"""
_, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub)
return self.group_controller.update_group_bio(bio)

View File

@@ -11,26 +11,26 @@ from app.models.event import Event, EventSeries, NewEventSeries
class EventController(BaseController):
"""
Handles all event-related operations and serves as an intermediate controller between
the main controller and the model layer.
Handles all event-related operations.
Inherits from BaseController, which provides logging and other generic methods.
Testing: pass a mocked EventQueries object to the constructor.
"""
def __init__(self, event_queries=event_queries) -> None:
def __init__(self, event_queries: EventQueries = event_queries) -> None:
"""
Initializes the EventController with an EventQueries object.
:param EventQueries event_queries: object for querying event data, defaults to event_queries
"""
super().__init__()
self.db: EventQueries = event_queries
def _all_series(self, data_rows: list[dict]) -> dict[str, EventSeries]:
"""Creates and returns a dictionary of EventSeries objects from a list of sql rows (as dicts).
Should only be used internally.
"""
Builds a dictionary of EventSeries objects from a list of dictionaries.
Must only be used internally.
Args:
data_rows (list[dict]): List of dicts, each representing a row from the database. `event_id` may be null
Returns:
dict[str, EventSeries]: A dictionary of EventSeries objects, keyed by series name
:param list[dict] data_rows: The list of sql rows as dictionaries
:return dict[str, EventSeries]: A dictionary of EventSeries objects keyed by series name
"""
all_series: dict[str, EventSeries] = {}
@@ -44,13 +44,11 @@ class EventController(BaseController):
return all_series
def get_all_series(self) -> list[EventSeries]:
"""Retrieves all EventSeries objects from the database and returns them as a list.
"""
Retrieves all EventSeries objects and returns them as a list.
Raises:
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
list[EventSeries]: A list of EventSeries objects which are suitable for a response body
:raises HTTPException: If any error occurs (status code 500)
:return list[EventSeries]: A list of EventSeries objects suitable for a response body
"""
series_data = self.db.select_all_series()
@@ -64,17 +62,13 @@ class EventController(BaseController):
)
def get_one_series_by_id(self, series_id: int) -> EventSeries:
"""Builds and returns a single EventSeries object by its numeric ID.
"""
Retrieves a single EventSeries object by numeric ID.
Args:
series_id (int): The numeric id of the series to retrieve
Raises:
HTTPException: If the series is not found (status code 404)
HTTPException: If an error occurs (status code 500)
Returns:
EventSeries: A single EventSeries object
:param int series_id: The ID of the series to retrieve
:raises HTTPException: If the series is not found (status code 404)
:raises HTTPException: If any error occurs during the instantiation process (status code 500)
:return EventSeries: The EventSeries object which is suitable for a response body
"""
if not (rows := self.db.select_one_by_id(series_id)):
raise HTTPException(
@@ -91,16 +85,12 @@ class EventController(BaseController):
)
def create_series(self, series: NewEventSeries) -> EventSeries:
"""Takes a NewEventSeries object and passes it to the database layer for insertion.
"""
Passes a new EventSeries object to the database for creation and returns the created object.
Args:
series (NewEventSeries): A NewEventSeries object which does not yet have an ID
Raises:
HTTPException: If the series name already exists (status code 400)
Returns:
EventSeries: The newly created EventSeries object with an ID
:param NewEventSeries series: The new EventSeries object to create
:raises HTTPException: If the series name already exists (status code 400)
:return EventSeries: The created EventSeries object which is suitable for a response body
"""
try:
inserted_id = self.db.insert_one_series(series)
@@ -114,14 +104,12 @@ class EventController(BaseController):
)
def add_series_poster(self, series_id: int, poster: UploadFile) -> EventSeries:
"""Adds (or updates) a poster image to a series.
"""
Updates the poster image for an EventSeries object and returns the updated object.
Args:
series_id (int): The numeric ID of the series to update
poster (UploadFile): The image file to upload
Returns:
EventSeries: The updated EventSeries object
:param int series_id: The numeric ID of the series
:param UploadFile poster: The new poster image file
:return EventSeries: The updated EventSeries object with the new poster image
"""
series = self.get_one_series_by_id(series_id)
series.poster_id = self._upload_poster(poster)
@@ -129,17 +117,12 @@ class EventController(BaseController):
return self.get_one_series_by_id(series.series_id)
def _upload_poster(self, poster: UploadFile) -> str:
"""Uploads a poster image to Cloudinary and returns the public ID for storage in the database.
Should only be used internally.
"""
Uploads an image file to the cloud and returns the public ID.
Args:
poster (UploadFile): The image file to upload
Raises:
HTTPException: If an error occurs during the upload process (status code 500)
Returns:
str: The public ID of the uploaded image
:param UploadFile poster: The image file to upload
:raises HTTPException: If any error occurs during the upload process (status code 500)
:return str: The public ID of the uploaded image
"""
image_file = self.verify_image(poster)
try:
@@ -152,27 +135,23 @@ class EventController(BaseController):
)
def delete_series(self, id: int) -> None:
"""Ensures an EventSeries object exists and then deletes it from the database.
"""
Deletes an EventSeries object from the database.
Args:
id (int): The numeric ID of the series to delete
:param int id: The numeric ID of the series to delete
"""
series = self.get_one_series_by_id(id)
self.db.delete_one_series(series)
def update_series(self, route_id: int, series: EventSeries) -> EventSeries:
"""Updates an existing EventSeries object in the database.
"""
Updates an EventSeries object in the database and returns the updated object.
Args:
route_id (int): The numeric ID of the series in the URL
series (EventSeries): The updated EventSeries object
Raises:
HTTPException: if the ID in the URL does not match the ID in the request body (status code 400)
HTTPException: if the poster ID is updated directly (status code 400)
Returns:
EventSeries: The updated EventSeries object with updated info
:param int route_id: The numeric ID in the URL
:param EventSeries series: The updated EventSeries object
:raises HTTPException: If the ID in the URL does not match the ID in the request body (status code 400)
:raises HTTPException: If the poster ID is updated directly (status code 400)
:return EventSeries: The updated EventSeries object which is suitable for a response body
"""
if route_id != series.series_id:
raise HTTPException(

View File

@@ -8,28 +8,26 @@ from app.models.group import Group
class GroupController(BaseController):
"""
Handles all group-related operations and serves as an intermediate controller between
the main controller and the model layer.
Handles all group-related operations.
Inherits from BaseController, which provides logging and other generic methods.
The corresponding table contains only one row.
Testing: pass a mocked GroupQueries object to the constructor.
"""
def __init__(self, group_queries=group_queries) -> None:
"""
Initializes the GroupController with a GroupQueries object.
:param GroupQueries group_queries: object for quering group data, defaults to group_queries
"""
super().__init__()
self.group_queries: GroupQueries = group_queries
def get_group(self) -> Group:
"""Retrieves the group from the database and returns it as a Group object.
"""
Instantiates a Group object and retuns it for a response body.
Raises:
HTTPException: If the group is not found (status code 404)
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
Group: A Group object which is suitable for a response body
:raises HTTPException: If the group is not found (status code 404)
:raises HTTPException: If any error occurs during the instantiation process (status code 500)
:return Group: The Group object which is suitable for a response body
"""
if (data := self.group_queries.select_one_by_id()) is None:
raise HTTPException(
@@ -44,16 +42,12 @@ class GroupController(BaseController):
)
def update_group_bio(self, bio: str) -> Group:
"""Updates the group's bio in the database and returns the updated Group object.
"""
Updates the group's bio in the database and returns the updated Group object.
Args:
bio (str): The new bio for the group
Raises:
HTTPException: If any error occurs during the update process (status code 500)
Returns:
Group: The updated Group object which is suitable for a response body
:param str bio: The new bio for the group
:raises HTTPException: If any error occurs during the update process (status code 500)
:return Group: The updated Group object which is suitable for a response body
"""
try:
self.group_queries.update_group_bio(bio)

View File

@@ -1,3 +1,5 @@
from typing import Optional
from fastapi import HTTPException, UploadFile, status
from icecream import ic
@@ -10,25 +12,25 @@ from app.models.musician import Musician
class MusicianController(BaseController):
"""
Handles all musician-related operations and serves as an intermediate controller between
the main controller and the model layer.
Handles all musician-related operations.
Inherits from BaseController, which provides logging and other generic methods.
Testing: pass a mocked MusicianQueries object to the constructor.
"""
def __init__(self, musician_queries=musician_queries) -> None:
def __init__(self, musician_queries: MusicianQueries = musician_queries) -> None:
"""
Initializes the MusicianController with a MusicianQueries object.
:param MusicianQueries musician_queries: object for querying musician data, defaults to musician_queries
"""
super().__init__()
self.db: MusicianQueries = musician_queries
def get_musicians(self) -> list[Musician]:
"""Retrieves all musicians from the database and returns them as a list of Musician objects.
"""
Retrieves all musicians and returns them as a list.
Raises:
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
list[Musician]: A list of Musician objects which are suitable for a response body
:raises HTTPException: If any error occurs during the retrieval process (status code 500)
:return list[Musician]: A list of Musician objects suitable for a response body
"""
data = self.db.select_all_series()
try:
@@ -40,17 +42,13 @@ class MusicianController(BaseController):
)
def get_musician(self, musician_id: int) -> Musician:
"""Retrieves a single musician from the database and returns it as a Musician object.
"""
Retrieves a single musician by numeric ID.
Args:
id (int): The ID of the musician to retrieve
Raises:
HTTPException: If the musician is not found (status code 404)
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
Musician: A Musician object which is suitable for a response body
:param int musician_id: The ID of the musician to retrieve
:raises HTTPException: If the musician is not found (status code 404)
:raises HTTPException: If any error occurs during the instantiation process (status code 500)
:return Musician: The musician object for a response body
"""
if (data := self.db.select_one_by_id(musician_id)) is None:
raise HTTPException(
@@ -68,20 +66,16 @@ class MusicianController(BaseController):
self,
musician_id: int,
new_bio: str,
file: UploadFile | None = None,
file: Optional[UploadFile] = None,
) -> Musician:
"""Updates a musician's bio and/or headshot by conditionally calling the appropriate methods.
"""
Updates a musician in the database and returns the updated musician object.
Args:
musician_id (int): The numeric ID of the musician to update
new_bio (str): The new biography for the musician
file (UploadFile | None, optional): The new headshot file. Defaults to None.
Raises:
HTTPException: If the musician is not found (status code 404)
Returns:
Musician: The updated Musician object
:param int musician_id: The ID of the musician to update
:param str new_bio: The new biography for the musician
:param Optional[UploadFile] file: The new headshot file, defaults to None
:raises HTTPException: _description_
:return Musician: _description_
"""
musician = self.get_musician(musician_id)
if new_bio != musician.bio:
@@ -97,17 +91,13 @@ class MusicianController(BaseController):
def _update_musician_headshot(
self, musician: Musician, headshot_id: str
) -> Musician:
"""Updates a musician's headshot in the database.
"""
Updates a musician's headshot in the database.
Args:
musician (Musician): The musician object to update
headshot_id (str): The public ID of the new headshot (as determined by Cloudinary)
Raises:
HTTPException: If any error occurs during the update process (status code 500)
Returns:
Musician: The updated Musician object
:param Musician musician: The musician object to update
:param str headshot_id: The new public ID for the headshot
:raises HTTPException: If any error occurs during the update process (status code 500)
:return Musician: The updated Musician object
"""
try:
self.db.update_headshot(musician, headshot_id)
@@ -119,17 +109,13 @@ class MusicianController(BaseController):
return self.get_musician(musician.id)
def _update_musician_bio(self, musician: Musician, bio: str) -> Musician:
"""Updates a musician's bio in the database.
"""
Updates a musician's biography in the database.
Args:
musician (Musician): The musician object to update
bio (str): The new biography for the musician
Raises:
HTTPException: If any error occurs during the update process (status code 500)
Returns:
Musician: The updated Musician object
:param Musician musician: The musician object to update
:param str bio: The new biography for the musician
:raises HTTPException: If any error occurs during the update process (status code 500)
:return Musician: The updated Musician object
"""
try:
self.db.update_bio(musician, bio)
@@ -141,17 +127,13 @@ class MusicianController(BaseController):
return self.get_musician(musician.id)
def _upload_headshot(self, musician: Musician, file: UploadFile) -> Musician:
"""Uploads a new headshot for a musician and updates the database with the new public ID.
"""
Uploads a new headshot image for a musician and returns the updated musician object.
Args:
musician (Musician): The musician object to update
file (UploadFile): The new headshot file
Raises:
HTTPException: If the file is not an image or exceeds the maximum file size (status code 400)
Returns:
Musician: The updated Musician object
:param Musician musician: The musician object to update
:param UploadFile file: The new headshot file
:raises HTTPException: If any error occurs during the upload process (status code 500)
:return Musician: The updated Musician object
"""
image_file = self.verify_image(file)
data = uploader.upload(image_file)

View File

@@ -10,26 +10,25 @@ from app.models.user import User
class UserController(BaseController):
"""
Handles all user-related operations and serves as an intermediate controller between
the main controller and the model layer.
Handles all user-related operations.
Inherits from BaseController, which provides logging and other generic methods.
Testing: pass a mocked UserQueries object to the constructor.
"""
def __init__(self, user_queries=user_queries) -> None:
def __init__(self, user_queries: UserQueries = user_queries) -> None:
"""
Initializes the UserController with a UserQueries object.
:param UserQueries user_queries: object for querying user data, defaults to user_queries
"""
super().__init__()
self.db: UserQueries = user_queries
def get_users(self) -> list[User]:
"""Retrieves all users from the database and returns them as a list of User objects.
"""
Retrieves all users and returns them as a list.
Raises:
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
list[User]: A list of User objects which are suitable for a response body
:raises HTTPException: If any error occurs during the retrieval process (status code 500)
:return list[User]: A list of User objects suitable for a response body
"""
data = self.db.select_all_series()
try:
@@ -41,17 +40,13 @@ class UserController(BaseController):
)
def get_user_by_id(self, user_id: int) -> User:
"""Retrieves a single user from the database and returns it as a User object.
"""
Retrieves a single user from the database and returns it as a User object.
Args:
user_id (int): The ID of the user to retrieve
Raises:
HTTPException: If the user is not found (status code 404)
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
User: A User object which is suitable for a response body
:param int user_id: The ID of the user to retrieve
:raises HTTPException: If the user is not found (status code 404)
:raises HTTPException: If any error occurs during the retrieval process (status code 500)
:return User: A User object which is suitable for a response body
"""
if (data := self.db.select_one_by_id(user_id)) is None:
raise HTTPException(
@@ -66,17 +61,13 @@ class UserController(BaseController):
)
def get_user_by_email(self, email: str) -> User:
"""Retrieves a single user from the database and returns it as a User object.
"""
Retrieves a single user from the database and returns it as a User object.
Args:
email (str): The email of the user to retrieve
Raises:
HTTPException: If the user is not found (status code 404)
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
User: A User object which is suitable for a response body
:param str email: The email of the user to retrieve
:raises HTTPException: If the user is not found (status code 404)
:raises HTTPException: If any error occurs during the retrieval process (status code 500)
:return User: A User object which is suitable for a response body
"""
if (data := self.db.select_one_by_email(email)) is None:
raise HTTPException(
@@ -91,17 +82,13 @@ class UserController(BaseController):
)
def get_user_by_sub(self, sub: str) -> User:
"""Retrieves a single user from the database and returns it as a User object.
"""
Retrieves a single user from the database and returns it as a User object.
Args:
sub (str): The sub of the user to retrieve
Raises:
HTTPException: If the user is not found (status code 404)
HTTPException: If any error occurs during the retrieval process (status code 500)
Returns:
User: A User object which is suitable for a response body
:param str sub: The sub of the user to retrieve
:raises HTTPException: If the user is not found (status code 404)
:raises HTTPException: If any error occurs during the retrieval process (status code 500)
:return User: A User object which is suitable for a response body
"""
if (data := self.db.select_one_by_sub(sub)) is None:
raise HTTPException(
@@ -116,13 +103,11 @@ class UserController(BaseController):
)
def create_user(self, token: HTTPAuthorizationCredentials) -> User:
"""Updates a user's sub in the database and creates a new User object.
"""
Creates a new user in the database and returns the created User object.
Args:
token (HTTPAuthorizationCredentials): The token containing the user's email and sub
Returns:
User: A User object which is suitable for a response body
:param HTTPAuthorizationCredentials token: The OAuth token
:return User: The created User object which is suitable for a response body
"""
email, sub = oauth_token.email_and_sub(token)
user: User = self.get_user_by_email(email)

View File

@@ -6,10 +6,18 @@ from pydantic import BaseModel, HttpUrl
class Poster(BaseModel):
"""
Represents a poster image file for an EventSeries object.
"""
file: UploadFile
class NewEvent(BaseModel):
"""
Represents a new event object as received from the client.
"""
location: str
time: datetime
map_url: Optional[HttpUrl] = None
@@ -17,16 +25,28 @@ class NewEvent(BaseModel):
class Event(NewEvent):
"""
Represents an existing event object to be returned to the client.
"""
event_id: int
class NewEventSeries(BaseModel):
"""
Represents a new event series object as received from the client.
"""
name: str
description: str
events: list[NewEvent]
class EventSeries(NewEventSeries):
"""
Represents an existing event series object to be returned to the client.
"""
series_id: int
events: list[Event]
poster_id: Optional[str] = None

View File

@@ -12,7 +12,6 @@ router = APIRouter(
@router.post("/", status_code=status.HTTP_201_CREATED)
async def post_message(contact: Contact):
"""Sends an email to the site owner with the provided name, email, and message."""
subject = f"New message from {contact.name}"
body = f"From: {contact.email}\n\n{contact.message}"
send_email(subject, body)