From 73bb4b3a7fce842f792cd88a567234a54f617d88 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Mon, 30 Jan 2023 19:29:33 +0000 Subject: [PATCH] WIP: typing --- app/models.py | 63 +++++++++++++++++-------------- app/musicmuster.py | 60 ++++++++++++++++------------- app/playlists.py | 4 +- play.py | 94 ---------------------------------------------- 4 files changed, 70 insertions(+), 151 deletions(-) delete mode 100755 play.py diff --git a/app/models.py b/app/models.py index 23d9498..fdff803 100644 --- a/app/models.py +++ b/app/models.py @@ -63,7 +63,7 @@ class Carts(Base): def __init__(self, session: scoped_session, cart_number: int, name: Optional[str] = None, - duration: int = None, path: str = None, + duration: Optional[int] = None, path: Optional[str] = None, enabled: bool = True) -> None: """Create new cart""" @@ -132,7 +132,7 @@ class Playdates(Base): id: int = Column(Integer, primary_key=True, autoincrement=True) lastplayed = Column(DateTime, index=True, default=None) track_id = Column(Integer, ForeignKey('tracks.id')) - track = relationship("Tracks", back_populates="playdates") + track: "Tracks" = relationship("Tracks", back_populates="playdates") def __repr__(self) -> str: return ( @@ -149,7 +149,8 @@ class Playdates(Base): session.commit() @staticmethod - def last_played(session: scoped_session, track_id: int) -> Optional[datetime]: + def last_played(session: scoped_session, + track_id: int) -> Optional[datetime]: """Return datetime track last played or None""" last_played = session.execute( @@ -165,7 +166,8 @@ class Playdates(Base): return None @staticmethod - def played_after(session: scoped_session, since: datetime) -> List["Playdates"]: + def played_after(session: scoped_session, + since: datetime) -> List["Playdates"]: """Return a list of Playdates objects since passed time""" return ( @@ -186,7 +188,7 @@ class Playlists(Base): __tablename__ = "playlists" - id = Column(Integer, primary_key=True, autoincrement=True) + id = Column(Integer, primary_key=True, autoincrement=True, nullable=False) name = Column(String(32), nullable=False, unique=True) last_used = Column(DateTime, default=None, nullable=True) tab = Column(Integer, default=None, nullable=True, unique=True) @@ -194,7 +196,7 @@ class Playlists(Base): is_template = Column(Boolean, default=False, nullable=False) query = Column(String(256), default=None, nullable=True, unique=False) deleted = Column(Boolean, default=False, nullable=False) - rows = relationship( + rows: "PlaylistRows" = relationship( "PlaylistRows", back_populates="playlist", cascade="all, delete-orphan", @@ -207,7 +209,7 @@ class Playlists(Base): f"is_templatee={self.is_template}>" ) - def __init__(self, session: scoped_session, name: str) -> None: + def __init__(self, session: scoped_session, name: str): self.name = name session.add(self) session.commit() @@ -345,9 +347,9 @@ class PlaylistRows(Base): row_number = Column(Integer, nullable=False) note = Column(String(2048), index=False) playlist_id = Column(Integer, ForeignKey('playlists.id'), nullable=False) - playlist = relationship(Playlists, back_populates="rows") + playlist: Playlists = relationship(Playlists, back_populates="rows") track_id = Column(Integer, ForeignKey('tracks.id'), nullable=True) - track = relationship("Tracks", back_populates="playlistrows") + track: "Tracks" = relationship("Tracks", back_populates="playlistrows") played = Column(Boolean, nullable=False, index=False, default=False) def __repr__(self) -> str: @@ -448,7 +450,8 @@ class PlaylistRows(Base): ).first() @staticmethod - def get_last_used_row(session: scoped_session, playlist_id: int) -> Optional[int]: + def get_last_used_row(session: scoped_session, + playlist_id: int) -> Optional[int]: """Return the last used row for playlist, or None if no rows""" return session.execute( @@ -515,8 +518,8 @@ class PlaylistRows(Base): return plrs @staticmethod - def move_rows_down(session: scoped_session, playlist_id: int, starting_row: int, - move_by: int) -> None: + def move_rows_down(session: scoped_session, playlist_id: int, + starting_row: int, move_by: int) -> None: """ Create space to insert move_by additional rows by incremented row number from starting_row to end of playlist @@ -567,27 +570,27 @@ class Settings(Base): value = self.f_datetime or self.f_int or self.f_string return f"" + def __init__(self, session: scoped_session, name: str): + + self.name = name + session.add(self) + session.flush() + @classmethod - def get_int_settings(cls, session: scoped_session, name: str) -> "Settings": + def get_int_settings(cls, session: scoped_session, + name: str) -> "Settings": """Get setting for an integer or return new setting record""" - int_setting: Settings - try: - int_setting = session.execute( + return session.execute( select(cls) .where(cls.name == name) ).scalar_one() except NoResultFound: - int_setting = Settings() - int_setting.name = name - int_setting.f_int = None - session.add(int_setting) + return Settings(session, name) - return int_setting - - def update(self, session: scoped_session, data: "Settings"): + def update(self, session: scoped_session, data: dict): for key, value in data.items(): assert hasattr(self, key) setattr(self, key, value) @@ -607,9 +610,10 @@ class Tracks(Base): path = Column(String(2048), index=False, nullable=False, unique=True) mtime = Column(Float, index=True) bitrate = Column(Integer, nullable=True, default=None) - playlistrows = relationship("PlaylistRows", back_populates="track") + playlistrows: PlaylistRows = relationship("PlaylistRows", + back_populates="track") playlists = association_proxy("playlistrows", "playlist") - playdates = relationship("Playdates", back_populates="track") + playdates: Playdates = relationship("Playdates", back_populates="track") def __repr__(self) -> str: return ( @@ -650,7 +654,8 @@ class Tracks(Base): return session.execute(select(cls)).scalars().all() @classmethod - def get_by_path(cls, session: scoped_session, path: str) -> "Tracks": + def get_by_path(cls, session: scoped_session, + path: str) -> Optional["Tracks"]: """ Return track with passed path, or None. """ @@ -666,7 +671,8 @@ class Tracks(Base): return None @classmethod - def search_artists(cls, session: scoped_session, text: str) -> List["Tracks"]: + def search_artists(cls, session: scoped_session, + text: str) -> List["Tracks"]: """Search case-insenstively for artists containing str""" return ( @@ -680,7 +686,8 @@ class Tracks(Base): ) @classmethod - def search_titles(cls, session: scoped_session, text: str) -> List["Tracks"]: + def search_titles(cls, session: scoped_session, + text: str) -> List["Tracks"]: """Search case-insenstively for titles containing str""" return ( session.execute( diff --git a/app/musicmuster.py b/app/musicmuster.py index 28a4c65..e9c731f 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -27,7 +27,7 @@ from PyQt5.QtWidgets import ( QProgressBar, ) -from dbconfig import engine, Session +from dbconfig import engine, Session, scoped_session import helpers import music @@ -59,7 +59,8 @@ class CartButton(QPushButton): """Create a cart pushbutton and set it disabled""" super().__init__(parent) - self.parent = parent + # Next line is redundant (check) + # self.parent = parent self.cart_id = cart.id if cart.path and cart.enabled and not cart.duration: tags = helpers.get_tags(cart.path) @@ -126,28 +127,28 @@ class PlaylistTrack: number: that's the playlist's problem. """ - self.artist = None - self.duration = None - self.end_time = None - self.fade_at = None - self.fade_length = None - self.path = None - self.playlist_id = None - self.playlist_tab = None - self.plr_id = None - self.silence_at = None - self.start_gap = None - self.start_time = None - self.title = None - self.track_id = None + self.artist: Optional[str] = None + self.duration: Optional[int] = None + self.end_time: Optional[datetime] = None + self.fade_at: Optional[int] = None + self.fade_length: Optional[int] = None + self.path: Optional[str] = None + self.playlist_id: Optional[int] = None + self.playlist_tab: Optional[PlaylistTab] = None + self.plr_id: Optional[int] = None + self.silence_at: Optional[datetime] = None + self.start_gap: Optional[int] = None + self.start_time: Optional[datetime] = None + self.title: Optional[str] = None + self.track_id: Optional[int] = None def __repr__(self) -> str: return ( f"" + f"playlist_id={self.playlist_id}>" ) - def set_plr(self, session: Session, plr: PlaylistRows, + def set_plr(self, session: scoped_session, plr: PlaylistRows, tab: PlaylistTab) -> None: """ Update with new plr information @@ -492,7 +493,7 @@ class Window(QMainWindow, Ui_MainWindow): self.timer.timeout.connect(self.tick) def create_playlist(self, - session: Session, + session: scoped_session, playlist_name: Optional[str] = None) -> Playlists: """Create new playlist""" @@ -512,7 +513,7 @@ class Window(QMainWindow, Ui_MainWindow): if playlist: self.create_playlist_tab(session, playlist) - def create_playlist_tab(self, session: Session, + def create_playlist_tab(self, session: scoped_session, playlist: Playlists) -> int: """ Take the passed playlist database object, create a playlist tab and @@ -714,7 +715,7 @@ class Window(QMainWindow, Ui_MainWindow): QMessageBox.Ok ) - def get_one_track(self, session: Session) -> Optional[Tracks]: + def get_one_track(self, session: scoped_session) -> Optional[Tracks]: """Show dialog box to select one track and return it to caller""" dlg = DbDialog(self, session, get_one_track=True) @@ -833,10 +834,10 @@ class Window(QMainWindow, Ui_MainWindow): _ = self.create_playlist_tab(session, playlist) # Set active tab record = Settings.get_int_settings(session, "active_tab") - if record and record.f_int is not None: + if record and record.f_int >= 0: self.tabPlaylist.setCurrentIndex(record.f_int) - def move_playlist_rows(self, session: Session, + def move_playlist_rows(self, session: scoped_session, playlistrows: List[PlaylistRows]) -> None: """ Move passed playlist rows to another playlist @@ -1325,7 +1326,7 @@ class Window(QMainWindow, Ui_MainWindow): # May also be called when last tab is closed pass - def this_is_the_next_playlist_row(self, session: Session, + def this_is_the_next_playlist_row(self, session: scoped_session, plr: PlaylistRows, playlist_tab: PlaylistTab) -> None: """ @@ -1468,6 +1469,9 @@ class Window(QMainWindow, Ui_MainWindow): # Time to end self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end)) + # Autoplay next track + # if time_to_silence <= 1500: + # self.play_next() else: if self.playing: self.stop_playing() @@ -1500,7 +1504,7 @@ class Window(QMainWindow, Ui_MainWindow): class CartDialog(QDialog): """Edit cart details""" - def __init__(self, parent: QMainWindow, session: Session, + def __init__(self, parent: QMainWindow, session: scoped_session, cart: Carts) -> None: """ Manage carts @@ -1537,7 +1541,7 @@ class CartDialog(QDialog): class DbDialog(QDialog): """Select track from database""" - def __init__(self, parent: QMainWindow, session: Session, + def __init__(self, parent: QMainWindow, session: scoped_session, get_one_track: bool = False) -> None: """ Subclassed QDialog to manage track selection @@ -1775,7 +1779,9 @@ if __name__ == "__main__": app = QApplication(sys.argv) win = Window() win.show() - sys.exit(app.exec()) + status = app.exec() + engine.dispose() + sys.exit(status) except Exception as exc: from helpers import send_mail diff --git a/app/playlists.py b/app/playlists.py index 055b153..10a685e 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -1205,7 +1205,7 @@ class PlaylistTab(QTableWidget): return start + timedelta(milliseconds=duration) - def _column_resize(self, idx: int, old: int, new: int) -> None: + def _column_resize(self, idx: int, _old: int, _new: int) -> None: """ Called when column widths are changed. @@ -1722,7 +1722,7 @@ class PlaylistTab(QTableWidget): continue attr_name = f"playlist_{column_name}_col_width" record: Settings = Settings.get_int_settings(session, attr_name) - if record and record.f_int is not None: + if record and record.f_int >= 0: self.setColumnWidth(idx, record.f_int) else: self.setColumnWidth(idx, Config.DEFAULT_COLUMN_WIDTH) diff --git a/play.py b/play.py deleted file mode 100755 index 3b7f381..0000000 --- a/play.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python - -from sqlalchemy import create_engine -from sqlalchemy import text -from sqlalchemy import Table, Column, Integer, String -from sqlalchemy import ForeignKey -from sqlalchemy import select -from sqlalchemy import insert -from sqlalchemy.orm import Session -from sqlalchemy.orm import declarative_base -from sqlalchemy.orm import relationship - - -Base = declarative_base() - -engine = create_engine("sqlite+pysqlite:///:memory:", echo=True, future=True) - - -class User(Base): - __tablename__ = 'user_account' - id = Column(Integer, primary_key=True) - name = Column(String(30)) - fullname = Column(String) - addresses = relationship("Address", back_populates="user") - - def __repr__(self): - return ( - f"User(id={self.id!r}, name={self.name!r}, " - f"fullname={self.fullname!r})" - ) - - -class Address(Base): - __tablename__ = 'address' - id = Column(Integer, primary_key=True) - email_address = Column(String, nullable=False) - user_id = Column(Integer, ForeignKey('user_account.id')) - user = relationship("User", back_populates="addresses") - - def __repr__(self): - return f"Address(id={self.id!r}, email_address={self.email_address!r})" - - -Base.metadata.create_all(engine) - -squidward = User(name="squidward", fullname="Squidward Tentacles") -krabs = User(name="ehkrabs", fullname="Eugene H. Krabs") - -session = Session(engine) - -session.add(squidward) -session.add(krabs) - -session.commit() - -u1 = User(name='pkrabs', fullname='Pearl Krabs') -a1 = Address(email_address="pearl.krabs@gmail.com") -u1.addresses.append(a1) -a2 = Address(email_address="pearl@aol.com", user=u1) - -session.add(u1) -session.add(a1) -session.add(a2) - -session.commit() - - -# with engine.connect() as conn: -# conn.execute(text("CREATE TABLE some_table (x int, y int)")) -# conn.execute( -# text("INSERT INTO some_table (x, y) VALUES (:x, :y)"), -# [{"x": 1, "y": 1}, {"x": 2, "y": 4}] -# ) -# conn.commit() -# -# with engine.begin() as conn: -# conn.execute( -# text("INSERT INTO some_table (x, y) VALUES (:x, :y)"), -# [{"x": 6, "y": 8}, {"x": 9, "y": 10}] -# ) -# -# # with engine.connect() as conn: -# # result = conn.execute(text("SELECT x, y FROM some_table")) -# # for row in result: -# # print(f"x: {row.x} y: {row.y}") -# -# -# stmt = text( -# "SELECT x, y FROM some_table WHERE y > :y ORDER BY x, y").bindparams(y=6) -# -# with Session(engine) as session: -# result = session.execute(stmt) -# for row in result: -# print(f"x: {row.x} y: {row.y}")