Compare commits

...

5 Commits

Author SHA1 Message Date
Keith Edmunds
5e4277646b Black formatting 2024-06-16 08:40:45 +01:00
Keith Edmunds
71257e4d67 Ensure one db instance only
Ensure testing db is correctly set to sqlite
2024-06-16 08:40:03 +01:00
Keith Edmunds
c0b7bf76f5 Clean up tmpdir after normalise tests 2024-06-16 08:18:24 +01:00
Keith Edmunds
5624d77519 Remove pygame dependency 2024-06-16 08:17:09 +01:00
Keith Edmunds
21156d8fa1 Improve getting/setting of Settings 2024-06-16 08:16:24 +01:00
15 changed files with 121 additions and 235 deletions

8
.envrc
View File

@ -4,17 +4,17 @@ export MAIL_PORT=587
export MAIL_SERVER="smtp.fastmail.com"
export MAIL_USERNAME="kae@midnighthax.com"
export MAIL_USE_TLS=True
export PYGAME_HIDE_SUPPORT_PROMPT=1
branch=$(git branch --show-current)
# Always treat running from /home/kae/mm as production
if [ $(pwd) == /home/kae/mm ]; then
export MM_ENV="PRODUCTION"
export ALCHEMICAL_DATABASE_URI="mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_prod"
export DATABASE_URL="mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_prod"
elif on_git_branch master; then
export MM_ENV="PRODUCTION"
export ALCHEMICAL_DATABASE_URI="mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_prod"
export DATABASE_URL="mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_prod"
else
export MM_ENV="DEVELOPMENT"
export ALCHEMICAL_DATABASE_URI="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster"
export DATABASE_URL="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster"
export PYTHONBREAKPOINT="pudb.set_trace"
fi

30
app/dbmanager.py Normal file
View File

@ -0,0 +1,30 @@
# Standard library imports
# PyQt imports
# Third party imports
from alchemical import Alchemical # type:ignore
# App imports
class DatabaseManager:
"""
Singleton class to ensure we only ever have one db object
"""
__instance = None
def __init__(self, database_url, **kwargs):
if DatabaseManager.__instance is None:
self.db = Alchemical(database_url, **kwargs)
self.db.create_all()
DatabaseManager.__instance = self
else:
raise Exception("Attempted to create a second DatabaseManager instance")
@staticmethod
def get_instance(database_url, **kwargs):
if DatabaseManager.__instance is None:
DatabaseManager(database_url, **kwargs)
return DatabaseManager.__instance

View File

@ -250,9 +250,9 @@ class TrackSelectDialog(QDialog):
self.track: Optional[Tracks] = None
self.signals = MusicMusterSignals()
record = Settings.get_int_settings(self.session, "dbdialog_width")
record = Settings.get_setting(self.session, "dbdialog_width")
width = record.f_int or 800
record = Settings.get_int_settings(self.session, "dbdialog_height")
record = Settings.get_setting(self.session, "dbdialog_height")
height = record.f_int or 600
self.resize(width, height)
@ -366,13 +366,13 @@ class TrackSelectDialog(QDialog):
if not event:
return
record = Settings.get_int_settings(self.session, "dbdialog_height")
if record.f_int != self.height():
record.update(self.session, {"f_int": self.height()})
record = Settings.get_setting(self.session, "dbdialog_height")
record.f_int = self.height()
record = Settings.get_int_settings(self.session, "dbdialog_width")
if record.f_int != self.width():
record.update(self.session, {"f_int": self.width()})
record = Settings.get_setting(self.session, "dbdialog_width")
record.f_int = self.width()
self.session.commit()
event.accept()

View File

@ -118,11 +118,8 @@ def get_embedded_time(text: str) -> Optional[dt.datetime]:
def get_all_track_metadata(filepath: str) -> Dict[str, str | int | float]:
"""Return all track metadata"""
return (
get_audio_metadata(filepath)
| get_tags(filepath)
| dict(path=filepath)
)
return get_audio_metadata(filepath) | get_tags(filepath) | dict(path=filepath)
def get_audio_metadata(filepath: str) -> Dict[str, str | int | float]:
"""Return audio metadata"""

View File

@ -8,7 +8,6 @@ import sys
# PyQt imports
# Third party imports
from alchemical import Alchemical # type:ignore
from sqlalchemy import (
bindparam,
delete,
@ -22,18 +21,20 @@ from sqlalchemy.orm import joinedload
from sqlalchemy.orm.session import Session
# App imports
from dbmanager import DatabaseManager
import dbtables
from config import Config
from log import log
# Establish database connection
ALCHEMICAL_DATABASE_URI = os.environ.get("ALCHEMICAL_DATABASE_URI")
if ALCHEMICAL_DATABASE_URI is None:
raise ValueError("ALCHEMICAL_DATABASE_URI is undefined")
if "unittest" in sys.modules and "sqlite" not in ALCHEMICAL_DATABASE_URI:
DATABASE_URL = os.environ.get("DATABASE_URL")
if DATABASE_URL is None:
raise ValueError("DATABASE_URL is undefined")
if "unittest" in sys.modules and "sqlite" not in DATABASE_URL:
raise ValueError("Unit tests running on non-Sqlite database")
db = Alchemical(ALCHEMICAL_DATABASE_URI, engine_options=Config.ENGINE_OPTIONS)
db = DatabaseManager.get_instance(DATABASE_URL, engine_options=Config.ENGINE_OPTIONS).db
db.create_all()
# Database classes
@ -584,22 +585,8 @@ class Settings(dbtables.SettingsTable):
session.commit()
@classmethod
def all_as_dict(cls, session):
"""
Return all setting in a dictionary keyed by name
"""
result = {}
settings = session.scalars(select(cls)).all()
for setting in settings:
result[setting.name] = setting
return result
@classmethod
def get_int_settings(cls, session: Session, name: str) -> "Settings":
"""Get setting for an integer or return new setting record"""
def get_setting(cls, session: Session, name: str) -> "Settings":
"""Get existing setting or return new setting record"""
try:
return session.execute(select(cls).where(cls.name == name)).scalar_one()
@ -607,12 +594,6 @@ class Settings(dbtables.SettingsTable):
except NoResultFound:
return Settings(session, name)
def update(self, session: Session, data: dict) -> None:
for key, value in data.items():
assert hasattr(self, key)
setattr(self, key, value)
session.commit()
class Tracks(dbtables.TracksTable):
def __init__(

View File

@ -239,36 +239,6 @@ class Window(QMainWindow, Ui_MainWindow):
)
else:
with db.Session() as session:
settings = Settings.all_as_dict(session)
record = settings["mainwindow_height"]
if record.f_int != self.height():
record.update(session, {"f_int": self.height()})
record = settings["mainwindow_width"]
if record.f_int != self.width():
record.update(session, {"f_int": self.width()})
record = settings["mainwindow_x"]
if record.f_int != self.x():
record.update(session, {"f_int": self.x()})
record = settings["mainwindow_y"]
if record.f_int != self.y():
record.update(session, {"f_int": self.y()})
# Save splitter settings
splitter_sizes = self.splitter.sizes()
assert len(splitter_sizes) == 2
splitter_top, splitter_bottom = splitter_sizes
record = settings["splitter_top"]
if record.f_int != splitter_top:
record.update(session, {"f_int": splitter_top})
record = settings["splitter_bottom"]
if record.f_int != splitter_bottom:
record.update(session, {"f_int": splitter_bottom})
# Save tab number of open playlists
open_playlist_ids: dict[int, int] = {}
for idx in range(self.tabPlaylist.count()):
@ -280,9 +250,20 @@ class Window(QMainWindow, Ui_MainWindow):
log.debug(f"Set {playlist=} tab to {idx=}")
playlist.tab = idx
# Save current tab
record = settings["active_tab"]
record.update(session, {"f_int": self.tabPlaylist.currentIndex()})
# Save window attributes
splitter_top, splitter_bottom = self.splitter.sizes()
attributes_to_save = dict(
mainwindow_height=self.height(),
mainwindow_width=self.width(),
mainwindow_x=self.x(),
mainwindow_y=self.y(),
splitter_top=splitter_top,
splitter_bottom=splitter_bottom,
active_tab=self.tabPlaylist.currentIndex(),
)
for name, value in attributes_to_save.items():
record = Settings.get_setting(session, name)
record.f_int = value
session.commit()
@ -775,7 +756,7 @@ class Window(QMainWindow, Ui_MainWindow):
playlist_ids.append(playlist.id)
log.debug(f"load_last_playlists() loaded {playlist=}")
# Set active tab
record = Settings.get_int_settings(session, "active_tab")
record = Settings.get_setting(session, "active_tab")
if record.f_int is not None and record.f_int >= 0:
self.tabPlaylist.setCurrentIndex(record.f_int)
@ -1334,40 +1315,17 @@ class Window(QMainWindow, Ui_MainWindow):
"""Set size of window from database"""
with db.Session() as session:
settings = Settings.all_as_dict(session)
if "mainwindow_x" in settings:
record = settings["mainwindow_x"]
x = record.f_int or 1
else:
x = 100
if "mainwindow_y" in settings:
record = settings["mainwindow_y"]
y = record.f_int or 1
else:
y = 100
if "mainwindow_width" in settings:
record = settings["mainwindow_width"]
width = record.f_int or 1599
else:
width = 100
if "mainwindow_height" in settings:
record = settings["mainwindow_height"]
height = record.f_int or 981
else:
height = 100
x = Settings.get_setting(session, "mainwindow_x").f_int or 100
y = Settings.get_setting(session, "mainwindow_y").f_int or 100
width = Settings.get_setting(session, "mainwindow_width").f_int or 100
height = Settings.get_setting(session, "mainwindow_height").f_int or 100
splitter_top = Settings.get_setting(session, "splitter_top").f_int or 100
splitter_bottom = (
Settings.get_setting(session, "splitter_bottom").f_int or 100
)
self.setGeometry(x, y, width, height)
if "splitter_top" in settings:
record = settings["splitter_top"]
splitter_top = record.f_int or 256
else:
splitter_top = 100
if "splitter_bottom" in settings:
record = settings["splitter_bottom"]
splitter_bottom = record.f_int or 256
else:
splitter_bottom = 100
self.splitter.setSizes([splitter_top, splitter_bottom])
return
def set_selected_track_next(self) -> None:
"""
@ -1661,11 +1619,9 @@ class SelectPlaylistDialog(QDialog):
self.session = session
self.playlist = None
record = Settings.get_int_settings(self.session, "select_playlist_dialog_width")
record = Settings.get_setting(self.session, "select_playlist_dialog_width")
width = record.f_int or 800
record = Settings.get_int_settings(
self.session, "select_playlist_dialog_height"
)
record = Settings.get_setting(self.session, "select_playlist_dialog_height")
height = record.f_int or 600
self.resize(width, height)
@ -1676,15 +1632,13 @@ class SelectPlaylistDialog(QDialog):
self.ui.lstPlaylists.addItem(p)
def __del__(self): # review
record = Settings.get_int_settings(
self.session, "select_playlist_dialog_height"
)
if record.f_int != self.height():
record.update(self.session, {"f_int": self.height()})
record = Settings.get_setting(self.session, "select_playlist_dialog_height")
record.f_int = self.height()
record = Settings.get_int_settings(self.session, "select_playlist_dialog_width")
if record.f_int != self.width():
record.update(self.session, {"f_int": self.width()})
record = Settings.get_setting(self.session, "select_playlist_dialog_width")
record.f_int = self.width()
self.session.commit()
def list_doubleclick(self, entry): # review
self.playlist = entry.data(Qt.ItemDataRole.UserRole)

View File

@ -573,7 +573,7 @@ class PlaylistTab(QTableView):
with db.Session() as session:
attr_name = f"playlist_col_{column_number}_width"
record = Settings.get_int_settings(session, attr_name)
record = Settings.get_setting(session, attr_name)
record.f_int = self.columnWidth(column_number)
session.commit()
@ -900,7 +900,7 @@ class PlaylistTab(QTableView):
with db.Session() as session:
for column_number in range(header.count() - 1):
attr_name = f"playlist_col_{column_number}_width"
record = Settings.get_int_settings(session, attr_name)
record = Settings.get_setting(session, attr_name)
if record.f_int is not None:
self.setColumnWidth(column_number, record.f_int)
else:

68
poetry.lock generated
View File

@ -1253,72 +1253,6 @@ files = [
{file = "pyfzf-0.3.1.tar.gz", hash = "sha256:dd902e34cffeca9c3082f96131593dd20b4b3a9bba5b9dde1b0688e424b46bd2"},
]
[[package]]
name = "pygame"
version = "2.5.2"
description = "Python Game Development"
optional = false
python-versions = ">=3.6"
files = [
{file = "pygame-2.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a0769eb628c818761755eb0a0ca8216b95270ea8cbcbc82227e39ac9644643da"},
{file = "pygame-2.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed9a3d98adafa0805ccbaaff5d2996a2b5795381285d8437a4a5d248dbd12b4a"},
{file = "pygame-2.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30d1618672a55e8c6669281ba264464b3ab563158e40d89e8c8b3faa0febebd"},
{file = "pygame-2.5.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39690e9be9baf58b7359d1f3b2336e1fd6f92fedbbce42987be5df27f8d30718"},
{file = "pygame-2.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03879ec299c9f4ba23901b2649a96b2143f0a5d787f0b6c39469989e2320caf1"},
{file = "pygame-2.5.2-cp310-cp310-win32.whl", hash = "sha256:74e1d6284100e294f445832e6f6343be4fe4748decc4f8a51131ae197dae8584"},
{file = "pygame-2.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:485239c7d32265fd35b76ae8f64f34b0637ae11e69d76de15710c4b9edcc7c8d"},
{file = "pygame-2.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34646ca20e163dc6f6cf8170f1e12a2e41726780112594ac061fa448cf7ccd75"},
{file = "pygame-2.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3b8a6e351665ed26ea791f0e1fd649d3f483e8681892caef9d471f488f9ea5ee"},
{file = "pygame-2.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc346965847aef00013fa2364f41a64f068cd096dcc7778fc306ca3735f0eedf"},
{file = "pygame-2.5.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35632035fd81261f2d797fa810ea8c46111bd78ceb6089d52b61ed7dc3c5d05f"},
{file = "pygame-2.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e24d05184e4195fe5ebcdce8b18ecb086f00182b9ae460a86682d312ce8d31f"},
{file = "pygame-2.5.2-cp311-cp311-win32.whl", hash = "sha256:f02c1c7505af18d426d355ac9872bd5c916b27f7b0fe224749930662bea47a50"},
{file = "pygame-2.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:6d58c8cf937815d3b7cdc0fa9590c5129cb2c9658b72d00e8a4568dea2ff1d42"},
{file = "pygame-2.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1a2a43802bb5e89ce2b3b775744e78db4f9a201bf8d059b946c61722840ceea8"},
{file = "pygame-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1c289f2613c44fe70a1e40769de4a49c5ab5a29b9376f1692bb1a15c9c1c9bfa"},
{file = "pygame-2.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:074aa6c6e110c925f7f27f00c7733c6303407edc61d738882985091d1eb2ef17"},
{file = "pygame-2.5.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe0228501ec616779a0b9c4299e837877783e18df294dd690b9ab0eed3d8aaab"},
{file = "pygame-2.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31648d38ecdc2335ffc0e38fb18a84b3339730521505dac68514f83a1092e3f4"},
{file = "pygame-2.5.2-cp312-cp312-win32.whl", hash = "sha256:224c308856334bc792f696e9278e50d099a87c116f7fc314cd6aa3ff99d21592"},
{file = "pygame-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:dd2d2650faf54f9a0f5bd0db8409f79609319725f8f08af6507a0609deadcad4"},
{file = "pygame-2.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9b30bc1220c457169571aac998e54b013aaeb732d2fd8744966cb1cfab1f61d1"},
{file = "pygame-2.5.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78fcd7643358b886a44127ff7dec9041c056c212b3a98977674f83f99e9b12d3"},
{file = "pygame-2.5.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cf093a51cb294ede56c29d4acf41538c00f297fcf78a9b186fb7d23c0577b6"},
{file = "pygame-2.5.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe323acbf53a0195c8c98b1b941eba7ac24e3e2b28ae48e8cda566f15fc4945"},
{file = "pygame-2.5.2-cp36-cp36m-win32.whl", hash = "sha256:5697528266b4716d9cdd44a5a1d210f4d86ef801d0f64ca5da5d0816704009d9"},
{file = "pygame-2.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edda1f7cff4806a4fa39e0e8ccd75f38d1d340fa5fc52d8582ade87aca247d92"},
{file = "pygame-2.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9bd738fd4ecc224769d0b4a719f96900a86578e26e0105193658a32966df2aae"},
{file = "pygame-2.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30a8d7cf12363b4140bf2f93b5eec4028376ca1d0fe4b550588f836279485308"},
{file = "pygame-2.5.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc12e4dea3e88ea8a553de6d56a37b704dbe2aed95105889f6afeb4b96e62097"},
{file = "pygame-2.5.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b34c73cb328024f8db3cb6487a37e54000148988275d8d6e5adf99d9323c937"},
{file = "pygame-2.5.2-cp37-cp37m-win32.whl", hash = "sha256:7d0a2794649defa57ef50b096a99f7113d3d0c2e32d1426cafa7d618eadce4c7"},
{file = "pygame-2.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:41f8779f52e0f6e6e6ccb8f0b5536e432bf386ee29c721a1c22cada7767b0cef"},
{file = "pygame-2.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:677e37bc0ea7afd89dde5a88ced4458aa8656159c70a576eea68b5622ee1997b"},
{file = "pygame-2.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47a8415d2bd60e6909823b5643a1d4ef5cc29417d817f2a214b255f6fa3a1e4c"},
{file = "pygame-2.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ff21201df6278b8ca2e948fb148ffe88f5481fd03760f381dd61e45954c7dff"},
{file = "pygame-2.5.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d29a84b2e02814b9ba925357fd2e1df78efe5e1aa64dc3051eaed95d2b96eafd"},
{file = "pygame-2.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d78485c4d21133d6b2fbb504cd544ca655e50b6eb551d2995b3aa6035928adda"},
{file = "pygame-2.5.2-cp38-cp38-win32.whl", hash = "sha256:d851247239548aa357c4a6840fb67adc2d570ce7cb56988d036a723d26b48bff"},
{file = "pygame-2.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:88d1cdacc2d3471eceab98bf0c93c14d3a8461f93e58e3d926f20d4de3a75554"},
{file = "pygame-2.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4f1559e7efe4efb9dc19d2d811d702f325d9605f9f6f9ececa39ee6890c798f5"},
{file = "pygame-2.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cf2191b756ceb0e8458a761d0c665b0c70b538570449e0d39b75a5ba94ac5cf0"},
{file = "pygame-2.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cf2257447ce7f2d6de37e5fb019d2bbe32ed05a5721ace8bc78c2d9beaf3aee"},
{file = "pygame-2.5.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cbbfaba2b81434d62631d0b08b85fab16cf4a36e40b80298d3868927e1299"},
{file = "pygame-2.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:daca456d5b9f52e088e06a127dec182b3638a775684fb2260f25d664351cf1ae"},
{file = "pygame-2.5.2-cp39-cp39-win32.whl", hash = "sha256:3b3e619e33d11c297d7a57a82db40681f9c2c3ae1d5bf06003520b4fe30c435d"},
{file = "pygame-2.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:1822d534bb7fe756804647b6da2c9ea5d7a62d8796b2e15d172d3be085de28c6"},
{file = "pygame-2.5.2-pp36-pypy36_pp73-win32.whl", hash = "sha256:e708fc8f709a0fe1d1876489345f2e443d47f3976d33455e2e1e937f972f8677"},
{file = "pygame-2.5.2-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c13edebc43c240fb0532969e914f0ccefff5ae7e50b0b788d08ad2c15ef793e4"},
{file = "pygame-2.5.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:263b4a7cbfc9fe2055abc21b0251cc17dea6dff750f0e1c598919ff350cdbffe"},
{file = "pygame-2.5.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e58e2b0c791041e4bccafa5bd7650623ba1592b8fe62ae0a276b7d0ecb314b6c"},
{file = "pygame-2.5.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0bd67426c02ffe6c9827fc4bcbda9442fbc451d29b17c83a3c088c56fef2c90"},
{file = "pygame-2.5.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dcff6cbba1584cf7732ce1dbdd044406cd4f6e296d13bcb7fba963fb4aeefc9"},
{file = "pygame-2.5.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce4b6c0bfe44d00bb0998a6517bd0cf9455f642f30f91bc671ad41c05bf6f6ae"},
{file = "pygame-2.5.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68c4e8e60b725ffc7a6c6ecd9bb5fcc5ed2d6e0e2a2c4a29a8454856ef16ad63"},
{file = "pygame-2.5.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f3849f97372a3381c66955f99a0d58485ccd513c3d00c030b869094ce6997a6"},
{file = "pygame-2.5.2.tar.gz", hash = "sha256:c1b89eb5d539e7ac5cf75513125fb5f2f0a2d918b1fd6e981f23bf0ac1b1c24a"},
]
[[package]]
name = "pygments"
version = "2.17.2"
@ -2036,4 +1970,4 @@ test = ["websockets"]
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "b33fb0a465d9bea7ec2bf14800405452d2af35e377d4c02022e33d318d8f190c"
content-hash = "3b2e747f93972b78a9a35454810c99c4ec81e14fc9780e65a6a4434a97d1a713"

View File

@ -21,7 +21,6 @@ pydymenu = "^0.5.2"
stackprinter = "^0.2.10"
pyqt6 = "^6.7.0"
pyqt6-webengine = "^6.7.0"
pygame = "^2.5.2"
pyqtgraph = "^0.13.3"
colorlog = "^6.8.2"
alchemical = "^1.0.2"

14
tests/__init__.py Normal file
View File

@ -0,0 +1,14 @@
# Standard library imports
import os
# PyQt imports
# Third party imports
# App imports
# Set up test database before importing db
# https://blog.miguelgrinberg.com/post/how-to-write-unit-tests-in-python-part-3-web-applications
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ["DATABASE_URL"] = "sqlite:///" + DB_FILE

View File

@ -1,5 +1,6 @@
# Standard library imports
import datetime as dt
import os
import shutil
import tempfile
import unittest
@ -95,7 +96,9 @@ class TestMMHelpers(unittest.TestCase):
_, mp3_temp_path = tempfile.mkstemp(suffix=".mp3")
shutil.copyfile("testdata/isa.mp3", mp3_temp_path)
normalise_track(mp3_temp_path)
os.unlink(mp3_temp_path)
_, flac_temp_path = tempfile.mkstemp(suffix=".flac")
shutil.copyfile("testdata/isa.flac", flac_temp_path)
normalise_track(flac_temp_path)
os.unlink(flac_temp_path)

View File

@ -1,5 +1,4 @@
# Standard library imports
import os
import unittest
# PyQt imports
@ -8,15 +7,7 @@ import unittest
import pytest
# App imports
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from models import db, Settings # noqa: E402
from app.models import db, Settings
class TestMMMisc(unittest.TestCase):
@ -41,10 +32,9 @@ class TestMMMisc(unittest.TestCase):
setting = Settings(session, SETTING_NAME)
# test repr
_ = str(setting)
setting.update(session, dict(f_int=VALUE))
_ = Settings.all_as_dict(session)
test = Settings.get_int_settings(session, SETTING_NAME)
setting.f_int = VALUE
test = Settings.get_setting(session, SETTING_NAME)
assert test.name == SETTING_NAME
assert test.f_int == VALUE
test_new = Settings.get_int_settings(session, NO_SUCH_SETTING)
test_new = Settings.get_setting(session, NO_SUCH_SETTING)
assert test_new.name == NO_SUCH_SETTING

View File

@ -1,6 +1,5 @@
# Standard library imports
import datetime as dt
import os
import unittest
# PyQt imports
@ -10,13 +9,7 @@ import unittest
# App imports
from app import helpers
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from app.models import ( # noqa: E402
from app.models import (
db,
NoteColours,
Playdates,

View File

@ -1,5 +1,4 @@
# Standard library imports
import os
import unittest
# PyQt imports
@ -9,15 +8,8 @@ from PyQt6.QtCore import Qt, QModelIndex
# App imports
from app.helpers import get_all_track_metadata
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from app import playlistmodel # noqa: E402
from app.models import ( # noqa: E402
from app import playlistmodel
from app.models import (
db,
Playlists,
Tracks,

View File

@ -3,22 +3,19 @@ import os
import unittest
# PyQt imports
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QColor
# Third party imports
import pytest
from pytestqt.plugin import QtBot # type: ignore
# App imports
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from config import Config
from app import playlistmodel, utilities
from app.models import ( # noqa: E402
from app.models import (
db,
NoteColours,
Playlists,
Tracks,
)
@ -116,7 +113,9 @@ class MyTestCase(unittest.TestCase):
model = playlistmodel.PlaylistModel(playlist.id)
# Add a track with a note
model.insert_row(proposed_row_number=0, track_id=self.tracks[1]['id'], note=note_text)
model.insert_row(
proposed_row_number=0, track_id=self.tracks[1]["id"], note=note_text
)
# We need to commit the session before re-querying
session.commit()
@ -127,7 +126,7 @@ class MyTestCase(unittest.TestCase):
retrieved_playlist = all_playlists[0]
assert len(retrieved_playlist.rows) == 1
paths = [a.track.path for a in retrieved_playlist.rows]
assert self.tracks[1]['path'] in paths
assert self.tracks[1]["path"] in paths
notes = [a.note for a in retrieved_playlist.rows]
assert note_text in notes