diff --git a/app/models.py b/app/models.py
index 8f2a287..1ba9695 100644
--- a/app/models.py
+++ b/app/models.py
@@ -186,7 +186,7 @@ class Playdates(Base):
id: int = Column(Integer, primary_key=True, autoincrement=True)
lastplayed: datetime = Column(DateTime, index=True, default=None)
track_id: int = Column(Integer, ForeignKey('tracks.id'))
- tracks: RelationshipProperty = relationship(
+ track: RelationshipProperty = relationship(
"Tracks", back_populates="playdates", lazy="joined")
def __init__(self, session: Session, track_id: int) -> None:
@@ -212,6 +212,13 @@ class Playdates(Base):
else:
return None
+ @staticmethod
+ def played_after(session: Session, since: datetime) -> List["Playdates"]:
+ """Return a list of Playdates objects since passed time"""
+
+ return session.query(Playdates).filter(
+ Playdates.lastplayed >= since).all()
+
@staticmethod
def remove_track(session: Session, track_id: int) -> None:
"""
@@ -478,7 +485,8 @@ class Tracks(Base):
back_populates="tracks",
lazy="joined")
playdates: RelationshipProperty = relationship("Playdates",
- back_populates="tracks",
+ back_populates="track"
+ "",
lazy="joined")
def __init__(
diff --git a/app/musicmuster.py b/app/musicmuster.py
index d51eb4b..e9f50e1 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -11,7 +11,7 @@ from datetime import datetime
from log import DEBUG, EXCEPTION
from typing import Callable, Dict, List, Optional, Tuple
-from PyQt5.QtCore import QEvent, QProcess, Qt, QTimer, QUrl
+from PyQt5.QtCore import QDate, QEvent, QProcess, Qt, QTime, QTimer, QUrl
from PyQt5.QtGui import QColor
from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
from PyQt5.QtWidgets import (
@@ -36,6 +36,7 @@ from playlists import PlaylistTab
from sqlalchemy.orm.exc import DetachedInstanceError
from ui.dlg_search_database_ui import Ui_Dialog
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
+from ui.downloadcsv_ui import Ui_DateSelect
from ui.main_window_ui import Ui_MainWindow
from utilities import create_track_from_file
@@ -170,6 +171,8 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionAdd_note.triggered.connect(self.create_note)
self.action_Clear_selection.triggered.connect(self.clear_selection)
self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
+ self.actionDownload_CSV_of_played_tracks.triggered.connect(
+ self.download_played_tracks)
self.actionEnable_controls.triggered.connect(
self.enable_play_next_controls)
self.actionExport_playlist.triggered.connect(self.export_playlist_tab)
@@ -279,6 +282,32 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionPlay_next.setEnabled(False)
self.statusbar.showMessage("Play controls: Disabled", 0)
+ def download_played_tracks(self) -> None:
+ """Download a CSV of played tracks"""
+
+ dlg = DownloadCSV(self)
+ if dlg.exec():
+ start_dt = dlg.ui.dateTimeEdit.dateTime().toPyDateTime()
+ # Get output filename
+ pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
+ self, 'Save CSV of tracks played',
+ directory="/tmp/playlist.csv",
+ filter="CSV files (*.csv)"
+ )
+ if not pathspec:
+ return
+
+ path: str = pathspec[0]
+ if not path.endswith(".csv"):
+ path += ".csv"
+
+ with open(path, "w") as f:
+ with Session() as session:
+ for playdate in Playdates.played_after(session, start_dt):
+ f.write(
+ f"{playdate.track.artist},{playdate.track.title}\n"
+ )
+
def enable_play_next_controls(self) -> None:
"""
Enable "play next" keyboard controls
@@ -957,6 +986,18 @@ class DbDialog(QDialog):
self.ui.dbPath.setText(track.path)
+class DownloadCSV(QDialog):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.ui = Ui_DateSelect()
+ self.ui.setupUi(self)
+ self.ui.dateTimeEdit.setDate(QDate.currentDate())
+ self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
+ self.ui.buttonBox.accepted.connect(self.accept)
+ self.ui.buttonBox.rejected.connect(self.reject)
+
+
class SelectPlaylistDialog(QDialog):
def __init__(self, parent=None, playlists=None, session=None):
super().__init__(parent)
diff --git a/app/ui/downloadcsv.ui b/app/ui/downloadcsv.ui
new file mode 100644
index 0000000..0c4141f
--- /dev/null
+++ b/app/ui/downloadcsv.ui
@@ -0,0 +1,107 @@
+
+
+ DateSelect
+
+
+
+ 0
+ 0
+ 280
+ 166
+
+
+
+ Dialog
+
+
+
+
+ 70
+ 110
+ 191
+ 32
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+ 70
+ 60
+ 194
+ 28
+
+
+
+ true
+
+
+
+
+
+ 10
+ 20
+ 261
+ 19
+
+
+
+ Download CSV of tracks played
+
+
+
+
+
+ 15
+ 66
+ 51
+ 19
+
+
+
+ Since:
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ DateSelect
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DateSelect
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/app/ui/downloadcsv_ui.py b/app/ui/downloadcsv_ui.py
new file mode 100644
index 0000000..dc231a9
--- /dev/null
+++ b/app/ui/downloadcsv_ui.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'ui/downloadcsv.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.6
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_DateSelect(object):
+ def setupUi(self, DateSelect):
+ DateSelect.setObjectName("DateSelect")
+ DateSelect.resize(280, 166)
+ self.buttonBox = QtWidgets.QDialogButtonBox(DateSelect)
+ self.buttonBox.setGeometry(QtCore.QRect(70, 110, 191, 32))
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.dateTimeEdit = QtWidgets.QDateTimeEdit(DateSelect)
+ self.dateTimeEdit.setGeometry(QtCore.QRect(70, 60, 194, 28))
+ self.dateTimeEdit.setCalendarPopup(True)
+ self.dateTimeEdit.setObjectName("dateTimeEdit")
+ self.label = QtWidgets.QLabel(DateSelect)
+ self.label.setGeometry(QtCore.QRect(10, 20, 261, 19))
+ self.label.setObjectName("label")
+ self.label_2 = QtWidgets.QLabel(DateSelect)
+ self.label_2.setGeometry(QtCore.QRect(15, 66, 51, 19))
+ self.label_2.setObjectName("label_2")
+
+ self.retranslateUi(DateSelect)
+ self.buttonBox.accepted.connect(DateSelect.accept) # type: ignore
+ self.buttonBox.rejected.connect(DateSelect.reject) # type: ignore
+ QtCore.QMetaObject.connectSlotsByName(DateSelect)
+
+ def retranslateUi(self, DateSelect):
+ _translate = QtCore.QCoreApplication.translate
+ DateSelect.setWindowTitle(_translate("DateSelect", "Dialog"))
+ self.label.setText(_translate("DateSelect", "Download CSV of tracks played"))
+ self.label_2.setText(_translate("DateSelect", "Since:"))
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index 1553706..9572a33 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -786,6 +786,7 @@ border: 1px solid rgb(85, 87, 83);
+
diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py
index 4ae1087..b05b061 100644
--- a/app/ui/main_window_ui.py
+++ b/app/ui/main_window_ui.py
@@ -443,6 +443,8 @@ class Ui_MainWindow(object):
self.actionEnable_controls.setObjectName("actionEnable_controls")
self.actionImport = QtWidgets.QAction(MainWindow)
self.actionImport.setObjectName("actionImport")
+ self.actionDownload_CSV_of_played_tracks = QtWidgets.QAction(MainWindow)
+ self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
self.menuFile.addAction(self.actionImport)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionE_xit)
@@ -465,6 +467,7 @@ class Ui_MainWindow(object):
self.menuPlaylist.addAction(self.actionSelect_played_tracks)
self.menuPlaylist.addAction(self.actionMoveSelected)
self.menuPlaylist.addSeparator()
+ self.menuPlaylist.addAction(self.actionDownload_CSV_of_played_tracks)
self.menuPlaylist.addAction(self.actionExport_playlist)
self.menu_Music.addAction(self.actionPlay_next)
self.menu_Music.addAction(self.actionSkip_next)
@@ -553,4 +556,5 @@ class Ui_MainWindow(object):
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))
self.actionImport.setText(_translate("MainWindow", "Import..."))
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
+ self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
import icons_rc