Added indexed lists and link edition in tunes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
|
.direnv
|
||||||
|
|
||||||
**/**.pyc
|
**/**.pyc
|
||||||
|
|||||||
@@ -13,11 +13,31 @@ def title_editor(
|
|||||||
return tema.title_editor(request=request, logged_in=logged_in, tema_id=tema_id)
|
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(
|
def lyric_editor(
|
||||||
request: Request,
|
request: Request,
|
||||||
logged_in: auth.RequireLogin,
|
logged_in: auth.RequireLogin,
|
||||||
tema_id: int,
|
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,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.params import Form
|
from fastapi.params import Form, Param
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from folkugat_web.api import router
|
from folkugat_web.api import router
|
||||||
from folkugat_web.fragments import tema, temes
|
from folkugat_web.fragments import tema, temes
|
||||||
from folkugat_web.model import temes as model
|
from folkugat_web.model import temes as model
|
||||||
from folkugat_web.services import auth
|
from folkugat_web.services import auth
|
||||||
from folkugat_web.services.temes import write as temes_w
|
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
|
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)
|
return tema.title(request=request, tema=new_tema, logged_in=logged_in)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/tema/{tema_id}/lyric/{lyric_idx}")
|
@router.get("/api/tema/{tema_id}/lyric/{lyric_id}")
|
||||||
def lyric(request: Request, logged_in: auth.LoggedIn, tema_id: int, lyric_idx: int):
|
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_idx=lyric_idx)
|
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(
|
def set_lyric(
|
||||||
request: Request,
|
request: Request,
|
||||||
logged_in: auth.RequireLogin,
|
logged_in: auth.RequireLogin,
|
||||||
tema_id: int,
|
tema_id: int,
|
||||||
lyric_idx: int,
|
lyric_id: int,
|
||||||
title: Annotated[str, Form()],
|
title: Annotated[str, Form()],
|
||||||
lyric: Annotated[str, Form()],
|
lyric: Annotated[str, Form()],
|
||||||
):
|
):
|
||||||
new_lyric = model.Lyrics(title=title, content=lyric.strip())
|
new_lyric = model.Lyrics(id=lyric_id, title=title, content=lyric.strip())
|
||||||
temes_w.update_lyric(tema_id=tema_id, lyric_idx=lyric_idx, lyric=new_lyric)
|
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_idx=lyric_idx)
|
return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_id=lyric_id)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/tema/{tema_id}/lyric")
|
@router.post("/api/tema/{tema_id}/lyric")
|
||||||
@@ -71,14 +72,95 @@ def add_lyric(
|
|||||||
tema_id: int,
|
tema_id: int,
|
||||||
):
|
):
|
||||||
new_tema = temes_w.add_lyric(tema_id=tema_id)
|
new_tema = temes_w.add_lyric(tema_id=tema_id)
|
||||||
lyric_idx = len(new_tema.lyrics) - 1
|
lyric_id = new_tema.lyrics[-1].id
|
||||||
return tema.lyric_editor(request=request, logged_in=logged_in, tema_id=tema_id, lyric_idx=lyric_idx)
|
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(
|
def delete_lyric(
|
||||||
tema_id: int,
|
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()
|
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,
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<li id="tema-link-{{ link.id }}">
|
||||||
|
<form class="flex flex-row items-start">
|
||||||
|
{% include "fragments/tema/link_icon.html" %}
|
||||||
|
<div class="grow my-2">
|
||||||
|
<p class="text-sm text-beige">
|
||||||
|
<select id="tema-link-{{ link.id }}-content-type"
|
||||||
|
name="content_type"
|
||||||
|
value="{{ link.content_type.value.capitalize() }}"
|
||||||
|
class="border border-yellow-50 focus:outline-none
|
||||||
|
rounded
|
||||||
|
text-yellow-50 text-center
|
||||||
|
bg-brown p-0 m-0">
|
||||||
|
<option value="{{ link.content_type.value }}">
|
||||||
|
{{ link.content_type.value.capitalize() }}
|
||||||
|
</option>
|
||||||
|
{% for ct in ContentType %}
|
||||||
|
{% if ct != link.content_type %}
|
||||||
|
<option value="{{ ct.value }}">
|
||||||
|
{{ ct.value.capitalize() }}
|
||||||
|
</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input name="title"
|
||||||
|
placeholder="Títol de l'enllaç"
|
||||||
|
value="{{ link.title }}"
|
||||||
|
class="border border-beige focus:outline-none
|
||||||
|
rounded w-full
|
||||||
|
bg-brown p-1 my-1"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input name="url"
|
||||||
|
placeholder="URL de l'enllaç"
|
||||||
|
value="{{ link.url }}"
|
||||||
|
class="border border-beige focus:outline-none
|
||||||
|
rounded w-full
|
||||||
|
bg-brown p-1 my-1"
|
||||||
|
hx-get="/api/tema/{{ tema.id }}/link/{{ link_id }}/icon"
|
||||||
|
hx-trigger="keyup delay:500ms changed"
|
||||||
|
hx-target="#link-icon-{{ link.id }}"
|
||||||
|
hx-include="[name='content_type']"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="m-2 text-sm text-beige">
|
||||||
|
<button title="Desa els canvis"
|
||||||
|
class="mx-1"
|
||||||
|
hx-put="/api/tema/{{ tema.id }}/link/{{ link.id }}"
|
||||||
|
hx-target="#tema-link-{{ link.id }}"
|
||||||
|
hx-swap="outerHTML">
|
||||||
|
<i class="fa fa-check" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button title="Descarta els canvis"
|
||||||
|
class="mx-1"
|
||||||
|
hx-get="/api/tema/{{ tema.id }}/link/{{ link.id }}"
|
||||||
|
hx-target="#tema-link-{{ link.id }}"
|
||||||
|
hx-swap="outerHTML">
|
||||||
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form id="tema-lyric-{{ lyric_idx }}">
|
<form id="tema-lyric-{{ lyric.id }}">
|
||||||
<h5 class="text-sm text-beige text-right">
|
<h5 class="text-sm text-beige text-right">
|
||||||
<input name="title"
|
<input name="title"
|
||||||
placeholder="Nom de la lletra"
|
placeholder="Nom de la lletra"
|
||||||
@@ -9,15 +9,15 @@
|
|||||||
/>
|
/>
|
||||||
<button title="Desa els canvis"
|
<button title="Desa els canvis"
|
||||||
class="mx-1"
|
class="mx-1"
|
||||||
hx-put="/api/tema/{{ tema.id }}/lyric/{{ lyric_idx }}"
|
hx-put="/api/tema/{{ tema.id }}/lyric/{{ lyric.id }}"
|
||||||
hx-target="#tema-lyric-{{ lyric_idx }}"
|
hx-target="#tema-lyric-{{ lyric.id }}"
|
||||||
hx-swap="outerHTML">
|
hx-swap="outerHTML">
|
||||||
<i class="fa fa-check" aria-hidden="true"></i>
|
<i class="fa fa-check" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button title="Descarta els canvis"
|
<button title="Descarta els canvis"
|
||||||
class="mx-1"
|
class="mx-1"
|
||||||
hx-get="/api/tema/{{ tema.id }}/lyric/{{ lyric_idx }}"
|
hx-get="/api/tema/{{ tema.id }}/lyric/{{ lyric.id }}"
|
||||||
hx-target="#tema-lyric-{{ lyric_idx }}"
|
hx-target="#tema-lyric-{{ lyric.id }}"
|
||||||
hx-swap="outerHTML">
|
hx-swap="outerHTML">
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,21 +1,29 @@
|
|||||||
<li class="flex flex-row items-start">
|
<li id="tema-link-{{ link.id }}"
|
||||||
<div class="p-2 m-2 text-beige border border-beige rounded-md">
|
class="flex flex-row items-start">
|
||||||
<a href="{{ link.url }}" target="_blank">
|
{% include "fragments/tema/link_icon.html" %}
|
||||||
{% if link.subtype == LinkSubtype.SPOTIFY %}
|
|
||||||
{% include "icons/spotify.svg" %}
|
|
||||||
{% elif link.subtype == LinkSubtype.YOUTUBE %}
|
|
||||||
{% include "icons/youtube.svg" %}
|
|
||||||
{% elif link.subtype == LinkSubtype.PDF %}
|
|
||||||
{% include "icons/pdf.svg" %}
|
|
||||||
{% elif link.type == LinkType.AUDIO %}
|
|
||||||
{% include "icons/notes.svg" %}
|
|
||||||
{% else %}
|
|
||||||
{% include "icons/link.svg" %}
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
<p class="text-sm text-beige">Partitura</p>
|
<p class="text-sm text-beige">
|
||||||
<p>Hola</p>
|
{{ link.content_type.value.capitalize() }}
|
||||||
|
</p>
|
||||||
|
{% if link.title %}
|
||||||
|
<p>{{ link.title }}</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% if logged_in %}
|
||||||
|
<div class="grow"></div>
|
||||||
|
<div class="m-2 text-sm text-beige">
|
||||||
|
<button title="Modifica l'enllaç"
|
||||||
|
hx-get="/api/tema/{{ tema.id }}/editor/link/{{ link.id }}"
|
||||||
|
hx-target="#tema-link-{{ link.id }}"
|
||||||
|
hx-swap="outerHTML">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button title="Esborra l'enllaç"
|
||||||
|
hx-delete="/api/tema/{{ tema.id }}/link/{{ link.id }}"
|
||||||
|
hx-target="#tema-link-{{ link.id }}"
|
||||||
|
hx-swap="outerHTML">
|
||||||
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
16
folkugat_web/assets/templates/fragments/tema/link_icon.html
Normal file
16
folkugat_web/assets/templates/fragments/tema/link_icon.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<div id="link-icon-{{ link.id }}"
|
||||||
|
class="p-2 m-2 text-beige border border-beige rounded-md">
|
||||||
|
<a href="{{ link.url }}" target="_blank">
|
||||||
|
{% if link.link_type == LinkType.SPOTIFY %}
|
||||||
|
{% include "icons/spotify.svg" %}
|
||||||
|
{% elif link.link_type == LinkType.YOUTUBE %}
|
||||||
|
{% include "icons/youtube.svg" %}
|
||||||
|
{% elif link.link_type == LinkType.PDF %}
|
||||||
|
{% include "icons/pdf.svg" %}
|
||||||
|
{% elif link.content_type == ContentType.AUDIO %}
|
||||||
|
{% include "icons/notes.svg" %}
|
||||||
|
{% else %}
|
||||||
|
{% include "icons/link.svg" %}
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -4,13 +4,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if tema.links %}
|
{% if tema.links %}
|
||||||
{% for link in tema.links %}
|
|
||||||
<ul class="flex flex-col justify-center"
|
<ul class="flex flex-col justify-center"
|
||||||
id="new-link-target">
|
id="new-link-target">
|
||||||
{% set link_idx = loop.index0 %}
|
{% for link in tema.links %}
|
||||||
{% include "fragments/tema/link.html" %}
|
{% include "fragments/tema/link.html" %}
|
||||||
</ul>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if logged_in %}
|
{% if logged_in %}
|
||||||
@@ -19,7 +18,7 @@
|
|||||||
class="text-sm text-beige text-right"
|
class="text-sm text-beige text-right"
|
||||||
hx-post="/api/tema/{{ tema.id }}/link"
|
hx-post="/api/tema/{{ tema.id }}/link"
|
||||||
hx-target="#new-link-target"
|
hx-target="#new-link-target"
|
||||||
hx-swap="beforebegin">
|
hx-swap="beforeend transition:true">
|
||||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
Afegeix un enllaç
|
Afegeix un enllaç
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
<div id="tema-lyric-{{ lyric_idx }}">
|
<div id="tema-lyric-{{ lyric.id }}">
|
||||||
<h5 class="text-sm text-beige text-right">
|
<h5 class="text-sm text-beige text-right">
|
||||||
{{ lyric.title }}
|
{{ lyric.title }}
|
||||||
{% if logged_in %}
|
{% if logged_in %}
|
||||||
<button title="Modifica la lletra"
|
<button title="Modifica la lletra"
|
||||||
class="mx-1"
|
class="mx-1"
|
||||||
hx-get="/api/tema/{{ tema.id }}/editor/lyric/{{ lyric_idx }}"
|
hx-get="/api/tema/{{ tema.id }}/editor/lyric/{{ lyric.id }}"
|
||||||
hx-target="#tema-lyric-{{ lyric_idx }}"
|
hx-target="#tema-lyric-{{ lyric.id }}"
|
||||||
hx-swap="outerHTML">
|
hx-swap="outerHTML">
|
||||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button title="Esborra la lletra"
|
<button title="Esborra la lletra"
|
||||||
class="mx-1"
|
class="mx-1"
|
||||||
hx-delete="/api/tema/{{ tema.id }}/lyric/{{ lyric_idx }}"
|
hx-delete="/api/tema/{{ tema.id }}/lyric/{{ lyric.id }}"
|
||||||
hx-target="#tema-lyric-{{ lyric_idx }}"
|
hx-target="#tema-lyric-{{ lyric.id }}"
|
||||||
hx-swap="outerHTML">
|
hx-swap="outerHTML">
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
{% if tema.lyrics %}
|
{% if tema.lyrics %}
|
||||||
{% for lyric in tema.lyrics %}
|
{% for lyric in tema.lyrics %}
|
||||||
{% set lyric_idx = loop.index0 %}
|
|
||||||
{% include "fragments/tema/lyric.html" %}
|
{% include "fragments/tema/lyric.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -4,18 +4,6 @@
|
|||||||
{% include "fragments/tema/title.html" %}
|
{% include "fragments/tema/title.html" %}
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
|
|
||||||
<!-- {% if tema.scores() %} -->
|
|
||||||
<!-- temes -->
|
|
||||||
<!-- <h4 class="text-xl text-beige">Tema</h4> -->
|
|
||||||
<!-- <hr class="h-px mt-1 mb-3 bg-beige border-0"> -->
|
|
||||||
<!-- {% for file in tema.files %} -->
|
|
||||||
<!-- {% if file.type == FileType.PDF %} -->
|
|
||||||
<!-- {% set pdf_url = file.path %} -->
|
|
||||||
<!-- {% include "fragments/pdf_viewer.html" %} -->
|
|
||||||
<!-- {% endif %} -->
|
|
||||||
<!-- {% endfor %} -->
|
|
||||||
<!-- {% endif %} -->
|
|
||||||
|
|
||||||
{% include "fragments/tema/lyrics.html" %}
|
{% include "fragments/tema/lyrics.html" %}
|
||||||
{% include "fragments/tema/links.html" %}
|
{% include "fragments/tema/links.html" %}
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,16 @@
|
|||||||
{% for link in tema.links %}
|
{% for link in tema.links %}
|
||||||
<li class="p-2 m-2 text-beige border border-beige rounded-md">
|
<li class="p-2 m-2 text-beige border border-beige rounded-md">
|
||||||
<a href="{{ link.url }}" target="_blank">
|
<a href="{{ link.url }}" target="_blank">
|
||||||
{% if link.type == LinkType.AUDIO %}
|
{% if link.content_type == ContentType.AUDIO %}
|
||||||
{% if link.subtype == LinkSubtype.SPOTIFY %}
|
{% if link.link_type == LinkType.SPOTIFY %}
|
||||||
{% include "icons/spotify.svg" %}
|
{% include "icons/spotify.svg" %}
|
||||||
{% elif link.subtype == LinkSubtype.YOUTUBE %}
|
{% elif link.link_type == LinkType.YOUTUBE %}
|
||||||
{% include "icons/youtube.svg" %}
|
{% include "icons/youtube.svg" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include "icons/notes.svg" %}
|
{% include "icons/notes.svg" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif link.type == LinkType.SCORE %}
|
{% elif link.content_type == ContentType.PARTITURA %}
|
||||||
{% if link.subtype == LinkSubtype.PDF %}
|
{% if link.link_type == LinkType.PDF %}
|
||||||
{% include "icons/pdf.svg" %}
|
{% include "icons/pdf.svg" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include "icons/link.svg" %}
|
{% include "icons/link.svg" %}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from folkugat_web.model import IndexedList
|
||||||
from folkugat_web.model import temes as model
|
from folkugat_web.model import temes as model
|
||||||
|
|
||||||
|
|
||||||
@@ -27,9 +28,9 @@ def row_to_tema(row: tuple) -> model.Tema:
|
|||||||
return model.Tema(
|
return model.Tema(
|
||||||
id=row[0],
|
id=row[0],
|
||||||
title=row[1],
|
title=row[1],
|
||||||
properties=list(map(model.Property.from_dict, json.loads(row[2]))),
|
properties=IndexedList(map(model.Property.from_dict, json.loads(row[2]))),
|
||||||
links=list(map(model.Link.from_dict, json.loads(row[3]))),
|
links=IndexedList(map(model.Link.from_dict, json.loads(row[3]))),
|
||||||
lyrics=list(map(model.Lyrics.from_dict, json.loads(row[4]))),
|
lyrics=IndexedList(map(model.Lyrics.from_dict, json.loads(row[4]))),
|
||||||
alternatives=json.loads(row[5]),
|
alternatives=json.loads(row[5]),
|
||||||
ngrams=cell_to_ngrams(row[6]),
|
ngrams=cell_to_ngrams(row[6]),
|
||||||
modification_date=datetime.datetime.fromisoformat(row[7]),
|
modification_date=datetime.datetime.fromisoformat(row[7]),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from folkugat_web.model import IndexedList
|
||||||
from folkugat_web.model import temes as model
|
from folkugat_web.model import temes as model
|
||||||
|
|
||||||
TEMES = [
|
TEMES = [
|
||||||
@@ -9,26 +10,29 @@ TEMES = [
|
|||||||
# ---
|
# ---
|
||||||
model.Tema(
|
model.Tema(
|
||||||
title="Pasdoble de Muntanya (Cançó amb el nom molt llarg)",
|
title="Pasdoble de Muntanya (Cançó amb el nom molt llarg)",
|
||||||
links=[
|
links=IndexedList([
|
||||||
model.Link(
|
model.Link(
|
||||||
type=model.LinkType.AUDIO,
|
id=0,
|
||||||
subtype=model.LinkSubtype.SPOTIFY,
|
content_type=model.ContentType.AUDIO,
|
||||||
|
link_type=model.LinkType.SPOTIFY,
|
||||||
url="https://open.spotify.com/track/4j9Krf19c5USmMvVUCoeWa?si=3023d1d83f814886",
|
url="https://open.spotify.com/track/4j9Krf19c5USmMvVUCoeWa?si=3023d1d83f814886",
|
||||||
|
title="Versió de l'Orquestrina Trama",
|
||||||
),
|
),
|
||||||
],
|
]),
|
||||||
hidden=False,
|
hidden=False,
|
||||||
).with_ngrams(),
|
).with_ngrams(),
|
||||||
# ---
|
# ---
|
||||||
model.Tema(
|
model.Tema(
|
||||||
title="Astrid Waltz",
|
title="Astrid Waltz",
|
||||||
alternatives=["vals"],
|
alternatives=["vals"],
|
||||||
links=[
|
links=IndexedList([
|
||||||
model.Link(
|
model.Link(
|
||||||
type=model.LinkType.OTHER,
|
id=0,
|
||||||
subtype=None,
|
content_type=model.ContentType.OTHER,
|
||||||
|
link_type=None,
|
||||||
url="https://marc.sastre.cat/folkugat",
|
url="https://marc.sastre.cat/folkugat",
|
||||||
)
|
)
|
||||||
],
|
]),
|
||||||
hidden=False,
|
hidden=False,
|
||||||
).with_ngrams(),
|
).with_ngrams(),
|
||||||
# ---
|
# ---
|
||||||
@@ -45,13 +49,14 @@ TEMES = [
|
|||||||
# ---
|
# ---
|
||||||
model.Tema(
|
model.Tema(
|
||||||
title="El Gitano",
|
title="El Gitano",
|
||||||
links=[
|
links=IndexedList([
|
||||||
model.Link(
|
model.Link(
|
||||||
type=model.LinkType.SCORE,
|
id=0,
|
||||||
subtype=model.LinkSubtype.PDF,
|
content_type=model.ContentType.PARTITURA,
|
||||||
|
link_type=model.LinkType.PDF,
|
||||||
url="/db/temes/1/tema.pdf",
|
url="/db/temes/1/tema.pdf",
|
||||||
)
|
)
|
||||||
],
|
]),
|
||||||
hidden=False,
|
hidden=False,
|
||||||
).with_ngrams(),
|
).with_ngrams(),
|
||||||
# ---
|
# ---
|
||||||
@@ -68,8 +73,9 @@ TEMES = [
|
|||||||
# ---
|
# ---
|
||||||
model.Tema(
|
model.Tema(
|
||||||
title="Malaguenya de Barxeta",
|
title="Malaguenya de Barxeta",
|
||||||
lyrics=[
|
lyrics=IndexedList([
|
||||||
model.Lyrics(
|
model.Lyrics(
|
||||||
|
id=0,
|
||||||
title="Malaguenya de Barxeta",
|
title="Malaguenya de Barxeta",
|
||||||
content="""
|
content="""
|
||||||
Mira si he corregut terres
|
Mira si he corregut terres
|
||||||
@@ -93,13 +99,14 @@ d'allà on renaix de les cendres
|
|||||||
el meu País Valencià.
|
el meu País Valencià.
|
||||||
""".strip(),
|
""".strip(),
|
||||||
),
|
),
|
||||||
],
|
]),
|
||||||
properties=[
|
properties=IndexedList([
|
||||||
model.Property(
|
model.Property(
|
||||||
model.PropertyField.AUTOR,
|
id=0,
|
||||||
"Pep Jimeno 'Botifarra'"
|
field=model.PropertyField.AUTOR,
|
||||||
|
value="Pep Jimeno 'Botifarra'"
|
||||||
)
|
)
|
||||||
],
|
]),
|
||||||
hidden=False,
|
hidden=False,
|
||||||
).with_ngrams(),
|
).with_ngrams(),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -25,11 +25,10 @@ def footer(request, value, logged_in):
|
|||||||
|
|
||||||
|
|
||||||
def nota(request):
|
def nota(request):
|
||||||
response = templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"fragments/nota/nota.html",
|
"fragments/nota/nota.html",
|
||||||
{
|
{
|
||||||
"request": request,
|
"request": request,
|
||||||
}
|
},
|
||||||
|
headers={"HX-Refresh": "true"},
|
||||||
)
|
)
|
||||||
response.headers["HX-Refresh"] = "true"
|
|
||||||
return response
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from typing import Optional
|
|||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from folkugat_web.model import temes as model
|
from folkugat_web.model import temes as model
|
||||||
from folkugat_web.services.temes import query as temes_q
|
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
|
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)
|
tema = temes_q.get_tema_by_id(tema_id)
|
||||||
if tema is None:
|
if tema is None:
|
||||||
raise ValueError(f"No tune exists for tema_id: {tema_id}")
|
raise ValueError(f"No tune exists for tema_id: {tema_id}")
|
||||||
if len(tema.lyrics) < lyric_idx:
|
lyric = tema.lyrics.get(lyric_id)
|
||||||
raise ValueError(f'Lyric index out of bounds')
|
|
||||||
lyric = tema.lyrics[lyric_idx]
|
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"fragments/tema/lyric.html",
|
"fragments/tema/lyric.html",
|
||||||
@@ -47,19 +46,17 @@ def lyric(request: Request, logged_in: bool, tema_id: int, lyric_idx: int):
|
|||||||
"request": request,
|
"request": request,
|
||||||
"logged_in": logged_in,
|
"logged_in": logged_in,
|
||||||
"tema": tema,
|
"tema": tema,
|
||||||
"lyric_idx": lyric_idx,
|
"lyric_id": lyric_id,
|
||||||
"lyric": lyric,
|
"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)
|
tema = temes_q.get_tema_by_id(tema_id)
|
||||||
if tema is None:
|
if tema is None:
|
||||||
raise ValueError(f"No tune exists for tema_id: {tema_id}")
|
raise ValueError(f"No tune exists for tema_id: {tema_id}")
|
||||||
if len(tema.lyrics) < lyric_idx:
|
lyric = tema.lyrics.get(lyric_id)
|
||||||
raise ValueError(f'Lyric index out of bounds')
|
|
||||||
lyric = tema.lyrics[lyric_idx]
|
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"fragments/tema/editor/lyric.html",
|
"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,
|
"request": request,
|
||||||
"logged_in": logged_in,
|
"logged_in": logged_in,
|
||||||
"tema": tema,
|
"tema": tema,
|
||||||
"lyric_idx": lyric_idx,
|
"lyric_id": lyric_id,
|
||||||
"lyric": lyric,
|
"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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ def temes_busca_result(request: Request, tema: model.Tema, logged_in: bool):
|
|||||||
"request": request,
|
"request": request,
|
||||||
"logged_in": logged_in,
|
"logged_in": logged_in,
|
||||||
"tema": tema,
|
"tema": tema,
|
||||||
"LinkSubtype": model.LinkSubtype,
|
|
||||||
"LinkType": model.LinkType,
|
"LinkType": model.LinkType,
|
||||||
|
"ContentType": model.ContentType,
|
||||||
}
|
}
|
||||||
).body.decode('utf-8')
|
).body.decode('utf-8')
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ def tema(request: Request, tema_id: int, logged_in: bool):
|
|||||||
"request": request,
|
"request": request,
|
||||||
"logged_in": logged_in,
|
"logged_in": logged_in,
|
||||||
"Pages": Pages,
|
"Pages": Pages,
|
||||||
"LinkSubtype": model.LinkSubtype,
|
|
||||||
"LinkType": model.LinkType,
|
"LinkType": model.LinkType,
|
||||||
|
"ContentType": model.ContentType,
|
||||||
"tema": tema,
|
"tema": tema,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from ._base import IndexedList
|
||||||
|
|||||||
36
folkugat_web/model/_base.py
Normal file
36
folkugat_web/model/_base.py
Normal file
@@ -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)
|
||||||
@@ -5,16 +5,18 @@ from typing import Optional
|
|||||||
|
|
||||||
from folkugat_web.services import ngrams
|
from folkugat_web.services import ngrams
|
||||||
|
|
||||||
|
from ._base import IndexedList, WithId
|
||||||
|
|
||||||
NGrams = dict[int, list[str]]
|
NGrams = dict[int, list[str]]
|
||||||
|
|
||||||
|
|
||||||
|
class ContentType(enum.Enum):
|
||||||
|
PARTITURA = "partitura"
|
||||||
|
AUDIO = "àudio"
|
||||||
|
OTHER = "enllaç"
|
||||||
|
|
||||||
|
|
||||||
class LinkType(enum.Enum):
|
class LinkType(enum.Enum):
|
||||||
SCORE = "score"
|
|
||||||
AUDIO = "audio"
|
|
||||||
OTHER = "other"
|
|
||||||
|
|
||||||
|
|
||||||
class LinkSubtype(enum.Enum):
|
|
||||||
# Score
|
# Score
|
||||||
PDF = "pdf"
|
PDF = "pdf"
|
||||||
IMAGE = "image"
|
IMAGE = "image"
|
||||||
@@ -24,16 +26,17 @@ class LinkSubtype(enum.Enum):
|
|||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Link:
|
class Link(WithId):
|
||||||
type: LinkType
|
content_type: ContentType
|
||||||
subtype: Optional[LinkSubtype]
|
link_type: Optional[LinkType]
|
||||||
url: str
|
url: str
|
||||||
title: str = ""
|
title: str = ""
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return dict(
|
return dict(
|
||||||
type=self.type.value,
|
id=self.id,
|
||||||
subtype=self.subtype.value if self.subtype else None,
|
content_type=self.content_type.value,
|
||||||
|
link_type=self.link_type.value if self.link_type else None,
|
||||||
url=self.url,
|
url=self.url,
|
||||||
title=self.title,
|
title=self.title,
|
||||||
)
|
)
|
||||||
@@ -41,8 +44,9 @@ class Link:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, d):
|
def from_dict(cls, d):
|
||||||
return cls(
|
return cls(
|
||||||
type=LinkType(d["type"]),
|
id=d["id"],
|
||||||
subtype=LinkSubtype(d["subtype"]) if d["subtype"] else None,
|
content_type=ContentType(d["content_type"]),
|
||||||
|
link_type=LinkType(d["link_type"]) if d["link_type"] else None,
|
||||||
url=d["url"],
|
url=d["url"],
|
||||||
title=d["title"],
|
title=d["title"],
|
||||||
)
|
)
|
||||||
@@ -56,12 +60,13 @@ class PropertyField(enum.Enum):
|
|||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Property:
|
class Property(WithId):
|
||||||
field: PropertyField
|
field: PropertyField
|
||||||
value: str
|
value: str
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return dict(
|
return dict(
|
||||||
|
id=self.id,
|
||||||
field=self.field.value,
|
field=self.field.value,
|
||||||
value=self.value,
|
value=self.value,
|
||||||
)
|
)
|
||||||
@@ -69,18 +74,20 @@ class Property:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, d):
|
def from_dict(cls, d):
|
||||||
return cls(
|
return cls(
|
||||||
|
id=d["id"],
|
||||||
field=PropertyField(d["field"]),
|
field=PropertyField(d["field"]),
|
||||||
value=d["value"],
|
value=d["value"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Lyrics:
|
class Lyrics(WithId):
|
||||||
title: str
|
title: str
|
||||||
content: str
|
content: str
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return dict(
|
return dict(
|
||||||
|
id=self.id,
|
||||||
title=self.title,
|
title=self.title,
|
||||||
content=self.content,
|
content=self.content,
|
||||||
)
|
)
|
||||||
@@ -88,6 +95,7 @@ class Lyrics:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, d):
|
def from_dict(cls, d):
|
||||||
return cls(
|
return cls(
|
||||||
|
id=d["id"],
|
||||||
title=d["title"],
|
title=d["title"],
|
||||||
content=d["content"],
|
content=d["content"],
|
||||||
)
|
)
|
||||||
@@ -98,9 +106,9 @@ class Tema:
|
|||||||
id: Optional[int] = None
|
id: Optional[int] = None
|
||||||
# Info
|
# Info
|
||||||
title: str = ""
|
title: str = ""
|
||||||
properties: list[Property] = dataclasses.field(default_factory=list)
|
properties: IndexedList[Property] = dataclasses.field(default_factory=IndexedList)
|
||||||
links: list[Link] = dataclasses.field(default_factory=list)
|
links: IndexedList[Link] = dataclasses.field(default_factory=IndexedList)
|
||||||
lyrics: list[Lyrics] = dataclasses.field(default_factory=list)
|
lyrics: IndexedList[Lyrics] = dataclasses.field(default_factory=IndexedList)
|
||||||
# Search related
|
# Search related
|
||||||
alternatives: list[str] = dataclasses.field(default_factory=list)
|
alternatives: list[str] = dataclasses.field(default_factory=list)
|
||||||
ngrams: NGrams = dataclasses.field(default_factory=dict)
|
ngrams: NGrams = dataclasses.field(default_factory=dict)
|
||||||
@@ -115,9 +123,3 @@ class Tema:
|
|||||||
def with_ngrams(self):
|
def with_ngrams(self):
|
||||||
self.compute_ngrams()
|
self.compute_ngrams()
|
||||||
return self
|
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]
|
|
||||||
|
|||||||
32
folkugat_web/services/temes/links.py
Normal file
32
folkugat_web/services/temes/links.py
Normal file
@@ -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
|
||||||
@@ -20,29 +20,25 @@ def update_title(tema_id: int, title: str) -> model.Tema:
|
|||||||
return new_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:
|
with get_connection() as con:
|
||||||
tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con)
|
tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con)
|
||||||
if tema is None:
|
if tema is None:
|
||||||
raise ValueError(f"No tune found with tema_id = {tema_id}!")
|
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)
|
temes_w.update_tema(tema=tema, con=con)
|
||||||
|
|
||||||
return tema
|
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:
|
with get_connection() as con:
|
||||||
tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con)
|
tema = temes_q.get_tema_by_id(tema_id=tema_id, con=con)
|
||||||
if tema is None:
|
if tema is None:
|
||||||
raise ValueError(f"No tune found with tema_id = {tema_id}!")
|
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)
|
temes_w.update_tema(tema=tema, con=con)
|
||||||
|
|
||||||
return tema
|
return tema
|
||||||
@@ -54,9 +50,51 @@ def add_lyric(tema_id: int) -> model.Tema:
|
|||||||
if tema is None:
|
if tema is None:
|
||||||
raise ValueError(f"No tune found with tema_id = {tema_id}!")
|
raise ValueError(f"No tune found with tema_id = {tema_id}!")
|
||||||
tema.lyrics.append(model.Lyrics(
|
tema.lyrics.append(model.Lyrics(
|
||||||
|
id=None,
|
||||||
title=tema.title,
|
title=tema.title,
|
||||||
content="",
|
content="",
|
||||||
))
|
))
|
||||||
temes_w.update_tema(tema=tema, con=con)
|
temes_w.update_tema(tema=tema, con=con)
|
||||||
|
|
||||||
return tema
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user