diff --git a/flake.nix b/flake.nix
index 55de1c8..c3cadb8 100644
--- a/flake.nix
+++ b/flake.nix
@@ -59,6 +59,7 @@
shellHook = ''
export PYTHONPATH=${pythonEnv}/${python.sitePackages}:$PYTHONPATH
+ export PYTHONPATH=$(pwd):$PYTHONPATH
'';
};
});
diff --git a/folkugat_web/api/tema/__init__.py b/folkugat_web/api/tema/__init__.py
index aba1bbe..962bbf4 100644
--- a/folkugat_web/api/tema/__init__.py
+++ b/folkugat_web/api/tema/__init__.py
@@ -1 +1 @@
-from . import editor, index
+from . import editor, index, links, lyrics, properties
diff --git a/folkugat_web/api/tema/editor.py b/folkugat_web/api/tema/editor.py
index 4ae8578..1fa193c 100644
--- a/folkugat_web/api/tema/editor.py
+++ b/folkugat_web/api/tema/editor.py
@@ -11,78 +11,3 @@ def title_editor(
tema_id: int,
):
return tema.title_editor(request=request, logged_in=logged_in, tema_id=tema_id)
-
-
-@router.get("/api/tema/{tema_id}/editor/lyric/{lyric_id}")
-def lyric_editor(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- lyric_id: int,
-):
- 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,
- )
-
-
-@router.get("/api/tema/{tema_id}/editor/link/{link_id}/url")
-def link_editor_url_input(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- link_id: int,
-):
- return tema.link_editor_url(
- request=request,
- logged_in=logged_in,
- tema_id=tema_id,
- link_id=link_id,
- )
-
-
-@router.get("/api/tema/{tema_id}/editor/link/{link_id}/file")
-def link_editor_file_input(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- link_id: int,
-):
- return tema.link_editor_file(
- request=request,
- logged_in=logged_in,
- tema_id=tema_id,
- link_id=link_id,
- )
-
-
-@router.get("/api/tema/{tema_id}/editor/property/{property_id}")
-def property_editor(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- property_id: int,
-):
- return tema.property_editor(
- request=request,
- logged_in=logged_in,
- tema_id=tema_id,
- property_id=property_id,
- )
diff --git a/folkugat_web/api/tema/index.py b/folkugat_web/api/tema/index.py
index b6984d2..d73fd19 100644
--- a/folkugat_web/api/tema/index.py
+++ b/folkugat_web/api/tema/index.py
@@ -1,15 +1,18 @@
from typing import Annotated
-from fastapi import Request, UploadFile
-from fastapi.params import File, Form, Param
+from fastapi import HTTPException, Request
+from fastapi.params import Form
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, files
+from folkugat_web.services import auth
+from folkugat_web.services.temes import links as links_service
+from folkugat_web.services.temes import lyrics as lyrics_service
+from folkugat_web.services.temes import properties as properties_service
+from folkugat_web.services.temes import query as temes_q
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.utils import FnChain
@router.get("/tema/{tema_id}")
@@ -27,7 +30,17 @@ def page(request: Request, logged_in: auth.LoggedIn, tema_id: int):
@router.get("/api/tema/{tema_id}")
def contingut(request: Request, logged_in: auth.LoggedIn, tema_id: int):
- return temes.tema(request, tema_id, logged_in)
+ tema = temes_q.get_tema_by_id(tema_id)
+ if not tema:
+ raise HTTPException(status_code=404, detail="Could not find tune")
+ tema = (
+ FnChain.transform(tema) |
+ temes_q.tema_compute_stats |
+ links_service.add_links_to_tema |
+ lyrics_service.add_lyrics_to_tema |
+ properties_service.add_properties_to_tema
+ ).result()
+ return temes.tema(request, logged_in, tema)
@router.delete("/api/tema/{tema_id}")
@@ -62,190 +75,6 @@ def set_title(
return tema.title(request=request, tema=new_tema, logged_in=logged_in)
-@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_id}")
-def set_lyric(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- lyric_id: int,
- title: Annotated[str, Form()],
- lyric: Annotated[str, Form()],
-):
- 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")
-def add_lyric(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
-):
- new_tema = temes_w.add_lyric(tema_id=tema_id)
- 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_id}")
-def delete_lyric(
- tema_id: int,
- lyric_id: int,
-):
- _ = 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}")
-async def set_link(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- link_id: int,
- content_type: Annotated[model.ContentType, Form()],
- title: Annotated[str | None, Form()] = None,
- url: Annotated[str | None, Form()] = None,
- upload_file: Annotated[UploadFile | None, File()] = None,
-):
- if upload_file:
- url = await files.store_file(tema_id=tema_id, upload_file=upload_file)
-
- link_type = guess_link_type(url or '')
- new_link = model.Link(
- id=link_id,
- content_type=content_type,
- link_type=link_type,
- url=url or '',
- title=title or '',
- )
- _ = 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,
-):
- _tema = temes_w.delete_link(tema_id=tema_id, link_id=link_id)
- return HTMLResponse(
- headers={
- "HX-Trigger": f"reload-tema-{tema_id}-score"
- }
- )
-
-
-@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,
- )
-
-
-@router.get("/api/tema/{tema_id}/score")
-def get_score(
- request: Request,
- logged_in: auth.LoggedIn,
- tema_id: int,
-):
- return tema.score(
- request=request,
- logged_in=logged_in,
- tema_id=tema_id,
- )
-
-
-@router.get("/api/tema/{tema_id}/property/{property_id}")
-def property_(request: Request, logged_in: auth.LoggedIn, tema_id: int, property_id: int):
- return tema.property_(request=request, logged_in=logged_in, tema_id=tema_id, property_id=property_id)
-
-
-@router.put("/api/tema/{tema_id}/property/{property_id}")
-def set_property(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
- property_id: int,
- field: Annotated[model.PropertyField, Form()],
- value: Annotated[str, Form()],
-):
- new_property = model.Property(id=property_id, field=field, value=value.strip())
- _ = temes_w.update_property(tema_id=tema_id, property_id=property_id, prop=new_property)
- return tema.property_(request=request, logged_in=logged_in, tema_id=tema_id, property_id=property_id)
-
-
-@router.post("/api/tema/{tema_id}/property")
-def add_property(
- request: Request,
- logged_in: auth.RequireLogin,
- tema_id: int,
-):
- new_tema = temes_w.add_property(tema_id=tema_id)
- property_id = new_tema.properties[-1].id
- if property_id is None:
- raise RuntimeError("Invalid property_id on newly created property!")
- return tema.property_editor(
- request=request,
- logged_in=logged_in,
- tema_id=tema_id,
- property_id=property_id,
- )
-
-
-@router.delete("/api/tema/{tema_id}/property/{property_id}")
-def delete_property(_: auth.RequireLogin, tema_id: int, property_id: int):
- _tema = temes_w.delete_property(tema_id=tema_id, property_id=property_id)
- return HTMLResponse()
-
-
@router.put("/api/tema/{tema_id}/visible")
def set_visible(request: Request, logged_in: auth.RequireLogin, tema_id: int):
new_tema = temes_w.set_visibility(tema_id=tema_id, hidden=False)
diff --git a/folkugat_web/api/tema/links.py b/folkugat_web/api/tema/links.py
new file mode 100644
index 0000000..34b1262
--- /dev/null
+++ b/folkugat_web/api/tema/links.py
@@ -0,0 +1,160 @@
+from typing import Annotated
+
+from fastapi import HTTPException, Request, UploadFile
+from fastapi.params import File, Form, Param
+from fastapi.responses import HTMLResponse
+from folkugat_web.api import router
+from folkugat_web.fragments.tema import links as links_fragments
+from folkugat_web.model import temes as model
+from folkugat_web.services import auth, files
+from folkugat_web.services.temes import links as links_service
+
+
+@router.get("/api/tema/{tema_id}/link/{link_id}")
+def link(request: Request, logged_in: auth.LoggedIn, tema_id: int, link_id: int):
+ link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id)
+ if not link:
+ raise HTTPException(status_code=404, detail="Could not find link!")
+ return links_fragments.link(request=request, logged_in=logged_in, link=link)
+
+
+@router.put("/api/tema/{tema_id}/link/{link_id}")
+async def set_link(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ link_id: int,
+ content_type: Annotated[model.ContentType, Form()],
+ title: Annotated[str | None, Form()] = None,
+ url: Annotated[str | None, Form()] = None,
+ upload_file: Annotated[UploadFile | None, File()] = None,
+):
+ if upload_file:
+ url = await files.store_file(tema_id=tema_id, upload_file=upload_file)
+
+ link_type = links_service.guess_link_type(url or '')
+ new_link = model.Link(
+ id=link_id,
+ tema_id=tema_id,
+ content_type=content_type,
+ link_type=link_type,
+ url=(url or '').strip(),
+ title=(title or '').strip(),
+ )
+ links_service.update_link(link=new_link)
+ return links_fragments.link(request=request, logged_in=logged_in, link=new_link)
+
+
+@router.post("/api/tema/{tema_id}/link")
+def create_link(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+):
+ link = links_service.create_link(tema_id=tema_id)
+ return links_fragments.link_editor(
+ request=request,
+ logged_in=logged_in,
+ link=link,
+ )
+
+
+@router.delete("/api/tema/{tema_id}/link/{link_id}")
+def delete_link(
+ _logged_in: auth.RequireLogin,
+ tema_id: int,
+ link_id: int,
+):
+ links_service.delete_link(link_id=link_id, tema_id=tema_id)
+ return HTMLResponse(
+ headers={
+ "HX-Trigger": f"reload-tema-{tema_id}-score"
+ }
+ )
+
+
+@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()],
+):
+ link = model.Link(
+ id=link_id,
+ tema_id=tema_id,
+ content_type=content_type,
+ link_type=links_service.guess_link_type(url),
+ url=url,
+ )
+ return links_fragments.link_icon(
+ request=request,
+ logged_in=logged_in,
+ link=link,
+ )
+
+
+@router.get("/api/tema/{tema_id}/score")
+def get_score(
+ request: Request,
+ logged_in: auth.LoggedIn,
+ tema_id: int,
+):
+ return links_fragments.score(
+ request=request,
+ logged_in=logged_in,
+ tema_id=tema_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,
+):
+ link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id)
+ if not link:
+ raise HTTPException(status_code=404, detail="Could not find link!")
+ return links_fragments.link_editor(
+ request=request,
+ logged_in=logged_in,
+ link=link,
+ )
+
+
+@router.get("/api/tema/{tema_id}/editor/link/{link_id}/url")
+def link_editor_url_input(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ link_id: int,
+):
+ link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id)
+ if not link:
+ raise HTTPException(status_code=404, detail="Could not find link!")
+ return links_fragments.link_editor_url(
+ request=request,
+ logged_in=logged_in,
+ link=link,
+ )
+
+
+@router.get("/api/tema/{tema_id}/editor/link/{link_id}/file")
+def link_editor_file_input(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ link_id: int,
+):
+ link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id)
+ if not link:
+ raise HTTPException(status_code=404, detail="Could not find link!")
+ return links_fragments.link_editor_file(
+ request=request,
+ logged_in=logged_in,
+ link=link,
+ )
diff --git a/folkugat_web/api/tema/lyrics.py b/folkugat_web/api/tema/lyrics.py
new file mode 100644
index 0000000..26ef07d
--- /dev/null
+++ b/folkugat_web/api/tema/lyrics.py
@@ -0,0 +1,80 @@
+import dataclasses
+from typing import Annotated
+
+from fastapi import HTTPException, Request
+from fastapi.params import Form
+from fastapi.responses import HTMLResponse
+from folkugat_web.api import router
+from folkugat_web.fragments.tema import lyrics as lyrics_fragments
+from folkugat_web.services import auth
+from folkugat_web.services.temes import lyrics as lyrics_service
+
+
+@router.get("/api/tema/{tema_id}/lyric/{lyric_id}")
+def lyric(
+ request: Request,
+ logged_in: auth.LoggedIn,
+ tema_id: int,
+ lyric_id: int,
+):
+ lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id)
+ if not lyric:
+ raise HTTPException(status_code=404, detail="Could not find lyric!")
+ return lyrics_fragments.lyric(request=request, logged_in=logged_in, lyric=lyric)
+
+
+@router.put("/api/tema/{tema_id}/lyric/{lyric_id}")
+def set_lyric(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ lyric_id: int,
+ title: Annotated[str, Form()],
+ content: Annotated[str, Form()],
+):
+ lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id)
+ if not lyric:
+ raise HTTPException(status_code=404, detail="Could not find lyric!")
+ new_lyric = dataclasses.replace(lyric, title=title, content=content.strip())
+ lyrics_service.update_lyric(lyric=new_lyric)
+ return lyrics_fragments.lyric(request=request, logged_in=logged_in, lyric=new_lyric)
+
+
+@router.post("/api/tema/{tema_id}/lyric")
+def add_lyric(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+):
+ lyric = lyrics_service.create_lyric(tema_id=tema_id)
+ return lyrics_fragments.lyric_editor(
+ request=request,
+ logged_in=logged_in,
+ lyric=lyric,
+ )
+
+
+@router.delete("/api/tema/{tema_id}/lyric/{lyric_id}")
+def delete_lyric(
+ tema_id: int,
+ lyric_id: int,
+):
+ lyrics_service.delete_lyric(lyric_id=lyric_id, tema_id=tema_id)
+ return HTMLResponse()
+
+
+@router.get("/api/tema/{tema_id}/editor/lyric/{lyric_id}")
+def lyric_editor(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ lyric_id: int,
+):
+ lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id)
+ if not lyric:
+ raise HTTPException(status_code=404, detail="Could not find lyric!")
+ return lyrics_fragments.lyric_editor(
+ request=request,
+ logged_in=logged_in,
+ lyric=lyric,
+ )
diff --git a/folkugat_web/api/tema/properties.py b/folkugat_web/api/tema/properties.py
new file mode 100644
index 0000000..7a45637
--- /dev/null
+++ b/folkugat_web/api/tema/properties.py
@@ -0,0 +1,73 @@
+import dataclasses
+from typing import Annotated
+
+from fastapi import HTTPException, Request
+from fastapi.params import Form
+from fastapi.responses import HTMLResponse
+from folkugat_web.api import router
+from folkugat_web.fragments.tema import properties as properties_fragments
+from folkugat_web.model import temes as model
+from folkugat_web.services import auth
+from folkugat_web.services.temes import properties as properties_service
+
+
+@router.get("/api/tema/{tema_id}/property/{property_id}")
+def property_(request: Request, logged_in: auth.LoggedIn, tema_id: int, property_id: int):
+ property = properties_service.get_property_by_id(property_id=property_id, tema_id=tema_id)
+ if not property:
+ raise HTTPException(status_code=404, detail="Could not find lyric!")
+ return properties_fragments.property_(request=request, logged_in=logged_in, property=property)
+
+
+@router.put("/api/tema/{tema_id}/property/{property_id}")
+def set_property(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ property_id: int,
+ field: Annotated[model.PropertyField, Form()],
+ value: Annotated[str, Form()],
+):
+ property = properties_service.get_property_by_id(property_id=property_id, tema_id=tema_id)
+ if not property:
+ raise HTTPException(status_code=404, detail="Could not find lyric!")
+ new_property = dataclasses.replace(property, field=field, value=value.strip())
+ properties_service.update_property(property=new_property)
+ return properties_fragments.property_(request=request, logged_in=logged_in, property=new_property)
+
+
+@router.post("/api/tema/{tema_id}/property")
+def add_property(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+):
+ property = properties_service.create_property(tema_id=tema_id)
+ return properties_fragments.property_editor(
+ request=request,
+ logged_in=logged_in,
+ property=property,
+ )
+
+
+@router.delete("/api/tema/{tema_id}/property/{property_id}")
+def delete_property(_: auth.RequireLogin, tema_id: int, property_id: int):
+ properties_service.delete_property(property_id=property_id, tema_id=tema_id)
+ return HTMLResponse()
+
+
+@router.get("/api/tema/{tema_id}/editor/property/{property_id}")
+def property_editor(
+ request: Request,
+ logged_in: auth.RequireLogin,
+ tema_id: int,
+ property_id: int,
+):
+ property = properties_service.get_property_by_id(property_id=property_id, tema_id=tema_id)
+ if not property:
+ raise HTTPException(status_code=404, detail="Could not find lyric!")
+ return properties_fragments.property_editor(
+ request=request,
+ logged_in=logged_in,
+ property=property,
+ )
diff --git a/folkugat_web/assets/templates/fragments/pdf_viewer.html b/folkugat_web/assets/templates/fragments/pdf_viewer.html
index d1089c0..c6d29fa 100644
--- a/folkugat_web/assets/templates/fragments/pdf_viewer.html
+++ b/folkugat_web/assets/templates/fragments/pdf_viewer.html
@@ -37,4 +37,7 @@
? / ?
+
diff --git a/folkugat_web/assets/templates/fragments/tema/editor/link.html b/folkugat_web/assets/templates/fragments/tema/editor/link.html
index daa00ad..59aa0b3 100644
--- a/folkugat_web/assets/templates/fragments/tema/editor/link.html
+++ b/folkugat_web/assets/templates/fragments/tema/editor/link.html
@@ -36,14 +36,14 @@