initial commit for GitHub
This commit is contained in:
5
server/app/model/__init__.py
Normal file
5
server/app/model/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .albums import Album, Artist
|
||||
from .artwork import Artwork, Medium
|
||||
from .bio import Bio, ProfessionalService, SocialUrl
|
||||
from .quotes import Quote
|
||||
from .video import Video
|
||||
94
server/app/model/albums.py
Normal file
94
server/app/model/albums.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from typing import Optional, Type
|
||||
|
||||
from icecream import ic
|
||||
from pydantic import HttpUrl
|
||||
|
||||
from app.constants import ALBUMS_TABLE, ARTISTS_TABLE
|
||||
from app.model.model_object import ModelObject
|
||||
from app.model.response_object import ResponseObject
|
||||
|
||||
|
||||
class Artist(ModelObject, ResponseObject):
|
||||
artist_name: str
|
||||
artist_url: Optional[HttpUrl] = None
|
||||
|
||||
|
||||
class Album(ModelObject, ResponseObject):
|
||||
album_name: str
|
||||
release_year: int
|
||||
artist: Artist
|
||||
front_artwork_url: HttpUrl
|
||||
spotify_url: Optional[HttpUrl] = None
|
||||
itunes_url: Optional[HttpUrl] = None
|
||||
bandcamp_url: Optional[HttpUrl] = None
|
||||
apple_music_url: Optional[HttpUrl] = None
|
||||
rear_artwork_url: Optional[HttpUrl] = None
|
||||
bandcamp_player: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def select_one(
|
||||
cls, album_id: int, table_name: str = ALBUMS_TABLE
|
||||
) -> "Album | None":
|
||||
cursor, conn = cls._get_cursor_and_conn()
|
||||
cursor.execute(
|
||||
f"""-- sql
|
||||
SELECT
|
||||
al.id,
|
||||
al.album_name ,
|
||||
al.release_year,
|
||||
al.artist_id ,
|
||||
al.spotify_url ,
|
||||
al.itunes_url ,
|
||||
al.bandcamp_url ,
|
||||
al.apple_music_url ,
|
||||
al.front_artwork_url ,
|
||||
al.rear_artwork_url ,
|
||||
al.bandcamp_player ,
|
||||
ar.artist_name ,
|
||||
ar.artist_url
|
||||
FROM {table_name} al
|
||||
LEFT JOIN {ARTISTS_TABLE} ar
|
||||
ON al.artist_id = ar.id
|
||||
WHERE al.id = {album_id};
|
||||
"""
|
||||
)
|
||||
data: dict = cursor.fetchone() # type: ignore
|
||||
cls._close_cursor_and_conn(cursor, conn)
|
||||
return cls._construct(cls, data) if data else None
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, table_name: str = ALBUMS_TABLE) -> list["Album"]:
|
||||
cursor, conn = cls._get_cursor_and_conn()
|
||||
cursor.execute(
|
||||
f"""-- sql
|
||||
SELECT
|
||||
al.id,
|
||||
al.album_name ,
|
||||
al.release_year,
|
||||
al.artist_id ,
|
||||
al.spotify_url ,
|
||||
al.itunes_url ,
|
||||
al.bandcamp_url ,
|
||||
al.apple_music_url ,
|
||||
al.front_artwork_url ,
|
||||
al.rear_artwork_url ,
|
||||
al.bandcamp_player ,
|
||||
ar.artist_name ,
|
||||
ar.artist_url
|
||||
FROM {table_name} al
|
||||
LEFT JOIN {ARTISTS_TABLE} ar
|
||||
ON al.artist_id = ar.id;
|
||||
"""
|
||||
)
|
||||
data: dict = cursor.fetchall() # type: ignore
|
||||
cls._close_cursor_and_conn(cursor, conn)
|
||||
return sorted(
|
||||
[cls._construct(cls, row) for row in data],
|
||||
key=lambda album: album.release_year,
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _construct(cls, Obj: Type, data: dict) -> "Album":
|
||||
data["artist"] = Artist(**data)
|
||||
return Obj(**data)
|
||||
11
server/app/model/articles.py
Normal file
11
server/app/model/articles.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
|
||||
class Article(BaseModel):
|
||||
id: int
|
||||
article_title: str
|
||||
body: str
|
||||
is_featured: Optional[bool] = False
|
||||
video_url: Optional[HttpUrl] = None
|
||||
85
server/app/model/artwork.py
Normal file
85
server/app/model/artwork.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from typing import Optional, Type
|
||||
|
||||
from pydantic import HttpUrl
|
||||
|
||||
from app.constants import ART_MEDIUM_TABLE, ARTWORK_TABLE
|
||||
from app.model.model_object import ModelObject
|
||||
from app.model.response_object import ResponseObject
|
||||
|
||||
|
||||
class Medium(ModelObject, ResponseObject):
|
||||
medium_name: str
|
||||
|
||||
|
||||
class Artwork(ModelObject, ResponseObject):
|
||||
artwork_name: str
|
||||
source: HttpUrl
|
||||
thumbnail: HttpUrl
|
||||
is_featured: bool = False
|
||||
medium: Optional[Medium] = None
|
||||
release_year: Optional[int] = None
|
||||
size: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def select_one(
|
||||
cls, artwork_id: int, table_name: str = ARTWORK_TABLE
|
||||
) -> "Artwork | None":
|
||||
cursor, conn = cls._get_cursor_and_conn()
|
||||
cursor.execute(
|
||||
f"""-- sql
|
||||
SELECT
|
||||
a.id,
|
||||
a.medium_id ,
|
||||
a.artwork_name ,
|
||||
a.source ,
|
||||
a.thumbnail ,
|
||||
a.is_featured ,
|
||||
a.release_year ,
|
||||
a.`size` ,
|
||||
m.id as medium_id,
|
||||
m.medium_name
|
||||
FROM {table_name} a
|
||||
LEFT JOIN {ART_MEDIUM_TABLE} m ON a.medium_id = m.id
|
||||
WHERE a.id = {artwork_id}
|
||||
"""
|
||||
)
|
||||
row: dict = cursor.fetchone() # type: ignore
|
||||
cls._close_cursor_and_conn(cursor, conn)
|
||||
return cls._construct(cls, row) if row else None
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, table_name: str = ARTWORK_TABLE) -> list["Artwork"]:
|
||||
cursor, conn = cls._get_cursor_and_conn()
|
||||
cursor.execute(
|
||||
f"""-- sql
|
||||
SELECT
|
||||
a.id,
|
||||
a.medium_id ,
|
||||
a.artwork_name ,
|
||||
a.source ,
|
||||
a.thumbnail ,
|
||||
a.is_featured ,
|
||||
a.release_year ,
|
||||
a.`size` ,
|
||||
m.id as medium_id,
|
||||
m.medium_name
|
||||
FROM {table_name} a
|
||||
LEFT JOIN {ART_MEDIUM_TABLE} m ON a.medium_id = m.id
|
||||
"""
|
||||
)
|
||||
rows: list[dict] = cursor.fetchall() # type: ignore
|
||||
cls._close_cursor_and_conn(cursor, conn)
|
||||
return sorted(
|
||||
[cls._construct(cls, row) for row in rows],
|
||||
key=lambda a: (a.is_featured, a.release_year if a.release_year else 0),
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _construct(cls, Obj: Type, row: dict) -> "Artwork":
|
||||
row["medium"] = (
|
||||
Medium(id=row["medium_id"], medium_name=row["medium_name"])
|
||||
if row.get("medium_id")
|
||||
else None
|
||||
)
|
||||
return Obj(**row)
|
||||
62
server/app/model/bio.py
Normal file
62
server/app/model/bio.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from pydantic import HttpUrl
|
||||
|
||||
from app.constants import BIO_CONTENT_TABLE, SERVICES_TABLE, SOCIAL_TABLE
|
||||
from app.model.model_object import ModelObject
|
||||
from app.model.response_object import ResponseObject
|
||||
|
||||
|
||||
class ProfessionalService(ModelObject, ResponseObject):
|
||||
service_name: str
|
||||
|
||||
@classmethod
|
||||
def select_all(
|
||||
cls, table_name: str = SERVICES_TABLE
|
||||
) -> list["ProfessionalService"]:
|
||||
return [
|
||||
cls._construct(ProfessionalService, row)
|
||||
for row in super().select_all(table_name)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def select_one(
|
||||
cls, obj_id: int, table_name: str = SERVICES_TABLE
|
||||
) -> "ProfessionalService | None":
|
||||
return cls._construct(
|
||||
ProfessionalService, super().select_one(obj_id, table_name)
|
||||
)
|
||||
|
||||
|
||||
class SocialUrl(ModelObject, ResponseObject):
|
||||
social_name: str
|
||||
social_url: HttpUrl
|
||||
|
||||
@classmethod
|
||||
def select_one(
|
||||
cls, obj_id: int, table_name: str = SOCIAL_TABLE
|
||||
) -> "SocialUrl | None":
|
||||
return cls._construct(SocialUrl, super().select_one(obj_id, table_name))
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, table_name: str = SOCIAL_TABLE) -> list["SocialUrl"]:
|
||||
return [
|
||||
cls._construct(SocialUrl, row) for row in super().select_all(table_name)
|
||||
]
|
||||
|
||||
|
||||
class Bio(ModelObject, ResponseObject):
|
||||
name: str
|
||||
bio: str
|
||||
social_urls: list[SocialUrl]
|
||||
|
||||
@classmethod
|
||||
def select_one(cls, table_name: str = BIO_CONTENT_TABLE) -> "Bio":
|
||||
bio_data = super().select_one(1, table_name)
|
||||
bio_content = bio_data.get("content", "") if bio_data else ""
|
||||
socials = SocialUrl.select_all()
|
||||
bio = Bio(bio=bio_content, social_urls=socials, name="Megan Johns")
|
||||
del bio.id
|
||||
return bio
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, **args) -> None:
|
||||
raise NotImplemented
|
||||
59
server/app/model/model_object.py
Normal file
59
server/app/model/model_object.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from typing import Any, Optional, Type
|
||||
|
||||
from icecream import ic
|
||||
from mysql.connector.connection import MySQLConnection
|
||||
from mysql.connector.cursor import MySQLCursor
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.db.conn import connect_db
|
||||
|
||||
|
||||
class ModelObject:
|
||||
@classmethod
|
||||
def select_one(cls, obj_id: int, table_name: str = "") -> dict | None:
|
||||
if not table_name:
|
||||
raise Exception(
|
||||
"table_name cannot be an empty string. Check default arguments."
|
||||
)
|
||||
cursor, conn = cls._get_cursor_and_conn()
|
||||
cursor.execute(
|
||||
f"""-- sql
|
||||
SELECT *
|
||||
FROM {table_name}
|
||||
WHERE id = {obj_id};
|
||||
"""
|
||||
)
|
||||
data: dict[Any, Any] | None = cursor.fetchone() # type: ignore
|
||||
cls._close_cursor_and_conn(cursor, conn)
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, table_name: str = "") -> list[dict]:
|
||||
if not table_name:
|
||||
raise Exception(
|
||||
"table_name cannot be an empty string. Check default arguments."
|
||||
)
|
||||
cursor, conn = cls._get_cursor_and_conn()
|
||||
cursor.execute(
|
||||
f"""-- sql
|
||||
SELECT *
|
||||
FROM {table_name};
|
||||
"""
|
||||
)
|
||||
data: list[dict] = cursor.fetchall() # type: ignore
|
||||
cls._close_cursor_and_conn(cursor, conn)
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _get_cursor_and_conn(cls) -> tuple[MySQLCursor, MySQLConnection]:
|
||||
conn = connect_db()
|
||||
return conn.cursor(dictionary=True), conn
|
||||
|
||||
@classmethod
|
||||
def _close_cursor_and_conn(cls, cursor: MySQLCursor, conn: MySQLConnection) -> None:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
@classmethod
|
||||
def _construct(cls, Obj: Type, data: dict | None) -> Any:
|
||||
return Obj(**data) if data is not None else None
|
||||
22
server/app/model/quotes.py
Normal file
22
server/app/model/quotes.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import Optional, Type
|
||||
|
||||
from icecream import ic
|
||||
from pydantic import HttpUrl
|
||||
|
||||
from app.constants import QUOTES_TABLE
|
||||
from app.model.model_object import ModelObject
|
||||
from app.model.response_object import ResponseObject
|
||||
|
||||
|
||||
class Quote(ModelObject, ResponseObject):
|
||||
body: str
|
||||
author: str
|
||||
source: Optional[HttpUrl] = None
|
||||
|
||||
@classmethod
|
||||
def select_one(cls, obj_id: int, table_name: str = QUOTES_TABLE) -> "Quote | None":
|
||||
return cls._construct(cls, super().select_one(obj_id, table_name))
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, table_name: str = QUOTES_TABLE) -> list["Quote"]:
|
||||
return [cls._construct(cls, row) for row in super().select_all(table_name)]
|
||||
7
server/app/model/response_object.py
Normal file
7
server/app/model/response_object.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ResponseObject(BaseModel):
|
||||
id: Optional[int] = None
|
||||
25
server/app/model/video.py
Normal file
25
server/app/model/video.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from typing import Optional, Type
|
||||
|
||||
from pydantic import HttpUrl
|
||||
|
||||
from app.constants import VIDEOS_TABLE
|
||||
from app.model.model_object import ModelObject
|
||||
from app.model.response_object import ResponseObject
|
||||
|
||||
|
||||
class Video(ModelObject, ResponseObject):
|
||||
title: str
|
||||
subtitle: str
|
||||
description: str # html
|
||||
source: HttpUrl
|
||||
embedded_player_iframe: str # an iframe from YouTube/Vimeo
|
||||
website: Optional[HttpUrl] = None
|
||||
|
||||
@classmethod
|
||||
def select_one(cls, obj_id: int, table_name: str = VIDEOS_TABLE) -> "Video | None":
|
||||
data = super().select_one(obj_id, table_name)
|
||||
return cls._construct(cls, data)
|
||||
|
||||
@classmethod
|
||||
def select_all(cls, table_name: str = VIDEOS_TABLE) -> list["Video"]:
|
||||
return [cls._construct(cls, row) for row in super().select_all(table_name)]
|
||||
Reference in New Issue
Block a user