From a85efd08386f5260d12953438b2794eb4f941723 Mon Sep 17 00:00:00 2001 From: marc Date: Tue, 11 Mar 2025 23:05:20 +0100 Subject: [PATCH] Added indexed lists and link edition in tunes --- .gitignore | 2 + folkugat_web/api/tema/editor.py | 26 ++++- folkugat_web/api/tema/index.py | 110 +++++++++++++++--- .../templates/fragments/tema/editor/link.html | 66 +++++++++++ .../fragments/tema/editor/lyric.html | 10 +- .../assets/templates/fragments/tema/link.html | 44 ++++--- .../templates/fragments/tema/link_icon.html | 16 +++ .../templates/fragments/tema/links.html | 9 +- .../templates/fragments/tema/lyric.html | 10 +- .../templates/fragments/tema/lyrics.html | 1 - .../templates/fragments/tema/pagina.html | 12 -- .../templates/fragments/temes/result.html | 10 +- folkugat_web/dal/sql/temes/_conversion.py | 7 +- folkugat_web/data.py | 43 ++++--- folkugat_web/fragments/nota.py | 7 +- folkugat_web/fragments/tema.py | 85 ++++++++++++-- folkugat_web/fragments/temes.py | 4 +- folkugat_web/model/__init__.py | 1 + folkugat_web/model/_base.py | 36 ++++++ folkugat_web/model/temes.py | 50 ++++---- folkugat_web/services/temes/links.py | 32 +++++ folkugat_web/services/temes/write.py | 54 +++++++-- 22 files changed, 498 insertions(+), 137 deletions(-) create mode 100644 folkugat_web/assets/templates/fragments/tema/editor/link.html create mode 100644 folkugat_web/assets/templates/fragments/tema/link_icon.html create mode 100644 folkugat_web/model/_base.py create mode 100644 folkugat_web/services/temes/links.py diff --git a/.gitignore b/.gitignore index e3ebff7..70c86fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.direnv + **/**.pyc diff --git a/folkugat_web/api/tema/editor.py b/folkugat_web/api/tema/editor.py index a456f10..054e3a6 100644 --- a/folkugat_web/api/tema/editor.py +++ b/folkugat_web/api/tema/editor.py @@ -13,11 +13,31 @@ def title_editor( return tema.title_editor(request=request, logged_in=logged_in, tema_id=tema_id) -@router.get("/api/tema/{tema_id}/editor/lyric/{lyric_idx}") +@router.get("/api/tema/{tema_id}/editor/lyric/{lyric_id}") def lyric_editor( request: Request, logged_in: auth.RequireLogin, tema_id: int, - lyric_idx: int, + lyric_id: int, ): - return tema.lyric_editor(request=request, logged_in=logged_in, tema_id=tema_id, lyric_idx=lyric_idx) + return tema.lyric_editor( + request=request, + logged_in=logged_in, + tema_id=tema_id, + lyric_id=lyric_id, + ) + + +@router.get("/api/tema/{tema_id}/editor/link/{link_id}") +def link_editor( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, +): + return tema.link_editor( + request=request, + logged_in=logged_in, + tema_id=tema_id, + link_id=link_id, + ) diff --git a/folkugat_web/api/tema/index.py b/folkugat_web/api/tema/index.py index f735999..3edaca6 100644 --- a/folkugat_web/api/tema/index.py +++ b/folkugat_web/api/tema/index.py @@ -1,13 +1,14 @@ from typing import Annotated from fastapi import Request -from fastapi.params import Form +from fastapi.params import Form, Param from fastapi.responses import HTMLResponse from folkugat_web.api import router from folkugat_web.fragments import tema, temes from folkugat_web.model import temes as model from folkugat_web.services import auth from folkugat_web.services.temes import write as temes_w +from folkugat_web.services.temes.links import guess_link_type from folkugat_web.templates import templates @@ -45,23 +46,23 @@ def set_title( return tema.title(request=request, tema=new_tema, logged_in=logged_in) -@router.get("/api/tema/{tema_id}/lyric/{lyric_idx}") -def lyric(request: Request, logged_in: auth.LoggedIn, tema_id: int, lyric_idx: int): - return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_idx=lyric_idx) +@router.get("/api/tema/{tema_id}/lyric/{lyric_id}") +def lyric(request: Request, logged_in: auth.LoggedIn, tema_id: int, lyric_id: int): + return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_id=lyric_id) -@router.put("/api/tema/{tema_id}/lyric/{lyric_idx}") +@router.put("/api/tema/{tema_id}/lyric/{lyric_id}") def set_lyric( request: Request, logged_in: auth.RequireLogin, tema_id: int, - lyric_idx: int, + lyric_id: int, title: Annotated[str, Form()], lyric: Annotated[str, Form()], ): - new_lyric = model.Lyrics(title=title, content=lyric.strip()) - temes_w.update_lyric(tema_id=tema_id, lyric_idx=lyric_idx, lyric=new_lyric) - return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_idx=lyric_idx) + new_lyric = model.Lyrics(id=lyric_id, title=title, content=lyric.strip()) + temes_w.update_lyric(tema_id=tema_id, lyric_id=lyric_id, lyric=new_lyric) + return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_id=lyric_id) @router.post("/api/tema/{tema_id}/lyric") @@ -71,14 +72,95 @@ def add_lyric( tema_id: int, ): new_tema = temes_w.add_lyric(tema_id=tema_id) - lyric_idx = len(new_tema.lyrics) - 1 - return tema.lyric_editor(request=request, logged_in=logged_in, tema_id=tema_id, lyric_idx=lyric_idx) + lyric_id = new_tema.lyrics[-1].id + if lyric_id is None: + raise RuntimeError("Invalid lyric_id on newly created lyric!") + return tema.lyric_editor( + request=request, + logged_in=logged_in, + tema_id=tema_id, + lyric_id=lyric_id, + ) -@router.delete("/api/tema/{tema_id}/lyric/{lyric_idx}") +@router.delete("/api/tema/{tema_id}/lyric/{lyric_id}") def delete_lyric( tema_id: int, - lyric_idx: int, + lyric_id: int, ): - temes_w.delete_lyric(tema_id=tema_id, lyric_idx=lyric_idx) + temes_w.delete_lyric(tema_id=tema_id, lyric_id=lyric_id) return HTMLResponse() + + +@router.get("/api/tema/{tema_id}/link/{link_id}") +def link(request: Request, logged_in: auth.LoggedIn, tema_id: int, link_id: int): + return tema.link(request=request, logged_in=logged_in, tema_id=tema_id, link_id=link_id) + + +@router.put("/api/tema/{tema_id}/link/{link_id}") +def set_link( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, + content_type: Annotated[model.ContentType, Form()], + url: Annotated[str, Form()] = "", + title: Annotated[str, Form()] = "", +): + link_type = guess_link_type(url) + new_link = model.Link( + id=link_id, + content_type=content_type, + link_type=link_type, + url=url, + title=title, + ) + temes_w.update_link(tema_id=tema_id, link_id=link_id, link=new_link) + return tema.link(request=request, logged_in=logged_in, tema_id=tema_id, link_id=link_id) + + +@router.post("/api/tema/{tema_id}/link") +def add_link( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, +): + new_tema = temes_w.add_link(tema_id=tema_id) + link_id = new_tema.links[-1].id + if link_id is None: + raise RuntimeError("Invalid link_id on newly created link!") + return tema.link_editor( + request=request, + logged_in=logged_in, + tema_id=tema_id, + link_id=link_id, + ) + + +@router.delete("/api/tema/{tema_id}/link/{link_id}") +def delete_link( + _: auth.RequireLogin, + tema_id: int, + link_id: int, +): + temes_w.delete_link(tema_id=tema_id, link_id=link_id) + return HTMLResponse() + + +@router.get("/api/tema/{tema_id}/link/{link_id}/icon") +def link_icon( + request: Request, + logged_in: auth.LoggedIn, + tema_id: int, + link_id: int, + content_type: Annotated[model.ContentType, Param()], + url: Annotated[str, Param()], +): + return tema.link_icon( + request=request, + logged_in=logged_in, + tema_id=tema_id, + link_id=link_id, + url=url, + content_type=content_type, + ) diff --git a/folkugat_web/assets/templates/fragments/tema/editor/link.html b/folkugat_web/assets/templates/fragments/tema/editor/link.html new file mode 100644 index 0000000..05058db --- /dev/null +++ b/folkugat_web/assets/templates/fragments/tema/editor/link.html @@ -0,0 +1,66 @@ + diff --git a/folkugat_web/assets/templates/fragments/tema/editor/lyric.html b/folkugat_web/assets/templates/fragments/tema/editor/lyric.html index e8b31c0..d82312f 100644 --- a/folkugat_web/assets/templates/fragments/tema/editor/lyric.html +++ b/folkugat_web/assets/templates/fragments/tema/editor/lyric.html @@ -1,4 +1,4 @@ -
+
diff --git a/folkugat_web/assets/templates/fragments/tema/link.html b/folkugat_web/assets/templates/fragments/tema/link.html index 912a738..f44360f 100644 --- a/folkugat_web/assets/templates/fragments/tema/link.html +++ b/folkugat_web/assets/templates/fragments/tema/link.html @@ -1,21 +1,29 @@ -
  • - +
  • diff --git a/folkugat_web/assets/templates/fragments/tema/link_icon.html b/folkugat_web/assets/templates/fragments/tema/link_icon.html new file mode 100644 index 0000000..9087055 --- /dev/null +++ b/folkugat_web/assets/templates/fragments/tema/link_icon.html @@ -0,0 +1,16 @@ + diff --git a/folkugat_web/assets/templates/fragments/tema/links.html b/folkugat_web/assets/templates/fragments/tema/links.html index d92e1e8..12d85ae 100644 --- a/folkugat_web/assets/templates/fragments/tema/links.html +++ b/folkugat_web/assets/templates/fragments/tema/links.html @@ -4,13 +4,12 @@ {% endif %} {% if tema.links %} -{% for link in tema.links %} -{% endfor %} {% endif %} {% if logged_in %} @@ -19,7 +18,7 @@ class="text-sm text-beige text-right" hx-post="/api/tema/{{ tema.id }}/link" hx-target="#new-link-target" - hx-swap="beforebegin"> + hx-swap="beforeend transition:true"> Afegeix un enllaç diff --git a/folkugat_web/assets/templates/fragments/tema/lyric.html b/folkugat_web/assets/templates/fragments/tema/lyric.html index 153d366..2cb04fe 100644 --- a/folkugat_web/assets/templates/fragments/tema/lyric.html +++ b/folkugat_web/assets/templates/fragments/tema/lyric.html @@ -1,18 +1,18 @@ -
    +
    {{ lyric.title }} {% if logged_in %} diff --git a/folkugat_web/assets/templates/fragments/tema/lyrics.html b/folkugat_web/assets/templates/fragments/tema/lyrics.html index c64af98..40c8f54 100644 --- a/folkugat_web/assets/templates/fragments/tema/lyrics.html +++ b/folkugat_web/assets/templates/fragments/tema/lyrics.html @@ -4,7 +4,6 @@ {% if tema.lyrics %} {% for lyric in tema.lyrics %} - {% set lyric_idx = loop.index0 %} {% include "fragments/tema/lyric.html" %} {% endfor %} {% endif %} diff --git a/folkugat_web/assets/templates/fragments/tema/pagina.html b/folkugat_web/assets/templates/fragments/tema/pagina.html index cf0b54e..6b997d3 100644 --- a/folkugat_web/assets/templates/fragments/tema/pagina.html +++ b/folkugat_web/assets/templates/fragments/tema/pagina.html @@ -4,18 +4,6 @@ {% include "fragments/tema/title.html" %}
    - - - - - - - - - - - - {% include "fragments/tema/lyrics.html" %} {% include "fragments/tema/links.html" %} diff --git a/folkugat_web/assets/templates/fragments/temes/result.html b/folkugat_web/assets/templates/fragments/temes/result.html index 54bae48..16c2957 100644 --- a/folkugat_web/assets/templates/fragments/temes/result.html +++ b/folkugat_web/assets/templates/fragments/temes/result.html @@ -13,16 +13,16 @@ {% for link in tema.links %}
  • - {% if link.type == LinkType.AUDIO %} - {% if link.subtype == LinkSubtype.SPOTIFY %} + {% if link.content_type == ContentType.AUDIO %} + {% if link.link_type == LinkType.SPOTIFY %} {% include "icons/spotify.svg" %} - {% elif link.subtype == LinkSubtype.YOUTUBE %} + {% elif link.link_type == LinkType.YOUTUBE %} {% include "icons/youtube.svg" %} {% else %} {% include "icons/notes.svg" %} {% endif %} - {% elif link.type == LinkType.SCORE %} - {% if link.subtype == LinkSubtype.PDF %} + {% elif link.content_type == ContentType.PARTITURA %} + {% if link.link_type == LinkType.PDF %} {% include "icons/pdf.svg" %} {% else %} {% include "icons/link.svg" %} diff --git a/folkugat_web/dal/sql/temes/_conversion.py b/folkugat_web/dal/sql/temes/_conversion.py index 8aa2546..fa3f502 100644 --- a/folkugat_web/dal/sql/temes/_conversion.py +++ b/folkugat_web/dal/sql/temes/_conversion.py @@ -1,6 +1,7 @@ import datetime import json +from folkugat_web.model import IndexedList from folkugat_web.model import temes as model @@ -27,9 +28,9 @@ def row_to_tema(row: tuple) -> model.Tema: return model.Tema( id=row[0], title=row[1], - properties=list(map(model.Property.from_dict, json.loads(row[2]))), - links=list(map(model.Link.from_dict, json.loads(row[3]))), - lyrics=list(map(model.Lyrics.from_dict, json.loads(row[4]))), + properties=IndexedList(map(model.Property.from_dict, json.loads(row[2]))), + links=IndexedList(map(model.Link.from_dict, json.loads(row[3]))), + lyrics=IndexedList(map(model.Lyrics.from_dict, json.loads(row[4]))), alternatives=json.loads(row[5]), ngrams=cell_to_ngrams(row[6]), modification_date=datetime.datetime.fromisoformat(row[7]), diff --git a/folkugat_web/data.py b/folkugat_web/data.py index ec610bd..2154280 100644 --- a/folkugat_web/data.py +++ b/folkugat_web/data.py @@ -1,3 +1,4 @@ +from folkugat_web.model import IndexedList from folkugat_web.model import temes as model TEMES = [ @@ -9,26 +10,29 @@ TEMES = [ # --- model.Tema( title="Pasdoble de Muntanya (Cançó amb el nom molt llarg)", - links=[ + links=IndexedList([ model.Link( - type=model.LinkType.AUDIO, - subtype=model.LinkSubtype.SPOTIFY, + id=0, + content_type=model.ContentType.AUDIO, + link_type=model.LinkType.SPOTIFY, url="https://open.spotify.com/track/4j9Krf19c5USmMvVUCoeWa?si=3023d1d83f814886", + title="Versió de l'Orquestrina Trama", ), - ], + ]), hidden=False, ).with_ngrams(), # --- model.Tema( title="Astrid Waltz", alternatives=["vals"], - links=[ + links=IndexedList([ model.Link( - type=model.LinkType.OTHER, - subtype=None, + id=0, + content_type=model.ContentType.OTHER, + link_type=None, url="https://marc.sastre.cat/folkugat", ) - ], + ]), hidden=False, ).with_ngrams(), # --- @@ -45,13 +49,14 @@ TEMES = [ # --- model.Tema( title="El Gitano", - links=[ + links=IndexedList([ model.Link( - type=model.LinkType.SCORE, - subtype=model.LinkSubtype.PDF, + id=0, + content_type=model.ContentType.PARTITURA, + link_type=model.LinkType.PDF, url="/db/temes/1/tema.pdf", ) - ], + ]), hidden=False, ).with_ngrams(), # --- @@ -68,8 +73,9 @@ TEMES = [ # --- model.Tema( title="Malaguenya de Barxeta", - lyrics=[ + lyrics=IndexedList([ model.Lyrics( + id=0, title="Malaguenya de Barxeta", content=""" Mira si he corregut terres @@ -93,13 +99,14 @@ d'allà on renaix de les cendres el meu País Valencià. """.strip(), ), - ], - properties=[ + ]), + properties=IndexedList([ model.Property( - model.PropertyField.AUTOR, - "Pep Jimeno 'Botifarra'" + id=0, + field=model.PropertyField.AUTOR, + value="Pep Jimeno 'Botifarra'" ) - ], + ]), hidden=False, ).with_ngrams(), ] diff --git a/folkugat_web/fragments/nota.py b/folkugat_web/fragments/nota.py index 3affc86..7c7ed22 100644 --- a/folkugat_web/fragments/nota.py +++ b/folkugat_web/fragments/nota.py @@ -25,11 +25,10 @@ def footer(request, value, logged_in): def nota(request): - response = templates.TemplateResponse( + return templates.TemplateResponse( "fragments/nota/nota.html", { "request": request, - } + }, + headers={"HX-Refresh": "true"}, ) - response.headers["HX-Refresh"] = "true" - return response diff --git a/folkugat_web/fragments/tema.py b/folkugat_web/fragments/tema.py index 5a0a4c8..feb6556 100644 --- a/folkugat_web/fragments/tema.py +++ b/folkugat_web/fragments/tema.py @@ -3,6 +3,7 @@ from typing import Optional from fastapi import 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 from folkugat_web.templates import templates @@ -33,13 +34,11 @@ def title_editor(request: Request, logged_in: bool, tema_id: int): ) -def lyric(request: Request, logged_in: bool, tema_id: int, lyric_idx: 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 len(tema.lyrics) < lyric_idx: - raise ValueError(f'Lyric index out of bounds') - lyric = tema.lyrics[lyric_idx] + lyric = tema.lyrics.get(lyric_id) return templates.TemplateResponse( "fragments/tema/lyric.html", @@ -47,19 +46,17 @@ def lyric(request: Request, logged_in: bool, tema_id: int, lyric_idx: int): "request": request, "logged_in": logged_in, "tema": tema, - "lyric_idx": lyric_idx, + "lyric_id": lyric_id, "lyric": lyric, } ) -def lyric_editor(request: Request, logged_in: bool, tema_id: int, lyric_idx: 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 len(tema.lyrics) < lyric_idx: - raise ValueError(f'Lyric index out of bounds') - lyric = tema.lyrics[lyric_idx] + lyric = tema.lyrics.get(lyric_id) return templates.TemplateResponse( "fragments/tema/editor/lyric.html", @@ -67,7 +64,75 @@ def lyric_editor(request: Request, logged_in: bool, tema_id: int, lyric_idx: int "request": request, "logged_in": logged_in, "tema": tema, - "lyric_idx": lyric_idx, + "lyric_id": lyric_id, "lyric": lyric, } ) + + +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}") + link = tema.links.get(link_id) + + return templates.TemplateResponse( + "fragments/tema/link.html", + { + "request": request, + "logged_in": logged_in, + "tema": tema, + "link_id": link_id, + "link": link, + "LinkType": model.LinkType, + "ContentType": model.ContentType, + } + ) + + +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}") + link = tema.links.get(link_id) + + return templates.TemplateResponse( + "fragments/tema/editor/link.html", + { + "request": request, + "logged_in": logged_in, + "tema": tema, + "link_id": link_id, + "link": link, + "LinkType": model.LinkType, + "ContentType": model.ContentType, + } + ) + + +def link_icon( + request: Request, + logged_in: bool, + tema_id: int, + link_id: int, + url: str, + content_type: model.ContentType, +): + link = model.Link( + id=link_id, + content_type=content_type, + link_type=guess_link_type(url), + url=url, + ) + return templates.TemplateResponse( + "fragments/tema/link_icon.html", + { + "request": request, + "logged_in": logged_in, + "tema_id": tema_id, + "link_id": link_id, + "link": link, + "LinkType": model.LinkType, + "ContentType": model.ContentType, + } + ) diff --git a/folkugat_web/fragments/temes.py b/folkugat_web/fragments/temes.py index 4c72839..9031b95 100644 --- a/folkugat_web/fragments/temes.py +++ b/folkugat_web/fragments/temes.py @@ -25,8 +25,8 @@ def temes_busca_result(request: Request, tema: model.Tema, logged_in: bool): "request": request, "logged_in": logged_in, "tema": tema, - "LinkSubtype": model.LinkSubtype, "LinkType": model.LinkType, + "ContentType": model.ContentType, } ).body.decode('utf-8') @@ -47,8 +47,8 @@ def tema(request: Request, tema_id: int, logged_in: bool): "request": request, "logged_in": logged_in, "Pages": Pages, - "LinkSubtype": model.LinkSubtype, "LinkType": model.LinkType, + "ContentType": model.ContentType, "tema": tema, } ) diff --git a/folkugat_web/model/__init__.py b/folkugat_web/model/__init__.py index e69de29..08424dc 100644 --- a/folkugat_web/model/__init__.py +++ b/folkugat_web/model/__init__.py @@ -0,0 +1 @@ +from ._base import IndexedList diff --git a/folkugat_web/model/_base.py b/folkugat_web/model/_base.py new file mode 100644 index 0000000..06f1f99 --- /dev/null +++ b/folkugat_web/model/_base.py @@ -0,0 +1,36 @@ +import dataclasses +from typing import Optional, TypeVar + + +@dataclasses.dataclass +class WithId: + id: Optional[int] + + +T = TypeVar("T", bound=WithId) + + +class IndexedList(list[T]): + def append(self, _item: T) -> None: + if _item.id is None: + _item.id = max((i.id or 0 for i in self), default=0) + 1 + return super().append(_item) + + def find_idx(self, _id: int) -> int: + try: + i, _ = next(filter(lambda it: it[1].id == _id, enumerate(self))) + except StopIteration: + raise ValueError(f"Could not find item with id {_id}") + return i + + def get(self, _id: int) -> T: + i = self.find_idx(_id) + return self.__getitem__(i) + + def delete(self, _id: int) -> None: + i = self.find_idx(_id) + return super().__delitem__(i) + + def replace(self, _id: int, _obj: T) -> None: + i = self.find_idx(_id) + super().__setitem__(i, _obj) diff --git a/folkugat_web/model/temes.py b/folkugat_web/model/temes.py index 721249e..be40640 100644 --- a/folkugat_web/model/temes.py +++ b/folkugat_web/model/temes.py @@ -5,16 +5,18 @@ from typing import Optional from folkugat_web.services import ngrams +from ._base import IndexedList, WithId + NGrams = dict[int, list[str]] +class ContentType(enum.Enum): + PARTITURA = "partitura" + AUDIO = "àudio" + OTHER = "enllaç" + + class LinkType(enum.Enum): - SCORE = "score" - AUDIO = "audio" - OTHER = "other" - - -class LinkSubtype(enum.Enum): # Score PDF = "pdf" IMAGE = "image" @@ -24,16 +26,17 @@ class LinkSubtype(enum.Enum): @dataclasses.dataclass -class Link: - type: LinkType - subtype: Optional[LinkSubtype] +class Link(WithId): + content_type: ContentType + link_type: Optional[LinkType] url: str title: str = "" def to_dict(self): return dict( - type=self.type.value, - subtype=self.subtype.value if self.subtype else None, + id=self.id, + content_type=self.content_type.value, + link_type=self.link_type.value if self.link_type else None, url=self.url, title=self.title, ) @@ -41,8 +44,9 @@ class Link: @classmethod def from_dict(cls, d): return cls( - type=LinkType(d["type"]), - subtype=LinkSubtype(d["subtype"]) if d["subtype"] else None, + id=d["id"], + content_type=ContentType(d["content_type"]), + link_type=LinkType(d["link_type"]) if d["link_type"] else None, url=d["url"], title=d["title"], ) @@ -56,12 +60,13 @@ class PropertyField(enum.Enum): @dataclasses.dataclass -class Property: +class Property(WithId): field: PropertyField value: str def to_dict(self): return dict( + id=self.id, field=self.field.value, value=self.value, ) @@ -69,18 +74,20 @@ class Property: @classmethod def from_dict(cls, d): return cls( + id=d["id"], field=PropertyField(d["field"]), value=d["value"], ) @dataclasses.dataclass -class Lyrics: +class Lyrics(WithId): title: str content: str def to_dict(self): return dict( + id=self.id, title=self.title, content=self.content, ) @@ -88,6 +95,7 @@ class Lyrics: @classmethod def from_dict(cls, d): return cls( + id=d["id"], title=d["title"], content=d["content"], ) @@ -98,9 +106,9 @@ class Tema: id: Optional[int] = None # Info title: str = "" - properties: list[Property] = dataclasses.field(default_factory=list) - links: list[Link] = dataclasses.field(default_factory=list) - lyrics: list[Lyrics] = dataclasses.field(default_factory=list) + properties: IndexedList[Property] = dataclasses.field(default_factory=IndexedList) + links: IndexedList[Link] = dataclasses.field(default_factory=IndexedList) + lyrics: IndexedList[Lyrics] = dataclasses.field(default_factory=IndexedList) # Search related alternatives: list[str] = dataclasses.field(default_factory=list) ngrams: NGrams = dataclasses.field(default_factory=dict) @@ -115,9 +123,3 @@ class Tema: def with_ngrams(self): self.compute_ngrams() return self - - def scores(self): - return [link for link in self.links if link.type is LinkType.SCORE] - - def audios(self): - return [link for link in self.links if link.type is LinkType.AUDIO] diff --git a/folkugat_web/services/temes/links.py b/folkugat_web/services/temes/links.py new file mode 100644 index 0000000..35aea64 --- /dev/null +++ b/folkugat_web/services/temes/links.py @@ -0,0 +1,32 @@ +import re +from typing import Optional + +from folkugat_web.model import temes as model + +IMAGE_FORMATS_RE = "|".join([ + 'jpg', 'jpeg', 'png' +]) + +LINK_RES = { + model.LinkType.IMAGE: [ + re.compile(rf"^.*\.({IMAGE_FORMATS_RE})$") + ], + model.LinkType.PDF: [ + re.compile(r"^.*\.pdf$") + ], + model.LinkType.SPOTIFY: [ + re.compile(r"^.*spotify\.com.*$") + ], + model.LinkType.YOUTUBE: [ + re.compile(r"^.*youtube\.com.*$"), + re.compile(r"^.*youtu\.be.*$"), + ] +} + + +def guess_link_type(url: str) -> Optional[model.LinkType]: + for link_type, regexes in LINK_RES.items(): + for regex in regexes: + if regex.match(url): + return link_type + return None diff --git a/folkugat_web/services/temes/write.py b/folkugat_web/services/temes/write.py index 7e3f2ba..a156ed5 100644 --- a/folkugat_web/services/temes/write.py +++ b/folkugat_web/services/temes/write.py @@ -20,29 +20,25 @@ def update_title(tema_id: int, title: str) -> model.Tema: return new_tema -def update_lyric(tema_id: int, lyric_idx: int, lyric: model.Lyrics) -> model.Tema: +def update_lyric(tema_id: int, lyric_id: int, lyric: model.Lyrics) -> model.Tema: with get_connection() as con: tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con) if tema is None: raise ValueError(f"No tune found with tema_id = {tema_id}!") - if lyric_idx > len(tema.lyrics): - raise ValueError(f'Lyric index out of bounds') - tema.lyrics[lyric_idx] = lyric + tema.lyrics.replace(lyric_id, lyric) temes_w.update_tema(tema=tema, con=con) return tema -def delete_lyric(tema_id: int, lyric_idx: int) -> model.Tema: +def delete_lyric(tema_id: int, lyric_id: int) -> model.Tema: with get_connection() as con: tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con) if tema is None: raise ValueError(f"No tune found with tema_id = {tema_id}!") - if lyric_idx > len(tema.lyrics): - raise ValueError(f'Lyric index out of bounds') - del tema.lyrics[lyric_idx] + tema.lyrics.delete(lyric_id) temes_w.update_tema(tema=tema, con=con) return tema @@ -54,9 +50,51 @@ def add_lyric(tema_id: int) -> model.Tema: if tema is None: raise ValueError(f"No tune found with tema_id = {tema_id}!") tema.lyrics.append(model.Lyrics( + id=None, title=tema.title, content="", )) temes_w.update_tema(tema=tema, con=con) return tema + + +def update_link(tema_id: int, link_id: int, link: model.Link) -> model.Tema: + with get_connection() as con: + tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con) + if tema is None: + raise ValueError(f"No tune found with tema_id = {tema_id}!") + + tema.links.replace(link_id, link) + temes_w.update_tema(tema=tema, con=con) + + return tema + + +def delete_link(tema_id: int, link_id: int) -> model.Tema: + with get_connection() as con: + tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con) + if tema is None: + raise ValueError(f"No tune found with tema_id = {tema_id}!") + + tema.links.delete(link_id) + temes_w.update_tema(tema=tema, con=con) + + return tema + + +def add_link(tema_id: int) -> model.Tema: + with get_connection() as con: + tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con) + if tema is None: + raise ValueError(f"No tune found with tema_id = {tema_id}!") + tema.links.append(model.Link( + id=None, + title="", + url="", + content_type=model.ContentType.OTHER, + link_type=None, + )) + temes_w.update_tema(tema=tema, con=con) + + return tema