restructure

This commit is contained in:
matthias@matsewe.de
2024-05-22 19:44:24 +02:00
parent 32d2170a1b
commit f6016f5736
11 changed files with 195 additions and 115 deletions

67
app/crud.py Normal file
View File

@@ -0,0 +1,67 @@
import app.models as models
from sqlalchemy import func
def get_songs_and_vote_for_user(db, user_id) -> list[models.Song]:
votes = db.query(models.Vote).filter(models.Vote.user_id == user_id).subquery()
songs_and_votes = db.query(
models.Song, votes.c.vote
).filter(
models.Song.singable == True
).join(votes, isouter=True).filter().all()
return songs_and_votes
def get_all_songs_and_votes(db) -> dict[int, dict[int, int]]:
_v = db.query(models.Vote.song_id, models.Vote.vote, func.count(models.Vote.song_id)).group_by(models.Vote.song_id, models.Vote.vote).all()
votes = {}
for v in _v:
if v[0] not in votes:
votes[v[0]] = {-1 : 0, 0 : 0, 1 : 0}
votes[v[0]][v[1]] = v[2]
return votes
def create_song(db,
og_artist,
aca_artist,
title,
url,
yt_id,
spfy_id,
thumbnail,
is_aca,
arng_url,
categories,
main_category,
singable
):
s = models.Song(og_artist=og_artist,
aca_artist=aca_artist,
title=title,
url=url,
yt_id=yt_id,
spfy_id=spfy_id,
thumbnail=thumbnail,
is_aca=is_aca,
arng_url=arng_url,
categories=categories,
main_category=main_category,
singable=singable)
db.add(s)
db.commit()
def create_or_update_vote(db, song_id, user_id, vote):
vote_entry = db.query(models.Vote).filter(
(models.Vote.user_id == user_id) & (models.Vote.song_id == song_id)).first()
if vote_entry:
vote_entry.vote = str(vote) # type: ignore
else:
vote_entry = models.Vote(song_id=song_id, user_id=user_id, vote=vote)
db.add(vote_entry)
db.commit()

25
app/database.py Normal file
View File

@@ -0,0 +1,25 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, DeclarativeBase
from sqlalchemy.types import PickleType
import os
SQLALCHEMY_DATABASE_URL = "sqlite:///" + os.environ.get("DATABASE_URL", "/data/db.sqlite")
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
async def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
class Base(DeclarativeBase):
type_annotation_map = {
dict[str, bool]: PickleType
}

View File

@@ -1,6 +0,0 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///db.sqlite')
Session = sessionmaker(engine)
session = Session()

View File

@@ -1,11 +1,13 @@
from fastapi import FastAPI, Request
from fastapi import FastAPI, Request, Depends
from app.routers import admin, user, songs
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from app.dependencies import engine
from app.sql_models import Base
from app.routers.songs import get_songs
from app.database import engine, Base, get_db
from app.crud import get_songs_and_vote_for_user
from sqlalchemy.orm import Session
from typing import Annotated
from app.schemas import Song
Base.metadata.create_all(engine)
@@ -20,13 +22,14 @@ app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def root(request: Request, session_id : str = ""):
async def root(request: Request, session_id : str = "", db: Annotated[Session, Depends(get_db)] = None):
if session_id == "":
return templates.TemplateResponse(
request=request, name="landing.html"
)
else:
songs = await get_songs(session_id)
songs = [Song(**s.__dict__, vote=v) for s, v in get_songs_and_vote_for_user(db, session_id)]
songs_by_category = {}
all_categories = set()
for song in songs:

View File

@@ -1,26 +1,35 @@
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
from sqlalchemy import Integer, ForeignKey
from sqlalchemy.sql import func
from typing import Optional
from pydantic import BaseModel
from datetime import datetime
class GoogleFile(BaseModel):
file_id: str
file_name: str
class Genre(BaseModel):
genre_id: Optional[int]
genre_name: str
class Song(Base):
__tablename__ = 'songs'
id: Mapped[int] = mapped_column(primary_key=True)
og_artist: Mapped[Optional[str]]
aca_artist: Mapped[Optional[str]]
title: Mapped[Optional[str]]
url: Mapped[Optional[str]]
yt_id: Mapped[Optional[str]]
spfy_id: Mapped[Optional[str]]
thumbnail: Mapped[Optional[str]]
is_aca: Mapped[Optional[bool]]
arng_url: Mapped[Optional[str]]
categories: Mapped[Optional[dict[str, bool]]]
main_category: Mapped[Optional[str]]
singable: Mapped[Optional[bool]]
class Song(BaseModel):
id: int
og_artist: Optional[str]
aca_artist: Optional[str]
title: Optional[str]
url: Optional[str]
yt_id: Optional[str]
spfy_id: Optional[str]
thumbnail: Optional[str]
is_aca: bool
arng_url: Optional[str]
categories: dict[str, bool]
main_category: str
singable: bool
vote: Optional[int]
class Vote(Base):
__tablename__ = 'votes'
id: Mapped[int] = mapped_column(primary_key=True)
song_id: Mapped[int] = mapped_column(Integer, ForeignKey("songs.id"))
user_id: Mapped[int]
vote: Mapped[Optional[int]]
time_created: Mapped[datetime] = mapped_column(server_default=func.now())
time_updated: Mapped[Optional[datetime]
] = mapped_column(onupdate=func.now())

View File

@@ -1,17 +1,14 @@
from fastapi import APIRouter, Security
from app.sql_models import SqlSong
from app.dependencies import session
from app.dependencies import engine
from app.routers.user import get_current_user
import pandas as pd
import numpy as np
import re
import requests
import os
from app.sql_models import Base
from fastapi import APIRouter, Security, Depends
from sqlalchemy.orm import Session
from app.database import get_db, engine, Base
from app.routers.user import get_current_user
from app.crud import create_song
router = APIRouter(
prefix="/admin",
@@ -28,10 +25,11 @@ def get_main_category(categories) -> int:
else:
return np.argmax(categories != None, axis=0)
def get_youtube_id(url):
if url is None:
return None
youtube_regex = (
r'(https?://)?(www\.)?'
'(youtube|youtu|youtube-nocookie)\.(com|be)/'
@@ -43,10 +41,11 @@ def get_youtube_id(url):
return None
def get_thumbnail(url):
if url is None:
return "/static/cover.jpg"
m = get_youtube_id(url)
if m:
thumbnail_url = "https://img.youtube.com/vi/" + m + "/mqdefault.jpg"
@@ -56,6 +55,7 @@ def get_thumbnail(url):
else:
return "/static/cover.jpg"
def get_spotify_id(url):
if url is None:
return None
@@ -64,8 +64,9 @@ def get_spotify_id(url):
else:
return None
@router.post("/load_list")
async def create_upload_file():
async def create_upload_file(db: Session = Depends(get_db)):
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
@@ -83,12 +84,13 @@ async def create_upload_file():
spfy_id = get_spotify_id(row[3])
categories = {n: v for n, v in zip(
category_names, row[6:19] != None)}
category_names, row[6:19] != None)}
if not np.any(list(categories.values())):
continue
s = SqlSong(og_artist=row[0],
create_song(db,
og_artist=row[0],
aca_artist=row[1],
title=row[2],
url=row[3],
@@ -101,6 +103,3 @@ async def create_upload_file():
main_category=category_names[get_main_category(row[6:19])],
singable=row[19] != "nein"
)
session.add(s)
session.commit()

View File

@@ -1,39 +1,29 @@
from fastapi import APIRouter
from app.models import Song
from app.sql_models import SqlSong, SqlVote
from app.dependencies import session
from typing import Annotated
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
import app.models as models
from app.database import get_db
from app.schemas import Song
from app.crud import get_songs_and_vote_for_user, create_or_update_vote, get_all_songs_and_votes
router = APIRouter(
prefix="/songs",
#dependencies=[Security(get_current_user, scopes=["public"])],
# dependencies=[Security(get_current_user, scopes=["public"])],
responses={404: {"description": "Not found"}},
)
async def songify(s, votes):
return Song(**s.__dict__, vote=votes.get(s.id, None))
@router.get("/")
async def get_songs(user_id : str = "") -> list[Song]:
sqlsongs = session.query(SqlSong).filter(SqlSong.singable == True).all()
votes = session.query(SqlVote).filter(SqlVote.user_id == user_id).all()
votes = {v.song_id : v.vote for v in votes}
async def get_songs(user_id: str = "", db: Annotated[Session, Depends(get_db)] = None) -> list[Song]:
return [Song(**s.__dict__, vote=v) for s, v in get_songs_and_vote_for_user(db, user_id)]
songs = []
for s in sqlsongs:
try:
songs.append(Song(**s.__dict__, vote=votes.get(s.id, None)))
except:
print(s.__dict__)
pass
return songs
#return [Song(**s.__dict__, vote=votes.get(s.id, None)) for s in sqlsongs] # type: ignore
@router.post("/{song_id}/vote")
async def vote(song_id : str, user_id : str, vote : int):
vote_entry = session.query(SqlVote).filter((SqlVote.user_id == user_id) & (SqlVote.song_id == song_id)).first()
if vote_entry:
vote_entry.vote = str(vote) # type: ignore
else:
vote_entry = SqlVote(song_id=song_id, user_id=user_id, vote=vote)
session.add(vote_entry)
session.commit()
async def vote(song_id: str, user_id: str, vote: int, db: Annotated[Session, Depends(get_db)]):
create_or_update_vote(db, song_id, user_id, vote)
@router.get("/evaluation")
async def get_evaluation(db: Annotated[Session, Depends(get_db)] = None) -> dict[int, dict[int, int]]:
return get_all_songs_and_votes(db)

18
app/schemas.py Normal file
View File

@@ -0,0 +1,18 @@
from typing import Optional
from pydantic import BaseModel
class Song(BaseModel):
id: int
og_artist: Optional[str]
aca_artist: Optional[str]
title: Optional[str]
url: Optional[str]
yt_id: Optional[str]
spfy_id: Optional[str]
thumbnail: Optional[str]
is_aca: Optional[bool]
arng_url: Optional[str]
categories: Optional[dict[str, bool]]
main_category: Optional[str]
singable: Optional[bool]
vote: Optional[int]

View File

@@ -1,30 +0,0 @@
from sqlalchemy import Column, String, Integer, Boolean, PickleType
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class SqlSong(Base):
__tablename__ = 'songs'
id = Column(Integer, primary_key=True)
og_artist = Column(String)
aca_artist = Column(String)
title = Column(String)
url = Column(String)
yt_id = Column(String)
spfy_id = Column(String)
thumbnail = Column(String)
is_aca = Column(Boolean)
arng_url = Column(String)
categories = Column(PickleType)
main_category = Column(String)
singable = Column(Boolean)
class SqlVote(Base):
__tablename__ = 'votes'
id = Column(Integer, primary_key=True)
song_id = Column(Integer)
user_id = Column(String)
vote = Column(Integer, nullable=True)