import dataclasses import zlib from typing import Self from folkugat_web.model import temes from folkugat_web.utils import batched def get_hash(*v: bytes) -> bytes: return zlib.adler32(b"".join(v)).to_bytes(4) @dataclasses.dataclass class LyricsParagraph: lines: list[str] def hash(self) -> bytes: return get_hash(*(get_hash(line.encode()) for line in self.lines)) @dataclasses.dataclass class LyricsLine: paragraphs: list[LyricsParagraph] def hash(self) -> bytes: return get_hash(*(paragraph.hash() for paragraph in self.paragraphs)) @dataclasses.dataclass class LyricsText: lines: list[LyricsLine] @classmethod def from_lyrics(cls, lyrics: temes.Lyrics, max_cols: int = 3) -> Self: paragraphs = [ LyricsParagraph(lines=par_str.splitlines()) for par_str in lyrics.content.split("\n\n") if par_str ] return cls(lines=[ LyricsLine(paragraphs=list(line_pars)) for line_pars in batched(paragraphs, max_cols) ]) def hash(self) -> bytes: return get_hash(*(line.hash() for line in self.lines)) @dataclasses.dataclass class HeaderData: title: str composer: str | None @classmethod def from_tema(cls, tema: temes.Tema) -> Self: return cls( title=tema.title, composer=tema.composer() or tema.origin(), ) def hash(self) -> bytes: return get_hash( self.title.encode(), (self.composer or "").encode(), ) @dataclasses.dataclass class LilypondTune: header: HeaderData score_source: str | None lyrics: LyricsText | None is_unknown: bool = False def hash(self) -> bytes: return get_hash( self.header.hash(), (self.score_source or "").encode(), self.lyrics.hash() if self.lyrics else b"", ) @dataclasses.dataclass class LilypondSet: title: str tunes: list[LilypondTune] def hash(self) -> bytes: return get_hash( self.title.encode(), *(tune.hash() for tune in self.tunes), )