initial commit

This commit is contained in:
Lucas Jensen
2024-05-01 09:19:01 -07:00
commit 5d67c0c2b2
117 changed files with 9917 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
from app.controllers.controller import Controller
controller = Controller()

View File

@@ -0,0 +1,28 @@
from fastapi import HTTPException, UploadFile, status
from app.db.base_queries import BaseQueries
ALLOWED_FILES_TYPES = ["image/jpeg", "image/png"]
MAX_FILE_SIZE = 1000000 # 1 MB
class BaseController:
def __init__(self) -> None:
self.db: BaseQueries = None # type: ignore
self.ALL_FILES = ALLOWED_FILES_TYPES
self.MAX_FILE_SIZE = MAX_FILE_SIZE
async def verify_image(self, file: UploadFile) -> bytes:
print("verifying image")
if file.content_type not in self.ALL_FILES:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"File type {file.content_type} not allowed. Allowed file types are {self.ALL_FILES}",
)
image_file = await file.read()
if len(image_file) > self.MAX_FILE_SIZE:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"File size {len(image_file)} bytes exceeds maximum of {self.MAX_FILE_SIZE} bytes",
)
return image_file

View File

@@ -0,0 +1,99 @@
from fastapi import HTTPException, UploadFile, status
from fastapi.security import HTTPAuthorizationCredentials
from icecream import ic
from app.admin import oauth_token
from app.controllers.events import EventController
from app.controllers.group import GroupController
from app.controllers.musicians import MusicianController
from app.controllers.users import UserController
from app.models.event import EventSeries, NewEventSeries
from app.models.group import Group
from app.models.musician import Musician
from app.models.user import User
class Controller:
def __init__(self) -> None:
self.event_controller = EventController()
self.musician_controller = MusicianController()
self.user_controller = UserController()
self.group_controller = GroupController()
async def get_musicians(self) -> list[Musician]:
return await self.musician_controller.get_musicians()
async def get_musician(self, id: int) -> Musician:
return await self.musician_controller.get_musician(id)
async def update_musician(
self,
musician: Musician,
url_param_id: int,
token: HTTPAuthorizationCredentials,
file: UploadFile | None = None,
) -> Musician:
if musician.id != url_param_id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="ID in URL does not match ID in request body",
)
_, sub = oauth_token.email_and_sub(token)
await self.user_controller.get_user_by_sub(sub)
return await self.musician_controller.update_musician(
musician_id=musician.id,
new_bio=musician.bio,
file=file,
)
async def get_events(self) -> list[EventSeries]:
return await self.event_controller.get_all_series()
async def get_event(self, id: int) -> EventSeries:
return await self.event_controller.get_one_series(id)
async def create_event(
self, series: NewEventSeries, token: HTTPAuthorizationCredentials
) -> EventSeries:
_, sub = oauth_token.email_and_sub(token)
await self.user_controller.get_user_by_sub(sub)
return await self.event_controller.create_series(series)
async def add_series_poster(
self, series_id: int, poster: UploadFile, token: HTTPAuthorizationCredentials
) -> EventSeries:
_, sub = oauth_token.email_and_sub(token)
await self.user_controller.get_user_by_sub(sub)
return await self.event_controller.add_series_poster(series_id, poster)
async def delete_series(self, id: int, token: HTTPAuthorizationCredentials) -> None:
_, sub = oauth_token.email_and_sub(token)
await self.user_controller.get_user_by_sub(sub)
await self.event_controller.delete_series(id)
async def update_series(
self, route_id: int, series: EventSeries, token: HTTPAuthorizationCredentials
) -> EventSeries:
_, sub = oauth_token.email_and_sub(token)
await self.user_controller.get_user_by_sub(sub)
return await self.event_controller.update_series(route_id, series)
async def get_users(self) -> list[User]:
return await self.user_controller.get_users()
async def get_user(self, id: int) -> User:
return await self.user_controller.get_user_by_id(id)
async def create_user(self, token: HTTPAuthorizationCredentials) -> User:
return await self.user_controller.create_user(token)
async def get_group(self) -> Group:
return await self.group_controller.get_group()
async def update_group_bio(
self, bio: str, token: HTTPAuthorizationCredentials
) -> Group:
_, sub = oauth_token.email_and_sub(token)
await self.user_controller.get_user_by_sub(sub)
return await self.group_controller.update_group_bio(bio)

View File

@@ -0,0 +1,106 @@
from fastapi import HTTPException, UploadFile, status
from icecream import ic
from mysql.connector.errors import IntegrityError
from app.admin.images import uploader
from app.controllers.base_controller import BaseController
from app.db import event_queries
from app.db.events import EventQueries
from app.models.event import Event, EventSeries, NewEventSeries
class EventController(BaseController):
def __init__(self) -> None:
super().__init__()
self.db: EventQueries = event_queries
def _all_series(self, data: list[dict]) -> list[EventSeries]:
all_series: dict[str, EventSeries] = {}
for event_series_row in data:
series_name: str = event_series_row["name"]
event = Event(**event_series_row)
if series_name not in all_series:
all_series[series_name] = EventSeries(**event_series_row, events=[])
all_series[series_name].events.append(event)
return [series for series in all_series.values()]
async def get_all_series(self) -> list[EventSeries]:
data = await self.db.get_all()
try:
return self._all_series(data)
except Exception as e:
ic(e)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error retrieving event objects: {e}",
)
async def get_one_series(self, id: int) -> EventSeries:
if not (data := await self.db.get_one(id)):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Event not found"
)
try:
event = EventSeries(
**data[0], events=[Event(**e) for e in data if e.get("event_id")]
)
return event
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating event object: {e}",
)
async def create_series(self, series: NewEventSeries) -> EventSeries:
try:
inserted_id = await self.db.insert_one_series(series)
for new_event in series.events:
await self.db.insert_one_event(new_event, inserted_id)
return await self.get_one_series(inserted_id)
except IntegrityError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Series name already exists. Each series must have a unique name.\n{e}",
)
async def add_series_poster(self, series_id, poster: UploadFile) -> EventSeries:
series = await self.get_one_series(series_id)
series.poster_id = await self._upload_poster(poster)
await self.db.update_series_poster(series)
return await self.get_one_series(series.series_id)
async def _upload_poster(self, poster: UploadFile) -> str:
image_file = await self.verify_image(poster)
try:
data = uploader.upload(image_file)
return data.get("public_id")
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error uploading image: {e}",
)
async def delete_series(self, id: int) -> None:
series = await self.get_one_series(id)
await self.db.delete_one_series(series)
async def update_series(self, route_id: int, series: EventSeries) -> EventSeries:
if route_id != series.series_id:
print("error")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="ID in URL does not match ID in request body",
)
prev_series = await self.get_one_series(series.series_id)
if series.poster_id != prev_series.poster_id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Poster ID cannot be updated directly. Use the /poster endpoint instead.",
)
await self.db.delete_events_by_series(series)
await self.db.replace_series(series)
for event in series.events:
await self.db.insert_one_event(event, series.series_id)
return await self.get_one_series(series.series_id)

View File

@@ -0,0 +1,35 @@
from fastapi import HTTPException, status
from app.controllers.base_controller import BaseController
from app.db import group_queries
from app.db.group import GroupQueries
from app.models.group import Group
class GroupController(BaseController):
def __init__(self) -> None:
super().__init__()
self.db: GroupQueries = group_queries
async def get_group(self) -> Group:
if (data := await self.db.get_one()) is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Group not found"
)
try:
return Group(**data)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating group object: {e}",
)
async def update_group_bio(self, bio: str) -> Group:
try:
await self.db.update_group_bio(bio)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error updating group bio: {e}",
)
return await self.get_group()

View File

@@ -0,0 +1,89 @@
from fastapi import HTTPException, UploadFile, status
from icecream import ic
from app.admin.images import uploader
from app.controllers.base_controller import BaseController
from app.db import musician_queries
from app.db.musicians import MusicianQueries
from app.models.musician import Musician
class MusicianController(BaseController):
def __init__(self) -> None:
super().__init__()
self.db: MusicianQueries = musician_queries
async def get_musicians(self) -> list[Musician]:
data = await self.db.get_all()
try:
return [Musician(**m) for m in data]
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating musician objects: {e}",
)
async def get_musician(self, id: int) -> Musician:
if (data := await self.db.get_one(id)) is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Musician not found"
)
try:
return Musician(**data)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating musician object: {e}",
)
async def update_musician(
self,
musician_id: int,
new_bio: str,
file: UploadFile | None = None,
) -> Musician:
musician = await self.get_musician(musician_id)
if new_bio != musician.bio:
return await self.update_musician_bio(musician.id, new_bio)
if file is not None:
return await self.upload_headshot(musician.id, file)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Update operation not implemented. Neither the bio or headshot was updated.",
)
async def update_musician_headshot(self, id: int, headshot_id: str) -> Musician:
await self.get_musician(id)
try:
await self.db.update_headshot(id, headshot_id)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error updating musician headshot: {e}",
)
return await self.get_musician(id)
async def update_musician_bio(self, id: int, bio: str) -> Musician:
await self.get_musician(id) # Check if musician exists
try:
await self.db.update_bio(id, bio)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error updating musician bio: {e}",
)
return await self.get_musician(id)
async def upload_headshot(self, id: int, file: UploadFile) -> Musician:
image_file = await self.verify_image(file)
data = uploader.upload(image_file)
public_id = data.get("public_id")
if public_id is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to upload image",
)
await self.update_musician_headshot(id, public_id)
return await self.get_musician(id)

View File

@@ -0,0 +1,70 @@
from fastapi import HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials
from app.admin import oauth_token
from app.controllers.base_controller import BaseController
from app.db import user_queries
from app.db.users import UserQueries
from app.models.user import User
class UserController(BaseController):
def __init__(self) -> None:
super().__init__()
self.db: UserQueries = user_queries
async def get_users(self) -> list[User]:
data = await self.db.get_all()
try:
return [User(**e) for e in data]
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating user objects: {e}",
)
async def get_user_by_id(self, id: int) -> User:
if (data := await self.db.get_one(id)) is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
)
try:
return User(**data)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating user object: {e}",
)
async def get_user_by_email(self, email: str) -> User:
if (data := await self.db.get_one_by_email(email)) is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="User does not exist"
)
try:
return User(**data)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating user object: {e}",
)
async def get_user_by_sub(self, sub: str) -> User:
if (data := await self.db.get_one_by_sub(sub)) is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
)
try:
return User(**data)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating user object: {e}",
)
async def create_user(self, token: HTTPAuthorizationCredentials) -> User:
email, sub = oauth_token.email_and_sub(token)
user: User = await self.get_user_by_email(email)
if user.sub is None:
await self.db.update_sub(user.email, sub)
return await self.get_user_by_sub(sub)