feat: Add playlist management functionality and compliance checks

- Introduced a new "Playlists" column in results view with visual indicators for membership.
- Added `playlist_api.py` to handle playlist-related operations (fetch, add, remove tracks).
- Implemented `PlaylistManagerDialog` for seamless playlist addition/removal with caching.
- Integrated compliance check script (`check_agents_compliance.py`) for ensuring coding standards.
- Added extensive documentation (`playlist_membership_manager.md`) covering architecture and flow.
- Updated GUI logic and QStandardItemModel setup to include playlist data.
- Included unit tests for playlist management components and API.
This commit is contained in:
Warry
2025-12-29 17:37:39 +01:00
parent 75868b32d0
commit 296c6c7d6b
17 changed files with 3433 additions and 4 deletions
+115
View File
@@ -0,0 +1,115 @@
################################################################################
## Form generated from reading UI file 'dialog_playlist_manager.ui'
##
## Created by: Qt User Interface Compiler version 6.10.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt
from PySide6.QtWidgets import (
QFrame,
QHBoxLayout,
QLabel,
QPushButton,
QScrollArea,
QSizePolicy,
QSpacerItem,
QVBoxLayout,
QWidget,
)
class Ui_DialogPlaylistManager:
def setupUi(self, DialogPlaylistManager):
if not DialogPlaylistManager.objectName():
DialogPlaylistManager.setObjectName("DialogPlaylistManager")
DialogPlaylistManager.resize(609, 760)
DialogPlaylistManager.setMinimumSize(QSize(520, 560))
self.verticalLayoutRoot = QVBoxLayout(DialogPlaylistManager)
self.verticalLayoutRoot.setObjectName("verticalLayoutRoot")
self.horizontalLayoutHeader = QHBoxLayout()
self.horizontalLayoutHeader.setObjectName("horizontalLayoutHeader")
self.labelTitle = QLabel(DialogPlaylistManager)
self.labelTitle.setObjectName("labelTitle")
self.labelTitle.setWordWrap(True)
self.horizontalLayoutHeader.addWidget(self.labelTitle)
self.verticalLayoutRoot.addLayout(self.horizontalLayoutHeader)
self.frameSeparator = QFrame(DialogPlaylistManager)
self.frameSeparator.setObjectName("frameSeparator")
self.frameSeparator.setFrameShape(QFrame.Shape.HLine)
self.frameSeparator.setFrameShadow(QFrame.Shadow.Sunken)
self.verticalLayoutRoot.addWidget(self.frameSeparator)
self.scrollArea = QScrollArea(DialogPlaylistManager)
self.scrollArea.setObjectName("scrollArea")
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 589, 666))
self.verticalLayoutList = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayoutList.setObjectName("verticalLayoutList")
self.labelEmpty = QLabel(self.scrollAreaWidgetContents)
self.labelEmpty.setObjectName("labelEmpty")
self.labelEmpty.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.verticalLayoutList.addWidget(self.labelEmpty)
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.verticalLayoutList.addItem(self.verticalSpacer)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayoutRoot.addWidget(self.scrollArea)
self.frameFooterSeparator = QFrame(DialogPlaylistManager)
self.frameFooterSeparator.setObjectName("frameFooterSeparator")
self.frameFooterSeparator.setFrameShape(QFrame.Shape.HLine)
self.frameFooterSeparator.setFrameShadow(QFrame.Shadow.Sunken)
self.verticalLayoutRoot.addWidget(self.frameFooterSeparator)
self.horizontalLayoutFooter = QHBoxLayout()
self.horizontalLayoutFooter.setObjectName("horizontalLayoutFooter")
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayoutFooter.addItem(self.horizontalSpacer)
self.buttonClose = QPushButton(DialogPlaylistManager)
self.buttonClose.setObjectName("buttonClose")
self.horizontalLayoutFooter.addWidget(self.buttonClose)
self.verticalLayoutRoot.addLayout(self.horizontalLayoutFooter)
self.retranslateUi(DialogPlaylistManager)
self.buttonClose.clicked.connect(DialogPlaylistManager.accept)
self.buttonClose.setDefault(True)
QMetaObject.connectSlotsByName(DialogPlaylistManager)
# setupUi
def retranslateUi(self, DialogPlaylistManager):
DialogPlaylistManager.setWindowTitle(
QCoreApplication.translate("DialogPlaylistManager", "G\u00e9rer les playlists", None)
)
self.labelTitle.setText(
QCoreApplication.translate(
"DialogPlaylistManager",
'G\u00e9rer les playlists pour : <b><span style="color:#1e88e5;">Titre de la Piste</span></b>',
None,
)
)
self.labelEmpty.setText(
QCoreApplication.translate("DialogPlaylistManager", "Aucune playlist disponible.", None)
)
self.buttonClose.setText(QCoreApplication.translate("DialogPlaylistManager", "Fermer", None))
# retranslateUi
+147
View File
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogPlaylistManager</class>
<widget class="QDialog" name="DialogPlaylistManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>609</width>
<height>760</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>520</width>
<height>560</height>
</size>
</property>
<property name="windowTitle">
<string>Gérer les playlists</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutRoot">
<item>
<layout class="QHBoxLayout" name="horizontalLayoutHeader">
<item>
<widget class="QLabel" name="labelTitle">
<property name="text">
<string>Gérer les playlists pour : &lt;b&gt;&lt;span style=&quot;color:#1e88e5;&quot;&gt;Titre de la Piste&lt;/span&gt;&lt;/b&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frameSeparator">
<property name="frameShape">
<enum>QFrame::Shape::HLine</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Sunken</enum>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>666</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayoutList">
<item>
<widget class="QLabel" name="labelEmpty">
<property name="text">
<string>Aucune playlist disponible.</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frameFooterSeparator">
<property name="frameShape">
<enum>QFrame::Shape::HLine</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Sunken</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFooter">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonClose">
<property name="text">
<string>Fermer</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonClose</sender>
<signal>clicked()</signal>
<receiver>DialogPlaylistManager</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>