Afegir control visual de les lletres per a partitures
This commit is contained in:
@@ -33,11 +33,25 @@ def set_lyric(
|
|||||||
lyric_id: int,
|
lyric_id: int,
|
||||||
title: Annotated[str, Form()],
|
title: Annotated[str, Form()],
|
||||||
content: Annotated[str, Form()],
|
content: Annotated[str, Form()],
|
||||||
|
max_columns: Annotated[str | None, Form()] = None,
|
||||||
):
|
):
|
||||||
lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id)
|
lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id)
|
||||||
if not lyric:
|
if not lyric:
|
||||||
raise HTTPException(status_code=404, detail="Could not find lyric!")
|
raise HTTPException(status_code=404, detail="Could not find lyric!")
|
||||||
new_lyric = lyrics_service.update_lyric(lyric=lyric, title=title, content=content)
|
|
||||||
|
# Parse max_columns from string to int if provided
|
||||||
|
max_columns_int = None
|
||||||
|
if max_columns is not None and max_columns.strip():
|
||||||
|
try:
|
||||||
|
max_columns_int = int(max_columns.strip())
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(status_code=400, detail="max_columns must be a valid integer")
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_lyric = lyrics_service.update_lyric(lyric=lyric, title=title, content=content, max_columns=max_columns_int)
|
||||||
|
except ValueError as e:
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
return lyrics_fragments.lyric(request=request, logged_in=logged_in, lyric=new_lyric)
|
return lyrics_fragments.lyric(request=request, logged_in=logged_in, lyric=new_lyric)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -737,6 +737,10 @@ video {
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-16 {
|
||||||
|
width: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,16 @@
|
|||||||
rounded
|
rounded
|
||||||
bg-brown px-2 "
|
bg-brown px-2 "
|
||||||
/>
|
/>
|
||||||
|
<input name="max_columns"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="10"
|
||||||
|
placeholder="3 (default)"
|
||||||
|
value="{{ lyric.max_columns or '' }}"
|
||||||
|
class="border border-beige focus:outline-none
|
||||||
|
rounded
|
||||||
|
bg-brown px-2 ml-2 w-16 "
|
||||||
|
/>
|
||||||
<button title="Desa els canvis"
|
<button title="Desa els canvis"
|
||||||
class="mx-1"
|
class="mx-1"
|
||||||
hx-put="/api/tema/{{ lyric.tema_id }}/lyric/{{ lyric.id }}"
|
hx-put="/api/tema/{{ lyric.tema_id }}/lyric/{{ lyric.id }}"
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<div id="tema-lyric-{{ lyric.id }}">
|
<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 and lyric.max_columns %}
|
||||||
|
<span class="text-xs ml-2">{{ lyric.max_columns }} columnes</span>
|
||||||
|
{% endif %}
|
||||||
{% if logged_in %}
|
{% if logged_in %}
|
||||||
<button title="Modifica la lletra"
|
<button title="Modifica la lletra"
|
||||||
class="mx-1"
|
class="mx-1"
|
||||||
@@ -20,6 +23,6 @@
|
|||||||
</h5>
|
</h5>
|
||||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
{{ lyric.content.replace('\n', '<br>') | safe }}
|
{{ (lyric.content.replace('~', '')).replace('\n', '<br>') | safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
\hspace #1
|
\hspace #1
|
||||||
\center-column {
|
\center-column {
|
||||||
{% for line in paragraph.lines %}
|
{% for line in paragraph.lines %}
|
||||||
\line { {{ line | safe }} }
|
{% if line %} \line { {{ line | safe }} } {% else %} \vspace #1 {% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import TypedDict
|
|||||||
from folkugat_web.dal.sql import Connection, get_connection
|
from folkugat_web.dal.sql import Connection, get_connection
|
||||||
from folkugat_web.model import temes as model
|
from folkugat_web.model import temes as model
|
||||||
|
|
||||||
LyricRowTuple = tuple[int, int, str, str]
|
LyricRowTuple = tuple[int, int, str, str, int | None]
|
||||||
|
|
||||||
|
|
||||||
class LyricRowDict(TypedDict):
|
class LyricRowDict(TypedDict):
|
||||||
@@ -12,6 +12,7 @@ class LyricRowDict(TypedDict):
|
|||||||
tema_id: int
|
tema_id: int
|
||||||
title: str
|
title: str
|
||||||
content: str
|
content: str
|
||||||
|
max_columns: int | None
|
||||||
|
|
||||||
|
|
||||||
def lyric_to_row(lyric: model.Lyrics) -> LyricRowDict:
|
def lyric_to_row(lyric: model.Lyrics) -> LyricRowDict:
|
||||||
@@ -20,6 +21,7 @@ def lyric_to_row(lyric: model.Lyrics) -> LyricRowDict:
|
|||||||
"tema_id": lyric.tema_id,
|
"tema_id": lyric.tema_id,
|
||||||
"title": lyric.title,
|
"title": lyric.title,
|
||||||
"content": lyric.content,
|
"content": lyric.content,
|
||||||
|
"max_columns": lyric.max_columns,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ def row_to_lyric(row: LyricRowTuple) -> model.Lyrics:
|
|||||||
tema_id=row[1],
|
tema_id=row[1],
|
||||||
title=row[2],
|
title=row[2],
|
||||||
content=row[3],
|
content=row[3],
|
||||||
|
max_columns=row[4],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ def get_lyrics(lyric_id: int | None = None, tema_id: int | None = None, con: Con
|
|||||||
|
|
||||||
query = f"""
|
query = f"""
|
||||||
SELECT
|
SELECT
|
||||||
id, tema_id, title, content
|
id, tema_id, title, content, max_columns
|
||||||
FROM tema_lyrics
|
FROM tema_lyrics
|
||||||
{filter_clause}
|
{filter_clause}
|
||||||
"""
|
"""
|
||||||
@@ -77,9 +80,9 @@ def insert_lyric(lyric: model.Lyrics, con: Connection | None = None) -> model.Ly
|
|||||||
data = lyric_to_row(lyric)
|
data = lyric_to_row(lyric)
|
||||||
query = f"""
|
query = f"""
|
||||||
INSERT INTO tema_lyrics
|
INSERT INTO tema_lyrics
|
||||||
(id, tema_id, title, content)
|
(id, tema_id, title, content, max_columns)
|
||||||
VALUES
|
VALUES
|
||||||
(:id, :tema_id, :title, :content)
|
(:id, :tema_id, :title, :content, :max_columns)
|
||||||
RETURNING *
|
RETURNING *
|
||||||
"""
|
"""
|
||||||
with get_connection(con) as con:
|
with get_connection(con) as con:
|
||||||
@@ -95,6 +98,7 @@ def create_lyric(tema_id: int, title: str | None = None, con: Connection | None
|
|||||||
tema_id=tema_id,
|
tema_id=tema_id,
|
||||||
title=title or "",
|
title=title or "",
|
||||||
content="",
|
content="",
|
||||||
|
max_columns=None,
|
||||||
)
|
)
|
||||||
return insert_lyric(new_lyric, con=con)
|
return insert_lyric(new_lyric, con=con)
|
||||||
|
|
||||||
@@ -104,7 +108,7 @@ def update_lyric(lyric: model.Lyrics, con: Connection | None = None):
|
|||||||
query = """
|
query = """
|
||||||
UPDATE tema_lyrics
|
UPDATE tema_lyrics
|
||||||
SET
|
SET
|
||||||
tema_id = :tema_id, title = :title, content = :content
|
tema_id = :tema_id, title = :title, content = :content, max_columns = :max_columns
|
||||||
WHERE
|
WHERE
|
||||||
id = :id
|
id = :id
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -32,13 +32,16 @@ class LyricsText:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_lyrics(cls, lyrics: temes.Lyrics, max_cols: int = 3) -> Self:
|
def from_lyrics(cls, lyrics: temes.Lyrics, max_cols: int = 3) -> Self:
|
||||||
|
# Use stored max_columns if available, otherwise use the provided max_cols
|
||||||
|
effective_max_cols = lyrics.max_columns if lyrics.max_columns is not None else max_cols
|
||||||
|
# Remove ~ characters before processing
|
||||||
paragraphs = [
|
paragraphs = [
|
||||||
LyricsParagraph(lines=par_str.splitlines())
|
LyricsParagraph(lines=[line.replace("~", "") for line in par_str.splitlines()])
|
||||||
for par_str in lyrics.content.split("\n\n") if par_str
|
for par_str in lyrics.content.split("\n\n") if par_str
|
||||||
]
|
]
|
||||||
return cls(lines=[
|
return cls(lines=[
|
||||||
LyricsLine(paragraphs=list(line_pars))
|
LyricsLine(paragraphs=list(line_pars))
|
||||||
for line_pars in batched(paragraphs, max_cols)
|
for line_pars in batched(paragraphs, effective_max_cols)
|
||||||
])
|
])
|
||||||
|
|
||||||
def hash(self) -> bytes:
|
def hash(self) -> bytes:
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class Lyrics:
|
|||||||
tema_id: int
|
tema_id: int
|
||||||
title: str
|
title: str
|
||||||
content: str
|
content: str
|
||||||
|
max_columns: int | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ def _sub(pattern: str, sub: str) -> Callable[[str], str]:
|
|||||||
def _clean_string(lyrics_str: str) -> str:
|
def _clean_string(lyrics_str: str) -> str:
|
||||||
return (
|
return (
|
||||||
FnChain.transform(lyrics_str) |
|
FnChain.transform(lyrics_str) |
|
||||||
|
# _sub(r"~", "") |
|
||||||
_sub(r"\t", " ") |
|
_sub(r"\t", " ") |
|
||||||
_sub(r" +", " ") |
|
_sub(r" +", " ") |
|
||||||
_sub(r" \n", "\n") |
|
_sub(r" \n", "\n") |
|
||||||
@@ -39,8 +40,13 @@ def get_lyric_by_id(lyric_id: int, tema_id: int | None = None) -> model.Lyrics |
|
|||||||
return next(iter(lyrics_dal.get_lyrics(lyric_id=lyric_id, tema_id=tema_id)), None)
|
return next(iter(lyrics_dal.get_lyrics(lyric_id=lyric_id, tema_id=tema_id)), None)
|
||||||
|
|
||||||
|
|
||||||
def update_lyric(lyric: model.Lyrics, title: str, content: str):
|
def update_lyric(lyric: model.Lyrics, title: str, content: str, max_columns: int | None = None):
|
||||||
lyric = dataclasses.replace(lyric, title=title.strip(), content=_clean_string(content))
|
# Validate max_columns if provided
|
||||||
|
if max_columns is not None:
|
||||||
|
if not isinstance(max_columns, int) or max_columns < 1 or max_columns > 10:
|
||||||
|
raise ValueError("max_columns must be an integer between 1 and 10")
|
||||||
|
|
||||||
|
lyric = dataclasses.replace(lyric, title=title.strip(), content=_clean_string(content), max_columns=max_columns)
|
||||||
lyrics_dal.update_lyric(lyric=lyric)
|
lyrics_dal.update_lyric(lyric=lyric)
|
||||||
return lyric
|
return lyric
|
||||||
|
|
||||||
|
|||||||
8
scripts/08_add_lyrics_max_columns.py
Normal file
8
scripts/08_add_lyrics_max_columns.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from folkugat_web.dal.sql import get_connection
|
||||||
|
|
||||||
|
with get_connection() as con:
|
||||||
|
cur = con.cursor()
|
||||||
|
alter_query = """ ALTER TABLE tema_lyrics ADD COLUMN max_columns INTEGER"""
|
||||||
|
_ = cur.execute(alter_query)
|
||||||
|
|
||||||
|
print("DONE!")
|
||||||
Reference in New Issue
Block a user