Rebase dev onto v2_id branch

This commit is contained in:
Keith Edmunds 2022-02-06 22:58:15 +00:00
parent db86d04b9a
commit a164f4c962
13 changed files with 111 additions and 188 deletions

View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (musicmuster) (2)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Poetry (musicmuster) (2)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>
</project> </project>

View File

@ -36,15 +36,8 @@ class Config(object):
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS') is not None MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS') is not None
MAX_INFO_TABS = 3 MAX_INFO_TABS = 3
MILLISECOND_SIGFIGS = 0 MILLISECOND_SIGFIGS = 0
MYSQL_CONNECT = os.environ.get('MYSQL_CONNECT') or "mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_dev" # noqa E501 MYSQL_CONNECT = os.environ.get('MYSQL_CONNECT') or "mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_v2" # noqa E501
NORMALISE_ON_IMPORT = True NORMALISE_ON_IMPORT = True
NOTE_COLOURS = {
'track': "#ffff00",
'request': "#7cf000",
'wrap': "#fffacd",
'this month then': "#c256c2",
'story': "#dda0dd",
}
ROOT = os.environ.get('ROOT') or "/home/kae/music" ROOT = os.environ.get('ROOT') or "/home/kae/music"
TESTMODE = True TESTMODE = True
TIMER_MS = 500 TIMER_MS = 500

View File

@ -19,7 +19,7 @@ from sqlalchemy import (
) )
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
from sqlalchemy.orm import relationship, sessionmaker from sqlalchemy.orm import relationship, sessionmaker, scoped_session
from config import Config from config import Config
from log import DEBUG, ERROR from log import DEBUG, ERROR
@ -27,16 +27,26 @@ from log import DEBUG, ERROR
# Create session at the global level as per # Create session at the global level as per
# https://docs.sqlalchemy.org/en/13/orm/session_basics.html # https://docs.sqlalchemy.org/en/13/orm/session_basics.html
# Set up database connection Base = declarative_base()
engine = sqlalchemy.create_engine(f"{Config.MYSQL_CONNECT}?charset=utf8", Session = scoped_session(sessionmaker())
def dbinit():
# Set up database connection
global Session
engine = sqlalchemy.create_engine(
f"{Config.MYSQL_CONNECT}?charset=utf8",
encoding='utf-8', encoding='utf-8',
echo=Config.DISPLAY_SQL, echo=Config.DISPLAY_SQL,
pool_pre_ping=True) pool_pre_ping=True)
Base = declarative_base()
Base.metadata.create_all(engine)
# Create a Session factory Session.configure(bind=engine)
Session = sessionmaker(bind=engine) Base.metadata.create_all(engine)
# Create a Session factory
Session = sessionmaker(bind=engine)
# Database classes # Database classes

View File

@ -27,7 +27,7 @@ import helpers
import music import music
from config import Config from config import Config
from model import (Notes, Playdates, Playlists, PlaylistTracks, from models import (dbinit, Notes, Playdates, Playlists, PlaylistTracks,
Session, Settings, Tracks) Session, Settings, Tracks)
from playlists import PlaylistTab from playlists import PlaylistTab
from songdb import create_track_from_file from songdb import create_track_from_file
@ -226,12 +226,14 @@ class Window(QMainWindow, Ui_MainWindow):
def close_tab(self, index): def close_tab(self, index):
if hasattr(self.tabPlaylist.widget(index), 'is_playlist'): if hasattr(self.tabPlaylist.widget(index), 'is_playlist'):
if self.tabPlaylist.widget(index) == self.current_track_playlist_tab: if self.tabPlaylist.widget(index) == (
self.statusbar.showMessage("Can't close current track playlist", self.current_track_playlist_tab):
5000) self.statusbar.showMessage(
"Can't close current track playlist", 5000)
return return
if self.tabPlaylist.widget(index) == self.next_track_playlist_tab: if self.tabPlaylist.widget(index) == self.next_track_playlist_tab:
self.statusbar.showMessage("Can't close next track playlist", 5000) self.statusbar.showMessage(
"Can't close next track playlist", 5000)
return return
# It's OK to close this playlist so remove from open playlist list # It's OK to close this playlist so remove from open playlist list
with Session() as session: with Session() as session:
@ -950,6 +952,7 @@ class SelectPlaylistDialog(QDialog):
def main(): def main():
try: try:
app = QApplication(sys.argv) app = QApplication(sys.argv)
dbinit()
win = Window() win = Window()
win.show() win.show()
sys.exit(app.exec()) sys.exit(app.exec())

View File

@ -19,7 +19,7 @@ from config import Config
from datetime import datetime, timedelta from datetime import datetime, timedelta
from helpers import get_relative_date, open_in_audacity from helpers import get_relative_date, open_in_audacity
from log import DEBUG, ERROR from log import DEBUG, ERROR
from model import ( from models import (
Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks, NoteColours Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks, NoteColours
) )
from songdb import create_track_from_file, update_meta from songdb import create_track_from_file, update_meta

View File

@ -8,7 +8,7 @@ import tempfile
from config import Config from config import Config
from helpers import show_warning from helpers import show_warning
from log import DEBUG, INFO from log import DEBUG, INFO
from model import Notes, Playdates, PlaylistTracks, Session, Tracks from models import Notes, Playdates, PlaylistTracks, Session, Tracks
from mutagen.flac import FLAC from mutagen.flac import FLAC
from mutagen.mp3 import MP3 from mutagen.mp3 import MP3
from pydub import AudioSegment, effects from pydub import AudioSegment, effects

View File

@ -1,83 +1,56 @@
# https://stewartadam.io/blog/2019/04/04/testing-flask-applications-code-database-views-flask-config-and-app-context-pytest # https://itnext.io/setting-up-transactional-tests-with-pytest-and-sqlalchemy-b2d726347629
import pytest import pytest
import sys import sys
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
sys.path.append("app") sys.path.append("app")
from app.models import Base # noqa E402
from config import Config # noqa E402
from flask import g # noqa E402
from kpi import create_app # noqa E402
from kpi import db as _db # noqa E402
class TestConfig(Config): @pytest.fixture(scope="session")
# TESTING = True def connection():
SQLALCHEMY_DATABASE_URI = 'sqlite://' engine = create_engine(
PYTESTING = True "mysql+mysqldb://musicmuster_testing:musicmuster_testing@"
"localhost/musicmuster_testing"
)
return engine.connect()
# @pytest.fixture(scope="module")
# def app(): def seed_database():
# app = create_app(TestConfig) pass
# app_context = app.app_context()
# app_context.push() # users = [
# db.create_all() # {
# return app # "id": 1,
# "name": "John Doe",
# },
# # ...
# ]
# for user in users:
# db_user = User(**user)
# db_session.add(db_user)
# db_session.commit()
@pytest.fixture(scope="session")
def setup_database(connection):
Base.metadata.bind = connection
Base.metadata.create_all()
# seed_database()
yield
models.Base.metadata.drop_all()
@pytest.fixture @pytest.fixture
def no_bank_holidays(): def db_session(setup_database, connection):
"Set no bank holidays"
g.BankHolidays = []
@pytest.fixture(scope="session")
def app(request):
"""Test session-wide test `Flask` application."""
app = create_app(TestConfig)
return app
@pytest.fixture(autouse=True)
def _setup_app_context_for_test(request, app):
"""
Given app is session-wide, sets up a app context per test to ensure that
app and request stack is not shared between tests.
"""
ctx = app.app_context()
ctx.push()
yield # tests will run here
ctx.pop()
@pytest.fixture(scope="session")
def db(app, request):
"""Returns session-wide initialized database"""
with app.app_context():
_db.create_all()
yield _db
_db.drop_all()
@pytest.fixture(scope="function")
def session(app, db, request):
"""Creates a new database session for each test,
rolling back changes afterwards"""
connection = _db.engine.connect()
transaction = connection.begin() transaction = connection.begin()
yield scoped_session(
options = dict(bind=connection, binds={}) sessionmaker(autocommit=False, autoflush=False, bind=connection)
session = _db.create_scoped_session(options=options) )
_db.session = session
yield session
transaction.rollback() transaction.rollback()
connection.close()
session.remove()

View File

@ -1,83 +0,0 @@
# https://stewartadam.io/blog/2019/04/04/testing-flask-applications-code-database-views-flask-config-and-app-context-pytest
import pytest
import sys
sys.path.append("app")
from config import Config # noqa E402
from flask import g # noqa E402
from kpi import create_app # noqa E402
from kpi import db as _db # noqa E402
class TestConfig(Config):
# TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
PYTESTING = True
# @pytest.fixture(scope="module")
# def app():
# app = create_app(TestConfig)
# app_context = app.app_context()
# app_context.push()
# db.create_all()
# return app
@pytest.fixture
def no_bank_holidays():
"Set no bank holidays"
g.BankHolidays = []
@pytest.fixture(scope="session")
def app(request):
"""Test session-wide test `Flask` application."""
app = create_app(TestConfig)
return app
@pytest.fixture(autouse=True)
def _setup_app_context_for_test(request, app):
"""
Given app is session-wide, sets up a app context per test to ensure that
app and request stack is not shared between tests.
"""
ctx = app.app_context()
ctx.push()
yield # tests will run here
ctx.pop()
@pytest.fixture(scope="session")
def db(app, request):
"""Returns session-wide initialized database"""
with app.app_context():
_db.create_all()
yield _db
_db.drop_all()
@pytest.fixture(scope="function")
def session(app, db, request):
"""Creates a new database session for each test,
rolling back changes afterwards"""
connection = _db.engine.connect()
transaction = connection.begin()
options = dict(bind=connection, binds={})
session = _db.create_scoped_session(options=options)
_db.session = session
yield session
transaction.rollback()
connection.close()
session.remove()

View File

@ -24,7 +24,7 @@ fileConfig(config.config_file_name)
path = os.path.dirname(os.path.dirname(__file__)) path = os.path.dirname(os.path.dirname(__file__))
sys.path.insert(0, path) sys.path.insert(0, path)
sys.path.insert(0, os.path.join(path, "app")) sys.path.insert(0, os.path.join(path, "app"))
from app.model import Base from app.models import Base
target_metadata = Base.metadata target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py, # other values from the config, defined by the needs of env.py,
# can be acquired: # can be acquired:

25
poetry.lock generated
View File

@ -353,6 +353,27 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "pytest"
version = "7.0.0"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
tomli = ">=1.0.0"
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]] [[package]]
name = "python-vlc" name = "python-vlc"
version = "3.0.12118" version = "3.0.12118"
@ -804,6 +825,10 @@ pyqtwebengine-qt5 = [
{file = "PyQtWebEngine_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9e80b408d8de09d4e708d5d84c3ceaf3603292ff8f5e566ae44bb0320fa59c33"}, {file = "PyQtWebEngine_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9e80b408d8de09d4e708d5d84c3ceaf3603292ff8f5e566ae44bb0320fa59c33"},
{file = "PyQtWebEngine_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:24231f19e1595018779977de6722b5c69f3d03f34a5f7574ff21cd1e764ef76d"}, {file = "PyQtWebEngine_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:24231f19e1595018779977de6722b5c69f3d03f34a5f7574ff21cd1e764ef76d"},
] ]
pytest = [
{file = "pytest-7.0.0-py3-none-any.whl", hash = "sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9"},
{file = "pytest-7.0.0.tar.gz", hash = "sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11"},
]
python-vlc = [ python-vlc = [
{file = "python-vlc-3.0.12118.tar.gz", hash = "sha256:566f2f7c303f6800851cacc016df1c6eeec094ad63e0a49d87db9d698094f1fb"}, {file = "python-vlc-3.0.12118.tar.gz", hash = "sha256:566f2f7c303f6800851cacc016df1c6eeec094ad63e0a49d87db9d698094f1fb"},
{file = "python_vlc-3.0.12118-py3-none-any.whl", hash = "sha256:f88be06c6f819a4db2de1c586b193b5df1410ff10fca33b8c6f4e56037c46f7b"}, {file = "python_vlc-3.0.12118-py3-none-any.whl", hash = "sha256:f88be06c6f819a4db2de1c586b193b5df1410ff10fca33b8c6f4e56037c46f7b"},

View File

@ -20,6 +20,7 @@ PyQt5-sip = "^12.9.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
mypy = "^0.931" mypy = "^0.931"
pytest = "^7.0.0"
ipdb = "^0.13.9" ipdb = "^0.13.9"
[build-system] [build-system]

View File

@ -1 +0,0 @@
3149370

View File

@ -1,7 +1,6 @@
from model import Playlists, Session from app.models import Playlists, Session
def test_get_colour(): def test_get_colour(db_session):
with Session() as session: x = Playlists.get_all_playlists(db_session)
x = Playlists.get_all_playlists(session)
assert False assert False