Afegir temes coocurrents
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import dataclasses
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import HTTPException, Request
|
||||
@@ -5,6 +6,7 @@ from fastapi.params import Form
|
||||
from fastapi.responses import HTMLResponse
|
||||
from folkugat_web.api.router import get_router
|
||||
from folkugat_web.fragments import tema, temes
|
||||
from folkugat_web.model.temes import Tema
|
||||
from folkugat_web.services import auth
|
||||
from folkugat_web.services import files as files_service
|
||||
from folkugat_web.services.temes import links as links_service
|
||||
@@ -32,6 +34,21 @@ def page(request: Request, logged_in: auth.LoggedIn, tema_id: int):
|
||||
)
|
||||
|
||||
|
||||
def augment_played_with(tema: Tema) -> Tema:
|
||||
if tema.played_with:
|
||||
tema.played_with = [
|
||||
dataclasses.replace(
|
||||
co_tema,
|
||||
tema=(
|
||||
FnChain.transform(co_tema.tema) |
|
||||
scores_service.add_scores_to_tema |
|
||||
properties_service.add_properties_to_tema
|
||||
).result()
|
||||
) for co_tema in tema.played_with
|
||||
]
|
||||
return tema
|
||||
|
||||
|
||||
@router.get("/api/tema/{tema_id}")
|
||||
def contingut(request: Request, logged_in: auth.LoggedIn, tema_id: int):
|
||||
tema = temes_q.get_tema_by_id(tema_id)
|
||||
@@ -40,6 +57,8 @@ def contingut(request: Request, logged_in: auth.LoggedIn, tema_id: int):
|
||||
tema = (
|
||||
FnChain.transform(tema) |
|
||||
temes_q.tema_compute_stats |
|
||||
temes_q.tema_compute_played_with |
|
||||
augment_played_with |
|
||||
links_service.add_links_to_tema |
|
||||
lyrics_service.add_lyrics_to_tema |
|
||||
scores_service.add_scores_to_tema |
|
||||
|
||||
@@ -657,6 +657,10 @@ video {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.ml-4 {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.ml-auto {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<li class="flex flex-col
|
||||
items-center
|
||||
m-4 rounded-lg
|
||||
bg-white">
|
||||
<div class="flex flex-col items-start py-4 text-left w-full max-w-[655px]">
|
||||
<div class="px-4 text-black font-bold">
|
||||
<a href="/tema/{{ tema.id }}">
|
||||
{{ tema.title }}
|
||||
</a>
|
||||
</div>
|
||||
{% if tema.properties %}
|
||||
<ul class="flex flex-wrap text-sm px-3">
|
||||
{% for property in tema.properties %}
|
||||
<a class="bg-beige text-white rounded
|
||||
m-1 px-2"
|
||||
href="/temes?properties={{ property.value }}">
|
||||
{{ property.value }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if tema.main_score() and tema.main_score().preview_url %}
|
||||
<a href="/tema/{{ tema.id }}">
|
||||
<img class="px-4 pb-2"
|
||||
src="{{ tema.main_score().preview_url }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="flex flex-row w-full">
|
||||
<div class="flex flex-row items-center
|
||||
row-0 px-4 text-sm text-gray-400">
|
||||
<i class="mx-1">{% include "icons/music-box.svg" %}</i>
|
||||
{% if co_tema.count == 1 %}
|
||||
1 cop
|
||||
{% else %}
|
||||
{{ co_tema.count }} cops
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -1,11 +1,12 @@
|
||||
<div id="tema-property-{{ property.id }}"
|
||||
class="flex flex-row">
|
||||
class="flex flex-row items-center">
|
||||
<div class="px-2">
|
||||
<i>{{ property.field.value.capitalize() }}:</i>
|
||||
</div>
|
||||
<div>
|
||||
<a class="bg-beige text-white rounded m-1 px-2"
|
||||
href="/temes?query=&properties={{ property.value }}">
|
||||
{{ property.value }}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{% if logged_in %}
|
||||
<div class="grow"></div>
|
||||
|
||||
@@ -1,42 +1,49 @@
|
||||
<h4 class="pt-4 text-xl mt-3 text-beige">Estadístiques</h4>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
{% if tema.stats %}
|
||||
<h4 class="pt-4 text-xl mt-3 text-beige">Estadístiques</h4>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
<div>
|
||||
<p>
|
||||
Aquest tema ha sigut tocat en
|
||||
{% if tema.stats.times_played == 1%}
|
||||
una sessió.
|
||||
{% else %}
|
||||
{{ tema.stats.times_played }} sessions.
|
||||
{% endif %}
|
||||
</p>
|
||||
<p class="py-2">
|
||||
S'ha tocat a les sessions següents:
|
||||
<ol class="flex flex-col items-center justify-center">
|
||||
{% for session in tema.stats.sessions_played %}
|
||||
<li class="border rounded border-beige
|
||||
flex flex-row grow
|
||||
p-2 m-2 w-full max-w-xl
|
||||
relative">
|
||||
<a href="/session/{{session.id}}">
|
||||
<div class="flex flex-row grow items-center">
|
||||
<div class="flex-1">
|
||||
<a href="/sessio/{{session.id}}/">
|
||||
{% set dn = date_names(session.date) %}
|
||||
{{ dn.day_name }} {{ dn.day }} {{ dn.month_name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<a title="Més informació"
|
||||
class="text-beige mx-1"
|
||||
href="/sessio/{{session.id}}/">
|
||||
<i class="fa fa-chevron-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</p>
|
||||
<div class="flex flex-col">
|
||||
{% if tema.played_with %}
|
||||
<div class="my-2 flex flex-row items-center">
|
||||
<i class="mx-2 flex-none">{% include "icons/notes-small.svg" %}</i>
|
||||
<p>
|
||||
S'ha tocat juntament amb:
|
||||
</p>
|
||||
</div>
|
||||
<ol class="ml-4 flex flex-col justify-center">
|
||||
{% for co_tema in tema.played_with %}
|
||||
{% set tema = co_tema.tema %}
|
||||
{% include "fragments/tema/played_with.html" %}
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
<div class="my-2 flex flex-row items-center">
|
||||
<i class="mx-2 flex-none">{% include "icons/music-box.svg" %}</i>
|
||||
<p>
|
||||
S'ha tocat en
|
||||
{% if tema.stats.times_played == 1%}
|
||||
una sessió:
|
||||
{% else %}
|
||||
{{ tema.stats.times_played }} sessions:
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<ol class="ml-4 flex flex-col justify-center">
|
||||
{% for session in tema.stats.sessions_played %}
|
||||
<li class="flex flex-row grow
|
||||
my-1">
|
||||
<a class="flex flex-row grow items-center"
|
||||
href="/sessio/{{session.id}}">
|
||||
<i class="mx-2 text-beige flex-none">{% include "icons/calendar.svg" %}</i>
|
||||
<p>
|
||||
{% set dn = date_names(session.date) %} {{ dn.day_name }} {{ dn.day }} {{ dn.month_name }}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</div>
|
||||
{% else %}
|
||||
<div>
|
||||
<i>No s'ha tocat a cap jam (encara)</i>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
5
folkugat_web/assets/templates/icons/notes-small.svg
Normal file
5
folkugat_web/assets/templates/icons/notes-small.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-music-note-beamed" viewBox="0 0 16 16">
|
||||
<path d="M6 13c0 1.105-1.12 2-2.5 2S1 14.105 1 13c0-1.104 1.12-2 2.5-2s2.5.896 2.5 2m9-2c0 1.105-1.12 2-2.5 2s-2.5-.895-2.5-2 1.12-2 2.5-2 2.5.895 2.5 2"/>
|
||||
<path fill-rule="evenodd" d="M14 11V2h1v9zM6 3v10H5V3z"/>
|
||||
<path d="M5 2.905a1 1 0 0 1 .9-.995l8-.8a1 1 0 0 1 1.1.995V3L5 4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 426 B |
@@ -3,7 +3,9 @@ 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.dal.sql.temes.conversion import row_to_tema
|
||||
from folkugat_web.model import playlists as model
|
||||
from folkugat_web.model import temes as temes_model
|
||||
from folkugat_web.model.sessions import Session
|
||||
from folkugat_web.utils import groupby
|
||||
|
||||
@@ -76,3 +78,36 @@ def get_tune_sessions(tema_ids: list[int], con: Connection | None = None) -> dic
|
||||
key_fn=lambda row: row[0],
|
||||
group_fn=lambda rows: list(sessions_conversion.row_to_session(row[1:]) for row in rows)
|
||||
))
|
||||
|
||||
|
||||
CommonlyPlayedTuneRow = tuple[int, str, str, str, str, int, int]
|
||||
|
||||
|
||||
def get_commonly_played_tunes(
|
||||
tema_id: int,
|
||||
con: Connection | None = None,
|
||||
) -> list[temes_model.CommonlyPlayedTema]:
|
||||
query = """
|
||||
SELECT
|
||||
id, title, alternatives, creation_date, modification_date, hidden, count
|
||||
FROM (
|
||||
SELECT tema_id, count(*) count FROM playlists p JOIN (
|
||||
SELECT session_id, set_id
|
||||
FROM playlists
|
||||
WHERE tema_id = ?
|
||||
) s
|
||||
ON p.session_id == s.session_id AND p.set_id == s.set_id
|
||||
WHERE tema_id != ?
|
||||
GROUP BY tema_id
|
||||
) common JOIN temes t ON common.tema_id == t.id
|
||||
"""
|
||||
with get_connection(con) as con:
|
||||
cur = con.cursor()
|
||||
_ = cur.execute(query, [tema_id, tema_id])
|
||||
result_rows: Iterable[CommonlyPlayedTuneRow] = cur.fetchall()
|
||||
return [
|
||||
temes_model.CommonlyPlayedTema(
|
||||
tema=row_to_tema(row[:6]),
|
||||
count=row[6],
|
||||
) for row in result_rows
|
||||
]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import datetime
|
||||
import enum
|
||||
@@ -110,6 +112,7 @@ class Tema:
|
||||
creation_date: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
|
||||
# Stats
|
||||
stats: Stats | None = None
|
||||
played_with: list[CommonlyPlayedTema] | None = None
|
||||
|
||||
def ngrams(self) -> NGrams:
|
||||
return ngrams.get_text_ngrams(self.title, *self.alternatives)
|
||||
@@ -149,6 +152,12 @@ class Tema:
|
||||
return bool(self.lyrics)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CommonlyPlayedTema:
|
||||
tema: Tema
|
||||
count: int
|
||||
|
||||
|
||||
class TemaCols(enum.Enum):
|
||||
NOM = "nom"
|
||||
COPS_TOCAT = "cops_tocat"
|
||||
|
||||
@@ -35,3 +35,11 @@ def temes_compute_stats(temes: Iterable[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]
|
||||
|
||||
|
||||
def tema_compute_played_with(
|
||||
tema: model.Tema,
|
||||
) -> model.Tema:
|
||||
if tema.id:
|
||||
tema.played_with = playlists_q.get_commonly_played_tunes(tema_id=tema.id)
|
||||
return tema
|
||||
|
||||
Reference in New Issue
Block a user