Allow template edits and deletions. Deletions are now true deletes, not just flagged in database as deletes, and this applies to all playlists. Includes schema changes to cascade deletes.
164 lines
5.5 KiB
Python
164 lines
5.5 KiB
Python
# Standard library imports
|
|
from typing import List, Optional
|
|
import datetime as dt
|
|
|
|
# PyQt imports
|
|
|
|
# Third party imports
|
|
from alchemical import Model # type: ignore
|
|
from sqlalchemy import (
|
|
Boolean,
|
|
DateTime,
|
|
ForeignKey,
|
|
String,
|
|
)
|
|
from sqlalchemy.ext.associationproxy import association_proxy
|
|
from sqlalchemy.orm import (
|
|
Mapped,
|
|
mapped_column,
|
|
relationship,
|
|
)
|
|
|
|
# App imports
|
|
|
|
|
|
# Database classes
|
|
class NoteColoursTable(Model):
|
|
__tablename__ = "notecolours"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
substring: Mapped[str] = mapped_column(String(256), index=False)
|
|
colour: Mapped[str] = mapped_column(String(21), index=False)
|
|
enabled: Mapped[bool] = mapped_column(default=True, index=True)
|
|
foreground: Mapped[Optional[str]] = mapped_column(String(21), index=False)
|
|
is_regex: Mapped[bool] = mapped_column(default=False, index=False)
|
|
is_casesensitive: Mapped[bool] = mapped_column(default=False, index=False)
|
|
order: Mapped[Optional[int]] = mapped_column(index=True)
|
|
strip_substring: Mapped[bool] = mapped_column(default=True, index=False)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<NoteColours(id={self.id}, substring={self.substring}, "
|
|
f"colour={self.colour}>"
|
|
)
|
|
|
|
|
|
class PlaydatesTable(Model):
|
|
__tablename__ = "playdates"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
lastplayed: Mapped[dt.datetime] = mapped_column(index=True)
|
|
track_id: Mapped[int] = mapped_column(ForeignKey("tracks.id"))
|
|
track: Mapped["TracksTable"] = relationship(
|
|
"TracksTable", back_populates="playdates"
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<Playdates(id={self.id}, track_id={self.track_id} "
|
|
f"lastplayed={self.lastplayed}>"
|
|
)
|
|
|
|
|
|
class PlaylistsTable(Model):
|
|
"""
|
|
Manage playlists
|
|
"""
|
|
|
|
__tablename__ = "playlists"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(32), unique=True)
|
|
last_used: Mapped[Optional[dt.datetime]] = mapped_column(DateTime, default=None)
|
|
tab: Mapped[Optional[int]] = mapped_column(default=None)
|
|
open: Mapped[bool] = mapped_column(default=False)
|
|
is_template: Mapped[bool] = mapped_column(default=False)
|
|
rows: Mapped[List["PlaylistRowsTable"]] = relationship(
|
|
"PlaylistRowsTable",
|
|
back_populates="playlist",
|
|
cascade="all, delete-orphan",
|
|
order_by="PlaylistRowsTable.row_number",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<Playlists(id={self.id}, name={self.name}, "
|
|
f"is_templatee={self.is_template}, open={self.open}>"
|
|
)
|
|
|
|
|
|
class PlaylistRowsTable(Model):
|
|
__tablename__ = "playlist_rows"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
row_number: Mapped[int] = mapped_column(index=True)
|
|
note: Mapped[str] = mapped_column(
|
|
String(2048), index=False, default="", nullable=False
|
|
)
|
|
playlist_id: Mapped[int] = mapped_column(ForeignKey("playlists.id"), index=True)
|
|
playlist: Mapped[PlaylistsTable] = relationship(back_populates="rows")
|
|
track_id: Mapped[Optional[int]] = mapped_column(ForeignKey("tracks.id"))
|
|
track: Mapped["TracksTable"] = relationship(
|
|
"TracksTable",
|
|
back_populates="playlistrows",
|
|
)
|
|
played: Mapped[bool] = mapped_column(
|
|
Boolean, nullable=False, index=False, default=False
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<PlaylistRows(id={self.id}, playlist_id={self.playlist_id}, "
|
|
f"track_id={self.track_id}, "
|
|
f"note={self.note}, row_number={self.row_number}>"
|
|
)
|
|
|
|
|
|
class SettingsTable(Model):
|
|
"""Manage settings"""
|
|
|
|
__tablename__ = "settings"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(64), unique=True)
|
|
f_datetime: Mapped[Optional[dt.datetime]] = mapped_column(default=None)
|
|
f_int: Mapped[Optional[int]] = mapped_column(default=None)
|
|
f_string: Mapped[Optional[str]] = mapped_column(String(128), default=None)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<Settings(id={self.id}, name={self.name}, "
|
|
f"f_datetime={self.f_datetime}, f_int={self.f_int}, f_string={self.f_string}>"
|
|
)
|
|
|
|
|
|
class TracksTable(Model):
|
|
__tablename__ = "tracks"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
artist: Mapped[str] = mapped_column(String(256), index=True)
|
|
bitrate: Mapped[Optional[int]] = mapped_column(default=None)
|
|
duration: Mapped[int] = mapped_column(index=True)
|
|
fade_at: Mapped[int] = mapped_column(index=False)
|
|
intro: Mapped[Optional[int]] = mapped_column(default=None)
|
|
path: Mapped[str] = mapped_column(String(2048), index=False, unique=True)
|
|
silence_at: Mapped[int] = mapped_column(index=False)
|
|
start_gap: Mapped[int] = mapped_column(index=False)
|
|
title: Mapped[str] = mapped_column(String(256), index=True)
|
|
|
|
playlistrows: Mapped[List[PlaylistRowsTable]] = relationship(
|
|
"PlaylistRowsTable", back_populates="track"
|
|
)
|
|
playlists = association_proxy("playlistrows", "playlist")
|
|
playdates: Mapped[List[PlaydatesTable]] = relationship(
|
|
"PlaydatesTable",
|
|
back_populates="track",
|
|
lazy="joined",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<Track(id={self.id}, title={self.title}, "
|
|
f"artist={self.artist}, path={self.path}>"
|
|
)
|