Compare commits
No commits in common. "d471082e3f545e3d3f0624ed7ff2154036d07133" and "c04114b07a94460b7c0d6fe5670bb3bc97ab56e9" have entirely different histories.
d471082e3f
...
c04114b07a
@ -65,6 +65,7 @@ class Config(object):
|
||||
MAX_MISSING_FILES_TO_REPORT = 10
|
||||
MILLISECOND_SIGFIGS = 0
|
||||
MYSQL_CONNECT = os.environ.get('MYSQL_CONNECT') or "mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_v2" # noqa E501
|
||||
NORMALISE_ON_IMPORT = True
|
||||
NOTE_TIME_FORMAT = "%H:%M:%S"
|
||||
ROOT = os.environ.get('ROOT') or "/home/kae/music"
|
||||
IMPORT_DESTINATION = os.path.join(ROOT, "Singles")
|
||||
|
||||
@ -4,7 +4,7 @@ import os.path
|
||||
import re
|
||||
import stackprinter # type: ignore
|
||||
|
||||
from dbconfig import Session, scoped_session
|
||||
from dbconfig import Session
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
@ -61,8 +61,7 @@ class Carts(Base):
|
||||
f"name={self.name}, path={self.path}>"
|
||||
)
|
||||
|
||||
def __init__(self, session: scoped_session, cart_number: int,
|
||||
name: Optional[str] = None,
|
||||
def __init__(self, session: Session, cart_number: int, name: str = None,
|
||||
duration: int = None, path: str = None,
|
||||
enabled: bool = True) -> None:
|
||||
"""Create new cart"""
|
||||
@ -95,7 +94,7 @@ class NoteColours(Base):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_colour(session: scoped_session, text: str) -> Optional[str]:
|
||||
def get_colour(session: Session, text: str) -> Optional[str]:
|
||||
"""
|
||||
Parse text and return colour string if matched, else None
|
||||
"""
|
||||
@ -140,7 +139,7 @@ class Playdates(Base):
|
||||
f"lastplayed={self.lastplayed}>"
|
||||
)
|
||||
|
||||
def __init__(self, session: scoped_session, track_id: int) -> None:
|
||||
def __init__(self, session: Session, track_id: int) -> None:
|
||||
"""Record that track was played"""
|
||||
|
||||
self.lastplayed = datetime.now()
|
||||
@ -149,7 +148,7 @@ class Playdates(Base):
|
||||
session.commit()
|
||||
|
||||
@staticmethod
|
||||
def last_played(session: scoped_session, track_id: int) -> Optional[datetime]:
|
||||
def last_played(session: Session, track_id: int) -> Optional[datetime]:
|
||||
"""Return datetime track last played or None"""
|
||||
|
||||
last_played = session.execute(
|
||||
@ -165,7 +164,7 @@ class Playdates(Base):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def played_after(session: scoped_session, since: datetime) -> List["Playdates"]:
|
||||
def played_after(session: Session, since: datetime) -> List["Playdates"]:
|
||||
"""Return a list of Playdates objects since passed time"""
|
||||
|
||||
return (
|
||||
@ -207,12 +206,12 @@ class Playlists(Base):
|
||||
f"is_templatee={self.is_template}>"
|
||||
)
|
||||
|
||||
def __init__(self, session: scoped_session, name: str) -> None:
|
||||
def __init__(self, session: Session, name: str) -> None:
|
||||
self.name = name
|
||||
session.add(self)
|
||||
session.commit()
|
||||
|
||||
def close(self, session: scoped_session) -> None:
|
||||
def close(self, session: Session) -> None:
|
||||
"""Mark playlist as unloaded"""
|
||||
|
||||
closed_idx = self.tab
|
||||
@ -228,7 +227,7 @@ class Playlists(Base):
|
||||
|
||||
@classmethod
|
||||
def create_playlist_from_template(cls,
|
||||
session: scoped_session,
|
||||
session: Session,
|
||||
template: "Playlists",
|
||||
playlist_name: str) \
|
||||
-> "Playlists":
|
||||
@ -240,7 +239,7 @@ class Playlists(Base):
|
||||
return playlist
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, session: scoped_session) -> List["Playlists"]:
|
||||
def get_all(cls, session: Session) -> List["Playlists"]:
|
||||
"""Returns a list of all playlists ordered by last use"""
|
||||
|
||||
return (
|
||||
@ -254,7 +253,7 @@ class Playlists(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_all_templates(cls, session: scoped_session) -> List["Playlists"]:
|
||||
def get_all_templates(cls, session: Session) -> List["Playlists"]:
|
||||
"""Returns a list of all templates ordered by name"""
|
||||
|
||||
return (
|
||||
@ -268,7 +267,7 @@ class Playlists(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_closed(cls, session: scoped_session) -> List["Playlists"]:
|
||||
def get_closed(cls, session: Session) -> List["Playlists"]:
|
||||
"""Returns a list of all closed playlists ordered by last use"""
|
||||
|
||||
return (
|
||||
@ -285,7 +284,7 @@ class Playlists(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_open(cls, session: scoped_session) -> List[Optional["Playlists"]]:
|
||||
def get_open(cls, session: Session) -> List[Optional["Playlists"]]:
|
||||
"""
|
||||
Return a list of loaded playlists ordered by tab order.
|
||||
"""
|
||||
@ -300,14 +299,14 @@ class Playlists(Base):
|
||||
.all()
|
||||
)
|
||||
|
||||
def mark_open(self, session: scoped_session, tab_index: int) -> None:
|
||||
def mark_open(self, session: Session, tab_index: int) -> None:
|
||||
"""Mark playlist as loaded and used now"""
|
||||
|
||||
self.tab = tab_index
|
||||
self.last_used = datetime.now()
|
||||
|
||||
@staticmethod
|
||||
def move_tab(session: scoped_session, frm: int, to: int) -> None:
|
||||
def move_tab(session: Session, frm: int, to: int) -> None:
|
||||
"""Move tabs"""
|
||||
|
||||
row_frm = session.execute(
|
||||
@ -327,7 +326,7 @@ class Playlists(Base):
|
||||
row_frm.tab = to
|
||||
|
||||
@staticmethod
|
||||
def save_as_template(session: scoped_session,
|
||||
def save_as_template(session: Session,
|
||||
playlist_id: int, template_name: str) -> None:
|
||||
"""Save passed playlist as new template"""
|
||||
|
||||
@ -358,7 +357,7 @@ class PlaylistRows(Base):
|
||||
)
|
||||
|
||||
def __init__(self,
|
||||
session: scoped_session,
|
||||
session: Session,
|
||||
playlist_id: int,
|
||||
track_id: int,
|
||||
row_number: int,
|
||||
@ -373,17 +372,8 @@ class PlaylistRows(Base):
|
||||
session.add(self)
|
||||
session.flush()
|
||||
|
||||
def append_note(self, extra_note: str) -> None:
|
||||
"""Append passed note to any existing note"""
|
||||
|
||||
current_note = self.note
|
||||
if current_note:
|
||||
self.note = current_note + '\n' + extra_note
|
||||
else:
|
||||
self.note = extra_note
|
||||
|
||||
@staticmethod
|
||||
def copy_playlist(session: scoped_session,
|
||||
def copy_playlist(session: Session,
|
||||
src_id: int,
|
||||
dst_id: int) -> None:
|
||||
"""Copy playlist entries"""
|
||||
@ -398,7 +388,7 @@ class PlaylistRows(Base):
|
||||
plr.note)
|
||||
|
||||
@staticmethod
|
||||
def delete_plrids_not_in_list(session: scoped_session, playlist_id: int,
|
||||
def delete_plrids_not_in_list(session: Session, playlist_id: int,
|
||||
plrids: List["PlaylistRows"]) -> None:
|
||||
"""
|
||||
Delete rows in given playlist that have a higher row number
|
||||
@ -416,7 +406,7 @@ class PlaylistRows(Base):
|
||||
session.commit()
|
||||
|
||||
@staticmethod
|
||||
def fixup_rownumbers(session: scoped_session, playlist_id: int) -> None:
|
||||
def fixup_rownumbers(session: Session, playlist_id: int) -> None:
|
||||
"""
|
||||
Ensure the row numbers for passed playlist have no gaps
|
||||
"""
|
||||
@ -434,7 +424,7 @@ class PlaylistRows(Base):
|
||||
session.commit()
|
||||
|
||||
@staticmethod
|
||||
def get_track_plr(session: scoped_session, track_id: int,
|
||||
def get_track_plr(session: Session, track_id: int,
|
||||
playlist_id: int) -> Optional["PlaylistRows"]:
|
||||
"""Return first matching PlaylistRows object or None"""
|
||||
|
||||
@ -448,7 +438,7 @@ class PlaylistRows(Base):
|
||||
).first()
|
||||
|
||||
@staticmethod
|
||||
def get_last_used_row(session: scoped_session, playlist_id: int) -> Optional[int]:
|
||||
def get_last_used_row(session: Session, playlist_id: int) -> Optional[int]:
|
||||
"""Return the last used row for playlist, or None if no rows"""
|
||||
|
||||
return session.execute(
|
||||
@ -457,7 +447,7 @@ class PlaylistRows(Base):
|
||||
).scalar_one()
|
||||
|
||||
@classmethod
|
||||
def get_played_rows(cls, session: scoped_session,
|
||||
def get_played_rows(cls, session: Session,
|
||||
playlist_id: int) -> List[int]:
|
||||
"""
|
||||
For passed playlist, return a list of rows that
|
||||
@ -476,7 +466,7 @@ class PlaylistRows(Base):
|
||||
return plrs
|
||||
|
||||
@classmethod
|
||||
def get_rows_with_tracks(cls, session: scoped_session,
|
||||
def get_rows_with_tracks(cls, session: Session,
|
||||
playlist_id: int) -> List[int]:
|
||||
"""
|
||||
For passed playlist, return a list of rows that
|
||||
@ -495,8 +485,8 @@ class PlaylistRows(Base):
|
||||
return plrs
|
||||
|
||||
@classmethod
|
||||
def get_unplayed_rows(cls, session: scoped_session,
|
||||
playlist_id: int) -> List["PlaylistRows"]:
|
||||
def get_unplayed_rows(cls, session: Session,
|
||||
playlist_id: int) -> List["PlaylistRows]:
|
||||
"""
|
||||
For passed playlist, return a list of playlist rows that
|
||||
have not been played.
|
||||
@ -515,7 +505,7 @@ class PlaylistRows(Base):
|
||||
return plrs
|
||||
|
||||
@staticmethod
|
||||
def move_rows_down(session: scoped_session, playlist_id: int, starting_row: int,
|
||||
def move_rows_down(session: Session, playlist_id: int, starting_row: int,
|
||||
move_by: int) -> None:
|
||||
"""
|
||||
Create space to insert move_by additional rows by incremented row
|
||||
@ -532,7 +522,7 @@ class PlaylistRows(Base):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def indexed_by_id(session: scoped_session, plr_ids: List[int]) -> dict:
|
||||
def indexed_by_id(session: Session, plr_ids: List[int]) -> dict:
|
||||
"""
|
||||
Return a dictionary of playlist_rows indexed by their plr id from
|
||||
the passed plr_id list.
|
||||
@ -568,7 +558,7 @@ class Settings(Base):
|
||||
return f"<Settings(id={self.id}, name={self.name}, {value=}>"
|
||||
|
||||
@classmethod
|
||||
def get_int_settings(cls, session: scoped_session, name: str) -> "Settings":
|
||||
def get_int_settings(cls, session: Session, name: str) -> "Settings":
|
||||
"""Get setting for an integer or return new setting record"""
|
||||
|
||||
int_setting: Settings
|
||||
@ -587,7 +577,7 @@ class Settings(Base):
|
||||
|
||||
return int_setting
|
||||
|
||||
def update(self, session: scoped_session, data: "Settings"):
|
||||
def update(self, session: Session, data: "Settings"):
|
||||
for key, value in data.items():
|
||||
assert hasattr(self, key)
|
||||
setattr(self, key, value)
|
||||
@ -619,7 +609,7 @@ class Tracks(Base):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
session: scoped_session,
|
||||
session: Session,
|
||||
path: str,
|
||||
title: Optional[str] = None,
|
||||
artist: Optional[str] = None,
|
||||
@ -650,7 +640,7 @@ 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: Session, path: str) -> "Tracks":
|
||||
"""
|
||||
Return track with passed path, or None.
|
||||
"""
|
||||
@ -666,7 +656,7 @@ class Tracks(Base):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def search_artists(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
||||
def search_artists(cls, session: Session, text: str) -> List["Tracks"]:
|
||||
"""Search case-insenstively for artists containing str"""
|
||||
|
||||
return (
|
||||
@ -680,7 +670,7 @@ class Tracks(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def search_titles(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
||||
def search_titles(cls, session: Session, text: str) -> List["Tracks"]:
|
||||
"""Search case-insenstively for titles containing str"""
|
||||
return (
|
||||
session.execute(
|
||||
|
||||
@ -554,8 +554,7 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
return [self._get_playlistrow_id(a) for a in self._get_selected_rows()]
|
||||
|
||||
def get_selected_playlistrows(self,
|
||||
session: scoped_session) -> Optional[List]:
|
||||
def get_selected_playlistrows(self, session: scoped_session) -> Optional[List]:
|
||||
"""
|
||||
Return a list of PlaylistRows of the selected rows
|
||||
"""
|
||||
@ -696,11 +695,7 @@ class PlaylistTab(QTableWidget):
|
||||
if existing_plr and ask_yes_no("Duplicate row",
|
||||
"Track already in playlist. "
|
||||
"Move to new location?"):
|
||||
# Yes it is and we should reuse it
|
||||
# If we've been passed a note, we need to add that to the
|
||||
# existing track
|
||||
if note:
|
||||
existing_plr.append_note(note)
|
||||
# Yes it is and we shoudl reuse it
|
||||
return self._move_row(session, existing_plr, row_number)
|
||||
|
||||
# Build playlist_row object
|
||||
@ -1360,8 +1355,7 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
return playlistrow_id
|
||||
|
||||
def _get_playlistrow_object(self, session: scoped_session,
|
||||
row: int) -> int:
|
||||
def _get_playlistrow_object(self, session: scoped_session, row: int) -> int:
|
||||
"""Return the playlistrow object associated with this row"""
|
||||
|
||||
playlistrow_id = (self.item(row, USERDATA).data(self.PLAYLISTROW_ID))
|
||||
@ -1490,6 +1484,48 @@ class PlaylistTab(QTableWidget):
|
||||
and pos.y() >= rect.center().y() # noqa W503
|
||||
)
|
||||
|
||||
def _meta_clear_attribute(self, row: int, attribute: int) -> None:
|
||||
"""Clear given metadata for row"""
|
||||
|
||||
if row is None:
|
||||
raise ValueError(f"_meta_clear_attribute({row=}, {attribute=})")
|
||||
|
||||
new_metadata: int = self._meta_get(row) & ~(1 << attribute)
|
||||
self.item(row, USERDATA).setData(self.ROW_FLAGS, new_metadata)
|
||||
|
||||
def _meta_get(self, row: int) -> int:
|
||||
"""Return row metadata"""
|
||||
|
||||
return (self.item(row, USERDATA).data(self.ROW_FLAGS))
|
||||
|
||||
def _meta_search(self, metadata: int, one: bool = True) -> List[int]:
|
||||
"""
|
||||
Search rows for metadata.
|
||||
|
||||
If one is True, check that only one row matches and return
|
||||
the row number.
|
||||
|
||||
If one is False, return a list of matching row numbers.
|
||||
"""
|
||||
|
||||
matches = []
|
||||
for row in range(self.rowCount()):
|
||||
if self._meta_get(row):
|
||||
if self._meta_get(row) & (1 << metadata):
|
||||
matches.append(row)
|
||||
|
||||
if not one:
|
||||
return matches
|
||||
|
||||
if len(matches) <= 1:
|
||||
return matches
|
||||
else:
|
||||
log.error(
|
||||
f"Multiple matches for metadata '{metadata}' found "
|
||||
f"in rows: {', '.join([str(x) for x in matches])}"
|
||||
)
|
||||
raise AttributeError(f"Multiple '{metadata}' metadata {matches}")
|
||||
|
||||
def _move_row(self, session: scoped_session, plr: PlaylistRows,
|
||||
new_row_number: int) -> None:
|
||||
"""Move playlist row to new_row_number using parent copy/paste"""
|
||||
|
||||
30
poetry.lock
generated
30
poetry.lock
generated
@ -306,7 +306,7 @@ python-versions = ">=3.7"
|
||||
name = "mypy"
|
||||
version = "0.991"
|
||||
description = "Optional static typing for Python"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@ -325,7 +325,7 @@ reports = ["lxml"]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@ -671,11 +671,23 @@ postgresql_psycopg2cffi = ["psycopg2cffi"]
|
||||
pymysql = ["pymysql (<1)", "pymysql"]
|
||||
sqlcipher = ["sqlcipher3-binary"]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy-stubs"
|
||||
version = "0.4"
|
||||
description = "SQLAlchemy stubs and mypy plugin"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
mypy = ">=0.790"
|
||||
typing-extensions = ">=3.7.4"
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy2-stubs"
|
||||
version = "0.0.2a32"
|
||||
version = "0.0.2a31"
|
||||
description = "Typing Stubs for SQLAlchemy 1.4"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@ -748,7 +760,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@ -776,7 +788,7 @@ python-versions = "*"
|
||||
name = "typing-extensions"
|
||||
version = "4.4.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@ -804,7 +816,7 @@ python-versions = "*"
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "389d73715056202a39f0efe23941943d7a6849915607cb8ceb4c77bde7e4f709"
|
||||
content-hash = "9ae8194c057d5eb51e7f65f5328f94ea85e0a48a5fe12d38ad72f4a1aae7cbca"
|
||||
|
||||
[metadata.files]
|
||||
alembic = []
|
||||
@ -910,6 +922,10 @@ six = [
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
sqlalchemy = []
|
||||
sqlalchemy-stubs = [
|
||||
{file = "sqlalchemy-stubs-0.4.tar.gz", hash = "sha256:c665d6dd4482ef642f01027fa06c3d5e91befabb219dc71fc2a09e7d7695f7ae"},
|
||||
{file = "sqlalchemy_stubs-0.4-py3-none-any.whl", hash = "sha256:5eec7aa110adf9b957b631799a72fef396b23ff99fe296df726645d01e312aa5"},
|
||||
]
|
||||
sqlalchemy2-stubs = []
|
||||
stack-data = []
|
||||
stackprinter = []
|
||||
|
||||
@ -24,17 +24,18 @@ python-Levenshtein = "^0.12.2"
|
||||
pyfzf = "^0.3.1"
|
||||
pydymenu = "^0.5.2"
|
||||
stackprinter = "^0.2.10"
|
||||
sqlalchemy-stubs = "^0.4"
|
||||
sqlalchemy2-stubs = "^0.0.2-alpha.31"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
ipdb = "^0.13.9"
|
||||
sqlalchemy-stubs = "^0.4"
|
||||
PyQt5-stubs = "^5.15.2"
|
||||
pytest = "^7.0.1"
|
||||
pytest-qt = "^4.0.2"
|
||||
pydub-stubs = "^0.25.1"
|
||||
line-profiler = "^4.0.2"
|
||||
flakehell = "^0.9.0"
|
||||
sqlalchemy2-stubs = "^0.0.2-alpha.32"
|
||||
mypy = "^0.991"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
@ -44,6 +45,3 @@ build-backend = "poetry.core.masonry.api"
|
||||
mypy_path = "/home/kae/.cache/pypoetry/virtualenvs/musicmuster-oWgGw1IG-py3.9:/home/kae/git/musicmuster/app"
|
||||
plugins = "sqlalchemy.ext.mypy.plugin"
|
||||
|
||||
[tool.vulture]
|
||||
exclude = ["migrations"]
|
||||
paths = ["app"]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user