From fe407d6d892f2079a363d7c65a829cb77d167f7e Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Tue, 7 Jan 2025 21:18:14 +0100 Subject: [PATCH] implement general data tab view --- gui/data.py | 79 +++++++++++++++++ gui/dialogs.py | 85 +++++++++++++++++++ gui/kontor.py | 54 +++++++----- gui/model_config.py | 48 +++-------- gui/table_model.py | 24 +++--- .../kontor/admin/SetupModuleAdmin.java | 30 +++---- 6 files changed, 236 insertions(+), 84 deletions(-) create mode 100644 gui/data.py create mode 100644 gui/dialogs.py diff --git a/gui/data.py b/gui/data.py new file mode 100644 index 0000000..bb4436c --- /dev/null +++ b/gui/data.py @@ -0,0 +1,79 @@ +import mariadb + + +class KontorDB: + + def __init__(self, db_config): + self.db_conn = mariadb.connect( + host=db_config['mariadb']['host'], + port=db_config['mariadb']['port'], + user=db_config['mariadb']['user'], + password=db_config['mariadb']['password'], + database=db_config['mariadb']['database'] + ) + + def get_table_id(self, table_name): + cursor = self.db_conn.cursor() + cursor.execute("SELECT id, created_date, last_modified_date FROM meta_data_table WHERE table_name=?", (table_name, )) + row = cursor.fetchone() + cursor.close() + return row[0] + + def get_table_names(self) -> list: + tables_names = [] + cursor = self.db_conn.cursor() + cursor.execute("SELECT id, table_name from meta_data_table") + rows = cursor.fetchall() + for (_, table_name) in rows: + tables_names.append(table_name) + cursor.close() + return tables_names + + def get_column_meta_data(self, table_id): + cursor = self.db_conn.cursor() + meta_data = {} + cursor.execute("SELECT column_name, column_order, column_label FROM meta_data_column WHERE table_id=? AND is_shown is true ORDER bY column_order", (table_id, )) + rows = cursor.fetchall() + order = 0 + for (column_name, column_order, column_label) in rows: + meta_data[order] = { 'column': column_name, 'label': column_label, 'order': column_order} + order += 1 + cursor.close() + # print(f"retrieved {len(rows)} columns, set {len(meta_data)} headers") + return meta_data + + def get_filters(self, table_id): + cursor = self.db_conn.cursor() + filters = {} + cursor.execute("SELECT column_name, filter_label from meta_data_column WHERE table_id=? AND show_filter is true", (table_id, )) + rows = cursor.fetchall() + for row in rows: + filters[row[0]] = {'label': row[1], 'widget': None} + cursor.close() + # print(f"retrieved {len(rows)} filters: {filters}") + return filters + + def get_data(self, table_name: str, columns: dict, where_clause: str) -> list: + data = [] + cursor = self.db_conn.cursor() + cursor.execute(self.get_statement(table_name, columns, where_clause)) + rows = cursor.fetchall() + print(len(rows)) + for row in rows: + # print(f"KontorDB.get_data: {row}") + data.append(list(row)) + cursor.close() + print(f"KontorDB.getData: return {len(data)}") + return data + + def get_statement(self, table: str, header: dict, where_clause): + columns = "" + for index, column in header.items(): + if index > 0: + columns += ", " + columns += column['column'] + if len(columns) == 0: + columns = "*" + statement = f"SELECT {columns} FROM {table} {where_clause}" + print(f"{statement=}") + return statement diff --git a/gui/dialogs.py b/gui/dialogs.py new file mode 100644 index 0000000..ae69c3d --- /dev/null +++ b/gui/dialogs.py @@ -0,0 +1,85 @@ +from PySide6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QFileDialog, \ + QGroupBox, QCheckBox + + +class ExportKontorDialog(QDialog): + def __init__(self, parent=None, kontor_db=None): + super().__init__(parent) + + self.parent = parent + self.kontor_db = kontor_db + self.file_name = None + self.tables = [] + self._table_options = {} + + buttons = (QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + + self.buttonBox = QDialogButtonBox(buttons) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + self.label = QLabel() + self.label.setText("Export DB to data.json") + layout = QVBoxLayout() + file_layout = QHBoxLayout() + file_layout.addWidget(self.label) + file_button = QPushButton("Select file") + file_button.clicked.connect(self.select_file) + file_layout.addWidget(file_button) + layout.addLayout(file_layout) + + for table_name in self.kontor_db.get_table_names(): + check_box = QCheckBox(table_name) + self._table_options[table_name] = check_box + check_box.stateChanged.connect(self.change_selection) + layout.addWidget(check_box) + layout.addWidget(self.buttonBox) + self.setLayout(layout) + + def change_selection(self): + self.tables.clear() + for (name, box) in self._table_options.items(): + if box.isChecked(): + self.tables.append(name) + + def select_file(self): + file_dialog = QFileDialog() + file_dialog.setFileMode(QFileDialog.FileMode.AnyFile) + if file_dialog.exec(): + self.file_name = file_dialog.selectedFiles()[0] + self.label.setText(f"Export DB to {self.file_name}") + + def get_tables_to_export(self) -> list: + return self.tables + + +class ImportKontorDialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + + self.file_name = None + + QBtn = (QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + + self.buttonBox = QDialogButtonBox(QBtn) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + self.label = QLabel() + self.label.setText("Import DB from data.json") + layout = QVBoxLayout() + file_layout = QHBoxLayout() + file_layout.addWidget(self.label) + file_button = QPushButton("Select file") + file_button.clicked.connect(self.select_file) + file_layout.addWidget(file_button) + layout.addLayout(file_layout) + layout.addWidget(self.buttonBox) + self.setLayout(layout) + + def select_file(self): + file_dialog = QFileDialog() + file_dialog.setFileMode(QFileDialog.FileMode.ExistingFile) + if file_dialog.exec(): + self.file_name = file_dialog.selectedFiles()[0] + self.label.setText(f"Import DB from {self.file_name}") diff --git a/gui/kontor.py b/gui/kontor.py index 3b601ad..771c466 100644 --- a/gui/kontor.py +++ b/gui/kontor.py @@ -11,6 +11,8 @@ from PySide6.QtWidgets import QApplication, QLabel, QMainWindow from platformdirs import PlatformDirs from comic_model import ComicTableModel +from dialogs import ExportKontorDialog, ImportKontorDialog +from data import KontorDB from model_config import KontorModelConfig from table_model import KontorTableModel @@ -35,12 +37,13 @@ class MainWindow(QMainWindow): self.data = [] self.filter = {} + self.kontor_db = KontorDB(config) self.central_widget = QWidget() parent_layout = QVBoxLayout() self.central_widget.setLayout(parent_layout) self.tabs = QTabWidget() - self.tabs.addTab(self.generate_tab_comics(config), "Comics") - self.tabs.addTab(self.generate_tab_media_file(config), "MediaFile") + self.tabs.addTab(self.generate_data_tab("comic"), "Comics") + self.tabs.addTab(self.generate_data_tab("media_file"), "MediaFile") self.tabs.currentChanged.connect(self._tab_changed) #label.setAlignment(Qt.AlignmentFlag.AlignCenter) parent_layout.addWidget(self.tabs) @@ -52,9 +55,13 @@ class MainWindow(QMainWindow): self.aboutAction = QAction("&Über...", self) self.aboutAction.triggered.connect(self.about) self.importAction = QAction(self.import_icon, "&Import", self) + self.importAction.triggered.connect(self.import_from_file) self.exportAction = QAction(self.export_icon, "&Export", self) + self.exportAction.triggered.connect(self.export_to_file) self.refreshAction = QAction(self.circle_icon, "&Refresh", self) self.refreshAction.triggered.connect(self.refresh) + self.updateTitleAction = QAction("&Update Titles", self) + self.downloadAction = QAction("&Download Videos", self) self.exitAction = QAction("&Beenden", self) self.exitAction.setShortcut("Alt+F4") self.exitAction.triggered.connect(self.close) @@ -70,6 +77,10 @@ class MainWindow(QMainWindow): menu_bar.addMenu(kontor_menu) kontor_menu.addAction(self.importAction) kontor_menu.addAction(self.exportAction) + media_file_menu = QMenu("&MediaFile") + media_file_menu.addAction(self.updateTitleAction) + media_file_menu.addAction(self.downloadAction) + kontor_menu.addMenu(media_file_menu) # Help menu help_menu = QMenu("&Hilfe") menu_bar.addMenu(help_menu) @@ -91,36 +102,41 @@ class MainWindow(QMainWindow): def about(self): QMessageBox.about(self.central_widget, "Über Kontor", f"Python: 3.11\nKontor: 0.1.0") + def import_from_file(self): + import_dlg = ImportKontorDialog(self) + if import_dlg.exec(): + print(f"import DB from file {import_dlg.file_name}") + else: + print("no nothing for import") + pass + + def export_to_file(self): + export_dlg = ExportKontorDialog(self, self.kontor_db) + if export_dlg.exec(): + print(export_dlg.get_tables_to_export()) + self.statusBar.showMessage(f"export DB to {export_dlg.file_name}", 3000) + else: + self.statusBar.showMessage("Export cancelled", 3000) + def refresh(self): self.data[self.tabs.currentIndex()].refresh() def _tab_changed(self, tab_index): self.data[tab_index].refresh() - def generate_tab_comics(self, db_configuration): - comic_tab = QWidget() - layout = QVBoxLayout() - comic_tab.setLayout(layout) - model = ComicTableModel(db_configuration, self) - self.data.append(model) - table_view = QTableView() - table_view.setModel(model) - layout.addWidget(table_view) - return comic_tab - - def generate_tab_media_file(self, db_configuration): - media_file_tab = QWidget() - table_config = KontorModelConfig(db_configuration, self, "media_file") + def generate_data_tab(self, table_name): + data_tab = QWidget() + table_config = KontorModelConfig(self.kontor_db, self, table_name) model = KontorTableModel(table_config) layout = QVBoxLayout() - # model = MediaFileTableModel(db_configuration, self) self.data.append(model) - media_file_tab.setLayout(layout) + data_tab.setLayout(layout) table_view = QTableView() table_view.setModel(model) layout.addLayout(table_config.get_filter_layout()) layout.addWidget(table_view) - return media_file_tab + model.refresh() + return data_tab if __name__ == '__main__': diff --git a/gui/model_config.py b/gui/model_config.py index a9c6702..ba3e510 100644 --- a/gui/model_config.py +++ b/gui/model_config.py @@ -1,50 +1,30 @@ import mariadb from PySide6.QtWidgets import QHBoxLayout, QCheckBox +from data import KontorDB + class KontorModelConfig: - def __init__(self, db_config, main_window, table_name: str): + def __init__(self, kontor_db: KontorDB, main_window, table_name: str): self.header = {} self.filter = {} self.main_window = main_window self._table = table_name self._table_id = None - self.db_conn = mariadb.connect( - host=db_config['mariadb']['host'], - port=db_config['mariadb']['port'], - user=db_config['mariadb']['user'], - password=db_config['mariadb']['password'], - database=db_config['mariadb']['database'] - ) + self.kontor_db = kontor_db self.get_table_config() def get_table_id(self): if self._table_id is not None: return - cursor = self.db_conn.cursor() - cursor.execute("SELECT id, created_date, last_modified_date FROM meta_data_table WHERE table_name=?", (self._table, )) - rows = cursor.fetchall() - if len(rows) == 1: - self._table_id = rows[0][0] + self._table_id = self.kontor_db.get_table_id(self._table) def get_table_config(self): if self._table_id is None: self.get_table_id() - cursor = self.db_conn.cursor() - cursor.execute("SELECT column_name, column_order, column_label FROM meta_data_column WHERE table_id=? AND is_shown is true ORDER bY column_order", (self._table_id, )) - rows = cursor.fetchall() - self.header.clear() - order = 0 - for (column_name, column_order, column_label) in rows: - self.header[order] = { 'column': column_name, 'label': column_label, 'order': column_order} - order += 1 - # print(f"retrieved {len(rows)} columns, set {len(self.header)} headers") - cursor.execute("SELECT column_name, filter_label from meta_data_column WHERE table_id=? AND show_filter is true", (self._table_id, )) - rows = cursor.fetchall() - for row in rows: - self.filter[row[0]] = {'label': row[1], 'widget': None} - # print(f"retrieved {len(rows)} filters: {self.filter}") + self.header = self.kontor_db.get_column_meta_data(self._table_id) + self.filter = self.kontor_db.get_filters(self._table_id) def get_filter(self) -> str: filter_rule = "" @@ -61,16 +41,10 @@ class KontorModelConfig: # print(f"{filter_rule=}") return filter_rule - def get_statement(self) -> str: - filter_rule = self.get_filter() - # self.get_table_config() - columns = "" - for index, column in self.header.items(): - if index > 0: - columns += ", " - columns += column['column'] - statement = f"SELECT {columns} FROM media_file {filter_rule}" - return statement + def get_data(self) -> list: + data = self.kontor_db.get_data(self._table, self.header, self.get_filter()) + print(f"KontorModelConfig.get_data: {len(data)}") + return data def get_filter_layout(self) -> QHBoxLayout: filter_layout = QHBoxLayout() diff --git a/gui/table_model.py b/gui/table_model.py index 81eec89..a28e1cc 100644 --- a/gui/table_model.py +++ b/gui/table_model.py @@ -12,24 +12,22 @@ class KontorTableModel(QAbstractTableModel): super().__init__() self._main_window = model_config.main_window self._config = model_config - self._data = None + self._data = [] def refresh(self): - data = [] - cursor = self._config.db_conn.cursor() - cursor.execute(self._config.get_statement()) - rows = cursor.fetchall() - # print(len(rows)) - if len(rows) > 0: + data = self._config.get_data() + count = 0 + # print(data) + if data is not None: self.beginResetModel() - for row in rows: - data.append(list(row)) + self._data.clear() self._data = data self.endResetModel() - else: - self._data = None + count = len(data) + # print(data) + # print(self._data) self.layoutChanged.emit() - self._main_window.statusBar.showMessage(f"{len(rows)} Einträge geladen", 3000) + self._main_window.statusBar.showMessage(f"{count} Einträge geladen", 3000) def rowCount(self, parent=QModelIndex()): # The length of the outer list. @@ -39,7 +37,7 @@ class KontorTableModel(QAbstractTableModel): def headerData(self, col, orientation, role=Qt.ItemDataRole.DisplayRole): if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: - return self._config.header[col]['column'] + return self._config.header[col]['label'] if orientation == Qt.Orientation.Vertical and role == Qt.ItemDataRole.DisplayRole: return str(col+1) diff --git a/springboot/src/main/java/de/thpeetz/kontor/admin/SetupModuleAdmin.java b/springboot/src/main/java/de/thpeetz/kontor/admin/SetupModuleAdmin.java index de81e3b..81c6c6a 100644 --- a/springboot/src/main/java/de/thpeetz/kontor/admin/SetupModuleAdmin.java +++ b/springboot/src/main/java/de/thpeetz/kontor/admin/SetupModuleAdmin.java @@ -128,22 +128,22 @@ public class SetupModuleAdmin implements ApplicationListener