Skip to content

Commit

Permalink
Revert "Revert "Add highscore to root page using htmx (#24)" (#27)"
Browse files Browse the repository at this point in the history
This reverts commit 2572cb8.
  • Loading branch information
MauroLuzzatto authored Feb 10, 2024
1 parent 2572cb8 commit 33b8a3c
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 109 deletions.
27 changes: 27 additions & 0 deletions app/core/highscore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
secret_key = "highscore"


class Highscore:
def __init__(self, request):
self.session = request.session
scores = self.session.get(secret_key)

if not scores:
# save an empty scores dict in the session
scores = self.session[secret_key] = {}

self.scores = scores

def add_score(self, id, song, artist, prediction):
if id not in self.scores:
self.scores[id] = {
"song": song,
"artist": artist,
"prediction": prediction,
}

def get_highscore(self):
highscore = sorted(
self.scores.values(), key=lambda x: x["prediction"], reverse=True
)[:5]
return highscore
84 changes: 54 additions & 30 deletions app/routers/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

from app.core.highscore import Highscore
from app.utils.song_request_form import SongRequestForm
from music_flow.core.utils import path_app

Expand All @@ -18,7 +19,7 @@
erros = {
"song_not_found": "Song not found.",
"generic_error": "OPS, Something went wrong. Please try again.",
"failed_to_fetch_song": "Failed to fetch song. Please check for typos or try another one.",
"failed_to_fetch_song": "Failed to fetch song. Please check for typos or try another song.",
}


Expand All @@ -27,11 +28,24 @@

@router.get("/", response_class=HTMLResponse)
async def get_form(request: Request):
return templates.TemplateResponse("prediction.html", {"request": request})
highscore = Highscore(request)

payload = {
"request": request,
"scores": highscore.get_highscore(),
}

@router.post("/", response_class=HTMLResponse)
async def post_form(request: Request):
template = "prediction.html"
return templates.TemplateResponse(template, payload)


@router.get("/about/", response_class=HTMLResponse)
async def get_about(request: Request):
return templates.TemplateResponse("about.html", {"request": request})


@router.post("/search_song")
async def get_success_endpoint(request: Request):
"""_summary_
Args:
Expand All @@ -41,38 +55,48 @@ async def post_form(request: Request):
_type_: _description_
"""

template = "partials/songform.html"

form = SongRequestForm(request)
await form.load_data()
logger.debug(f"form: {form.__dict__}")

if not form.is_valid():
payload = form.as_dict()
return templates.TemplateResponse(template, payload)

logger.debug(f"form: {form.as_dict()}")

from main import get_prediction_api

try:
output = await get_prediction_api(song=form.song, artist=form.artist) # type: ignore
except HTTPException:
form.errors.append(erros["failed_to_fetch_song"])
return templates.TemplateResponse("prediction.html", form.__dict__)

if form.is_valid():
header = f"{form.song.capitalize()} by {form.artist.capitalize()}" # type: ignore
prediction = output.dict()

payload = {
"request": request,
"header": header,
"prediction": prediction,
}

try:
return templates.TemplateResponse("success.html", payload)
except Exception as e:
form.errors.append(erros["generic_error"])
logger.error(e)
return templates.TemplateResponse("prediction.html", form.__dict__)

return templates.TemplateResponse("prediction.html", form.__dict__)


@router.get("/about/", response_class=HTMLResponse)
async def get_about(request: Request):
return templates.TemplateResponse("about.html", {"request": request})
payload = form.as_dict()
del payload["song"]
del payload["artist"]
return templates.TemplateResponse(template, payload)

header = f'"{form.song.capitalize()}" by "{form.artist.capitalize()}"' # type: ignore
response = output.dict()

highscore = Highscore(request)
song = form.song
artist = form.artist
id = header
_prediction = response["prediction"]
highscore.add_score(id, song, artist, _prediction)

payload = {
"request": request,
"header": header,
"prediction": response,
"scores": highscore.get_highscore(),
}
try:
template = "partials/success.html"
return templates.TemplateResponse(template, payload)
except Exception as e:
form.errors.append(erros["generic_error"])
logger.error(e)
return templates.TemplateResponse(template, payload)
41 changes: 41 additions & 0 deletions app/templates/partials/songform.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<div class="container-fluid my-5 mx-5" style="height: 18em;" id="songForm">

<h3 class="text-left display-5">Let's predict the number of streams for a song</h3>

<div class="text-danger font-weight-bold">
{% for error in errors %}
<li>{{error}}</li>
{% endfor %}
</div>


<h4 class="text-left display-7">Choose a song!</h4>



<form id="myForm" action="/search_song" method="POST" hx-boost="true" hx-trigger="submit" hx-swap="outerHTML"
hx-target="#songForm">

<div class="mb-3">
<input type="text" required placeholder="Choose an artist e.g Queen" name="artist" value="{{artist}}"
class="form-control">
</div>

<div class="mb-3">
<input type="text" required placeholder="Choose a song e.g Don't stop me now" name="song" value="{{song}}"
class="form-control">
</div>

<div class="class text-center">
<button type="submit" hx-trigger="submit">Search</button>
<!-- <button type="submit" hx-trigger="click" hx-get="/success_endpoint" hx-target="#songForm"
hx-swap="innerHTML">Search</button> -->
</div>


</form>

<!-- <div hx-target="#songForm" hx-trigger="submit" hx-swap="innerHTML"></div> -->


</div>
38 changes: 38 additions & 0 deletions app/templates/partials/success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<div class="container-fluid my-5 mx-5" style="height: 18em;" id="songOutput">


<!-- <h2 class="text-center display-7">{{header}}</h2> -->
<h3 class="text-left display-5">{{header}}</h3>

<hr style="border-bottom: 1px solid #888">

Number of streams: {{prediction["prediction"]}}
Message: {{prediction["message"]["text"]}} {{prediction["message"]["emoji"]}}

<!-- <h4 class="text-left display-10">{{prediction["features"]}}</h4> -->





{% for key, value in prediction["song_metadata"].items() %}
{{key}}: {{ value }}
{% endfor %}


{% if prediction["preview_url"] is not none %}
<div class="container-audio">
<audio controls>
<source src={{prediction["preview_url"]}} />
</audio>
</div>
{% endif %}

<div class="class text-center">
<a href="/"><button type="submit">Try it again!</button></a>

</div>



</div>
51 changes: 22 additions & 29 deletions app/templates/prediction.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,28 @@
<title>Get song lyrics</title>
{% endblock %}

<div class="container">
<div class="row">
<div class="text-danger font-weight-bold">
{% for error in errors %}
<li>{{error}}</li>
{% endfor %}
</div>
</div>

<div class="row my-5">
<h3 class="text-left display-7">Let's predict the number of streams for a song</h3>
<h4 class="text-left display-7">Choose a song!</h4>

<form method="POST">


<div class="mb-3">
<input type="text" required placeholder="Choose an artist e.g Queen" name="artist" value="{{artist}}"
class="form-control">
</div>

<div class="mb-3">
<input type="text" required placeholder="Choose a song e.g Don't stop me now" name="song"
value="{{song}}" class="form-control">
</div>

<button type="submit">Search</button>
</form>
</div>



<!-- include songform -->
{% include 'partials/songform.html' %}



<div class="container-fluid my-5 mx-5" id="highscore">
<hr style="border-bottom: 1px solid #888">

<h4 class="text-left display-10">Highscore</h4>
<p class="text-left">The training dataset uses streams between 0 and 30.</p>

<ul class="list-group">
{% for score in scores %}
<li class="list-group-item">{{ score["prediction"] }}, {{ score["song"]}} by {{ score["artist"]}}
</li>
{% endfor %}
</ul>


</div>

{% endblock %}
1 change: 1 addition & 0 deletions app/templates/shared/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>MusicFlow API</title>
<script src="https://unpkg.com/htmx.org@1.7.0"></script>

<meta name="description" content="MusicFlow API" />
<meta name="nofoobar.com" content="Nofoobar">
Expand Down
41 changes: 0 additions & 41 deletions app/templates/success.html

This file was deleted.

23 changes: 14 additions & 9 deletions app/utils/song_request_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
class SongRequestForm:
def __init__(self, request: Request):
self.request: Request = request
self.errors: List = []
self.errors: List[str] = []
self.song: Optional[str] = None
self.artist: Optional[str] = None

Expand All @@ -16,16 +16,21 @@ async def load_data(self):
self.artist = form.get("artist") # type: ignore

def is_valid(self):
if not self.song or self.number_of_tokens(self.song) > 10:
self.errors.append("A valid song is required")
if not self.song and self.number_of_tokens(self.song) > 10:
song_error = "A valid song is required"
self.errors.append(song_error)

if not self.artist or self.number_of_tokens(self.artist) > 5:
self.errors.append("A valid artist is required")
if not self.artist and self.number_of_tokens(self.artist) > 5:
aritst_error = "A valid artist is required"
self.errors.append(aritst_error)

if not self.errors:
return True
return False
if self.errors:
return False
return True

def as_dict(self):
return self.__dict__

@staticmethod
def number_of_tokens(string):
def number_of_tokens(string: str) -> int:
return len(string.split())
4 changes: 4 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from mangum import Mangum
from starlette.middleware.sessions import SessionMiddleware

from app.__init__ import __version__ as api_version
from app.config import settings
Expand Down Expand Up @@ -77,6 +78,9 @@ async def lifespan(app: FastAPI):
app.add_middleware(
Analytics, is_lambda_runtime=is_lambda_runtime, is_testing=is_testing
)

app.add_middleware(SessionMiddleware, secret_key="some-random-string")

app.include_router(api.router)
app.include_router(root.router)

Expand Down
Loading

0 comments on commit 33b8a3c

Please sign in to comment.