diff --git a/folkugat_web/assets/templates/fragments/sessions/session_date.html b/folkugat_web/assets/templates/fragments/sessions/session_date.html
index 3442de3..26934a9 100644
--- a/folkugat_web/assets/templates/fragments/sessions/session_date.html
+++ b/folkugat_web/assets/templates/fragments/sessions/session_date.html
@@ -1,11 +1,7 @@
-{% if session == None %}
- L'últim Dimecres de cada mes
-{% else %}
- {% set dn = date_names(session.date) %}
- {{ dn.day_name }} {{ dn.day }} {{ dn.month_name }}
- de {{ session.start_time.strftime("%H:%M") }}
- a {{ session.end_time.strftime("%H:%M") }}
- {% if session.venue.name %}
- a {{ session.venue.name }}
- {% endif %}
+{% set dn = date_names(session.date) %}
+{{ dn.day_name }} {{ dn.day }} {{ dn.month_name }}
+ de {{ session.start_time.strftime("%H:%M") }}
+ a {{ session.end_time.strftime("%H:%M") }}
+{% if session.venue.name %}
+ a {{ session.venue.name }}
{% endif %}
diff --git a/folkugat_web/assets/templates/fragments/tema/pagina.html b/folkugat_web/assets/templates/fragments/tema/pagina.html
index 421c6e8..105c8f0 100644
--- a/folkugat_web/assets/templates/fragments/tema/pagina.html
+++ b/folkugat_web/assets/templates/fragments/tema/pagina.html
@@ -13,5 +13,6 @@
{% include "fragments/tema/lyrics.html" %}
{% include "fragments/tema/links.html" %}
{% include "fragments/tema/properties.html" %}
+ {% include "fragments/tema/stats.html" %}
diff --git a/folkugat_web/assets/templates/fragments/tema/stats.html b/folkugat_web/assets/templates/fragments/tema/stats.html
new file mode 100644
index 0000000..8e3ab06
--- /dev/null
+++ b/folkugat_web/assets/templates/fragments/tema/stats.html
@@ -0,0 +1,42 @@
+{% if tema.stats %}
+
Estadístiques
+
+
+
+ Aquest tema ha sigut tocat en
+ {% if tema.stats.times_played == 1%}
+ una sessió.
+ {% else %}
+ {{ tema.stats.times_played }} sessions.
+ {% endif %}
+
+
+ S'ha tocat a les sessions següents:
+
+ {% for session in tema.stats.sessions_played %}
+ -
+
+
+
+ {% endfor %}
+
+
+
+{% endif %}
diff --git a/folkugat_web/assets/templates/fragments/temes/results.html b/folkugat_web/assets/templates/fragments/temes/results.html
index 6bc5a1b..14c92b5 100644
--- a/folkugat_web/assets/templates/fragments/temes/results.html
+++ b/folkugat_web/assets/templates/fragments/temes/results.html
@@ -4,6 +4,7 @@
| Nom |
Enllaços |
+ Cops tocat |
{% for tema in temes %}
@@ -15,6 +16,13 @@
|
{% include "fragments/temes/result_links.html" %}
|
+
+ {% if tema.stats is none %}
+ -
+ {% else %}
+ {{ tema.stats.times_played }}
+ {% endif %}
+ |
{% endfor %}
diff --git a/folkugat_web/dal/sql/playlists/query.py b/folkugat_web/dal/sql/playlists/query.py
index f8562d7..d277bbb 100644
--- a/folkugat_web/dal/sql/playlists/query.py
+++ b/folkugat_web/dal/sql/playlists/query.py
@@ -1,10 +1,11 @@
-from collections.abc import Iterator
+from collections.abc import Iterable, Iterator
from typing import TypedDict
from folkugat_web.dal.sql import Connection, get_connection
from folkugat_web.dal.sql.sessions import conversion as sessions_conversion
from folkugat_web.model import playlists as model
from folkugat_web.model.sessions import Session
+from folkugat_web.utils import groupby
from . import conversion
@@ -55,14 +56,22 @@ def get_playlist_entries(
return map(conversion.row_to_playlist_entry, cur.fetchall())
-def get_tune_sessions(tema_ids: list[int], con: Connection | None = None) -> Iterator[Session]:
- query = """
+GetTuneSessionsRow = tuple[int, int, str, str, str, str | None, str | None, bool]
+
+
+def get_tune_sessions(tema_ids: list[int], con: Connection | None = None) -> dict[int, list[Session]]:
+ placeholders = ", ".join(["?" for _ in tema_ids])
+ query = f"""
SELECT p.tema_id, s.id, s.date, s.start_time, s.end_time, s.venue_name, s.venue_url, s.is_live
FROM playlists p JOIN sessions s ON p.session_id = s.id
- WHERE p.tema_id IN :tema_ids
+ WHERE p.tema_id IN ({placeholders})
"""
- data = dict(tema_ids=tuple(tema_ids))
with get_connection(con) as con:
cur = con.cursor()
- _ = cur.execute(query, data)
- return map(sessions_conversion.row_to_session, cur.fetchall())
+ _ = cur.execute(query, tema_ids)
+ result_rows: Iterable[GetTuneSessionsRow] = cur.fetchall()
+ return dict(groupby(
+ result_rows,
+ key_fn=lambda row: row[0],
+ group_fn=lambda rows: list(sessions_conversion.row_to_session(row[1:]) for row in rows)
+ ))
diff --git a/folkugat_web/fragments/tema.py b/folkugat_web/fragments/tema.py
index e577a93..7ec8235 100644
--- a/folkugat_web/fragments/tema.py
+++ b/folkugat_web/fragments/tema.py
@@ -1,6 +1,4 @@
-from typing import Optional
-
-from fastapi import Request
+from fastapi import HTTPException, Request
from folkugat_web.model import temes as model
from folkugat_web.services.temes import query as temes_q
from folkugat_web.services.temes.links import guess_link_type
@@ -12,6 +10,8 @@ def title(request: Request, logged_in: bool, tema: model.Tema | None = None, tem
if tema_id is None:
raise ValueError("Either 'tema' or 'tema_id' must be given!")
tema = temes_q.get_tema_by_id(tema_id)
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
return templates.TemplateResponse(
"fragments/tema/title.html",
{
@@ -24,6 +24,8 @@ def title(request: Request, logged_in: bool, tema: model.Tema | None = None, tem
def title_editor(request: Request, logged_in: bool, tema_id: int):
tema = temes_q.get_tema_by_id(tema_id)
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
return templates.TemplateResponse(
"fragments/tema/editor/title.html",
{
@@ -36,8 +38,8 @@ def title_editor(request: Request, logged_in: bool, tema_id: int):
def lyric(request: Request, logged_in: bool, tema_id: int, lyric_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
lyric = tema.lyrics.get(lyric_id)
return templates.TemplateResponse(
@@ -54,8 +56,8 @@ def lyric(request: Request, logged_in: bool, tema_id: int, lyric_id: int):
def lyric_editor(request: Request, logged_in: bool, tema_id: int, lyric_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
lyric = tema.lyrics.get(lyric_id)
return templates.TemplateResponse(
@@ -72,8 +74,8 @@ def lyric_editor(request: Request, logged_in: bool, tema_id: int, lyric_id: int)
def link(request: Request, logged_in: bool, tema_id: int, link_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
link = tema.links.get(link_id)
return templates.TemplateResponse(
@@ -95,8 +97,8 @@ def link(request: Request, logged_in: bool, tema_id: int, link_id: int):
def link_editor(request: Request, logged_in: bool, tema_id: int, link_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
link = tema.links.get(link_id)
return templates.TemplateResponse(
@@ -115,8 +117,8 @@ def link_editor(request: Request, logged_in: bool, tema_id: int, link_id: int):
def link_editor_url(request: Request, logged_in: bool, tema_id: int, link_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
link = tema.links.get(link_id)
return templates.TemplateResponse(
"fragments/tema/editor/link_url.html",
@@ -134,8 +136,8 @@ def link_editor_url(request: Request, logged_in: bool, tema_id: int, link_id: in
def link_editor_file(request: Request, logged_in: bool, tema_id: int, link_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
link = tema.links.get(link_id)
return templates.TemplateResponse(
"fragments/tema/editor/link_file.html",
@@ -175,8 +177,8 @@ def link_icon(request: Request, logged_in: bool, tema_id: int, link_id: int, url
def score(request: Request, logged_in: bool, tema_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
return templates.TemplateResponse(
"fragments/tema/score.html",
{
@@ -190,8 +192,8 @@ def score(request: Request, logged_in: bool, tema_id: int):
def property_(request: Request, logged_in: bool, tema_id: int, property_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
prop = tema.properties.get(property_id)
return templates.TemplateResponse(
@@ -209,8 +211,8 @@ def property_(request: Request, logged_in: bool, tema_id: int, property_id: int)
def property_editor(request: Request, logged_in: bool, tema_id: int, property_id: int):
tema = temes_q.get_tema_by_id(tema_id)
- if tema is None:
- raise ValueError(f"No tune exists for tema_id: {tema_id}")
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
prop = tema.properties.get(property_id)
return templates.TemplateResponse(
diff --git a/folkugat_web/fragments/temes.py b/folkugat_web/fragments/temes.py
index 59591f5..5f790a4 100644
--- a/folkugat_web/fragments/temes.py
+++ b/folkugat_web/fragments/temes.py
@@ -1,6 +1,7 @@
-from fastapi import Request
+from fastapi import HTTPException, Request
from folkugat_web.model import temes as model
from folkugat_web.model.pagines import Pages
+from folkugat_web.services import sessions as sessions_service
from folkugat_web.services.temes import query as temes_q
from folkugat_web.services.temes import search as temes_s
from folkugat_web.templates import templates
@@ -34,6 +35,8 @@ def temes_busca(request: Request, logged_in: bool, query: str, offset: int = 0,
if offset > 0:
prev_offset = max(offset - limit, 0)
+ temes = temes_q.temes_compute_stats(temes)
+
return templates.TemplateResponse(
"fragments/temes/results.html",
{
@@ -51,6 +54,9 @@ def temes_busca(request: Request, logged_in: bool, query: str, offset: int = 0,
def tema(request: Request, tema_id: int, logged_in: bool):
tema = temes_q.get_tema_by_id(tema_id)
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
+ tema = temes_q.tema_compute_stats(tema)
return templates.TemplateResponse(
"fragments/tema/pagina.html",
{
@@ -60,5 +66,6 @@ def tema(request: Request, tema_id: int, logged_in: bool):
"LinkType": model.LinkType,
"ContentType": model.ContentType,
"tema": tema,
+ "date_names": sessions_service.get_date_names,
}
)
diff --git a/folkugat_web/model/temes.py b/folkugat_web/model/temes.py
index 0a3c0fc..417bdd9 100644
--- a/folkugat_web/model/temes.py
+++ b/folkugat_web/model/temes.py
@@ -101,6 +101,12 @@ class Lyrics(WithId):
)
+@dataclasses.dataclass
+class Stats:
+ times_played: int
+ sessions_played: list[Session]
+
+
@dataclasses.dataclass
class Tema:
id: int | None = None
@@ -117,7 +123,7 @@ class Tema:
modification_date: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
creation_date: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
# Stats
- played: list[Session] = dataclasses.field(default_factory=list)
+ stats: Stats | None = None
def compute_ngrams(self):
self.ngrams = ngrams.get_text_ngrams(self.title, *self.alternatives)
@@ -126,5 +132,13 @@ class Tema:
self.compute_ngrams()
return self
+ @staticmethod
+ def _is_score(link: Link) -> bool:
+ if link.content_type is not ContentType.PARTITURA:
+ return False
+ if link.link_type is LinkType.PDF:
+ return link.url.startswith("/")
+ return link.link_type is LinkType.IMAGE
+
def score(self) -> Link | None:
- return next(filter(lambda l: l.content_type is ContentType.PARTITURA and l.url.startswith('/'), self.links), None)
+ return next(filter(self._is_score, self.links), None)
diff --git a/folkugat_web/services/temes/query.py b/folkugat_web/services/temes/query.py
index 890ae04..e165bf0 100644
--- a/folkugat_web/services/temes/query.py
+++ b/folkugat_web/services/temes/query.py
@@ -1,8 +1,29 @@
-from typing import Optional
-
+from folkugat_web.dal.sql.playlists import query as playlists_q
from folkugat_web.dal.sql.temes import query as temes_q
+from folkugat_web.model import sessions as sessions_model
from folkugat_web.model import temes as model
def get_tema_by_id(tema_id: int) -> model.Tema | None:
return temes_q.get_tema_by_id(tema_id)
+
+
+def tema_compute_stats(
+ tema: model.Tema,
+ tune_sessions_dict: dict[int, list[sessions_model.Session]] | None = None,
+) -> model.Tema:
+ if tema.id:
+ if tune_sessions_dict is None:
+ tune_sessions_dict = playlists_q.get_tune_sessions(tema_ids=[tema.id])
+ if tema.id and (tune_sessions := tune_sessions_dict.get(tema.id)):
+ tema.stats = model.Stats(
+ times_played=len(tune_sessions),
+ sessions_played=list(reversed(sorted(tune_sessions, key=lambda s: s.date))),
+ )
+ return tema
+
+
+def temes_compute_stats(temes: list[model.Tema]) -> list[model.Tema]:
+ tema_ids = [tema.id for tema in temes if tema.id is not None]
+ tune_sessions_dict = playlists_q.get_tune_sessions(tema_ids=tema_ids)
+ return [tema_compute_stats(tema=tema, tune_sessions_dict=tune_sessions_dict) for tema in temes]
diff --git a/folkugat_web/utils.py b/folkugat_web/utils.py
index b6ffc1d..36d5171 100644
--- a/folkugat_web/utils.py
+++ b/folkugat_web/utils.py
@@ -4,7 +4,7 @@ from typing import Protocol, Self, TypeVar
class SupportsLessThan(Protocol):
- def __lt__(self, other: Self) -> bool:
+ def __lt__(self, other: Self, /) -> bool:
raise NotImplementedError()