add export to json

This commit is contained in:
Thomas Peetz
2025-01-13 16:18:13 +01:00
parent 3f0a37ff19
commit 89b7b87b8c
8 changed files with 150 additions and 54 deletions
+98 -20
View File
@@ -1,3 +1,7 @@
import json
from datetime import datetime
from pathlib import Path
import mariadb import mariadb
from sqlalchemy import create_engine, select, text, MetaData, join from sqlalchemy import create_engine, select, text, MetaData, join
from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker
@@ -30,35 +34,67 @@ class KontorDB:
__session__ = sessionmaker(bind=engine) __session__ = sessionmaker(bind=engine)
self.session = __session__() self.session = __session__()
def get_table_id(self, table_name):
result = self.session.execute(select(MetaDataTable.id).where(MetaDataTable.table_name == table_name)).scalar()
return result
def get_table_names(self) -> list: def get_table_names(self) -> list:
tables = self.session.query(MetaDataTable).all() tables = self.session.query(MetaDataTable).all()
result = [table.table_name for table in tables] result = [table.table_name for table in tables]
return result return result
def get_column_meta_data(self, table_id: str, table_name: str) -> dict: def get_column_meta_data(self, table_name: str, view_only=True) -> dict:
meta_data = {} meta_data = {}
order = 0 order = 0
for (_, column) in self.session.query(MetaDataTable, MetaDataColumn).filter(MetaDataTable.id == MetaDataColumn.table_id).filter(MetaDataTable.table_name == table_name).filter(MetaDataColumn.is_shown == 1).all(): if view_only:
meta_data[order] = {'column': column.column_name, 'label': column.column_label, 'order': column.column_order, 'ref_column': column.ref_column} for (_, column) in (self.session.query(MetaDataTable, MetaDataColumn).
order += 1 filter(MetaDataTable.id == MetaDataColumn.table_id).
filter(MetaDataTable.table_name == table_name).
filter(MetaDataColumn.is_shown == 1).all()):
meta_data[order] = {'column': column.column_name, 'label': column.column_label,
'order': column.column_order, 'ref_column': column.ref_column}
order += 1
else:
for (_, column) in (self.session.query(MetaDataTable, MetaDataColumn).
filter(MetaDataTable.id == MetaDataColumn.table_id).
filter(MetaDataTable.table_name == table_name).all()):
meta_data[order] = {
'column': column.column_name,
'order': column.column_order,
'ref_column': column.ref_column
}
order += 1
return meta_data return meta_data
def get_filters(self, table_id): def get_filters(self, table_name):
cursor = self.db_conn.cursor() _filter_map = {}
filters = {} for (_, column) in (self.session.query(MetaDataTable, MetaDataColumn).
cursor.execute( filter(MetaDataTable.id == MetaDataColumn.table_id).
"SELECT column_name, filter_label from meta_data_column WHERE table_id=? AND show_filter is true", filter(MetaDataTable.table_name == table_name).
(table_id,)) filter(MetaDataColumn.show_filter == 1).all()):
rows = cursor.fetchall() _filter_map[column.column_name] = {'label': column.filter_label, 'widget': None}
for row in rows: print(f"retrieved {len(_filter_map)} filters: {_filter_map}")
filters[row[0]] = {'label': row[1], 'widget': None} return _filter_map
cursor.close()
# print(f"retrieved {len(rows)} filters: {filters}") def data(self, table, columns: dict, filters) -> list:
return filters data = []
entries = []
if len(filters) == 0:
entries = self.session.query(table).all()
else:
entries = self.session.query(table).filter_by(**filters)
for entry in entries:
row = []
for order in columns.keys():
column_name = columns[order]['column']
if str(column_name).endswith("_id"):
ref_table = column_name[:-3]
# print(f"{ref_table=}")
ref = getattr(entry, ref_table)
value = getattr(ref, "name")
# print(f"{value=}")
row.append(value)
else:
row.append(getattr(entry, column_name))
# print(repr(row))
data.append(row)
return data
def get_data(self, table_name: str, columns: dict, where_clause: str) -> list: def get_data(self, table_name: str, columns: dict, where_clause: str) -> list:
data = [] data = []
@@ -103,3 +139,45 @@ class KontorDB:
statement = f"SELECT {columns} FROM {table} {where_clause}" statement = f"SELECT {columns} FROM {table} {where_clause}"
print(f"{statement=}") print(f"{statement=}")
return statement return statement
def export_db(self, export_type: str, export_file_name: str, export_table_list: list):
print(f"export DB to {export_file_name} as {export_type}")
db = {}
for table in export_table_list:
columns = self.get_column_meta_data(table, view_only=False)
model = Base.model_lookup_by_table_name(table)
rows = self.session.query(model).all()
entries = []
print(f"found {len(rows)} entries")
print(f"found {len(columns)} columns")
for row in rows:
print(row)
entry = {}
for order in columns:
print(columns[order])
column_name = columns[order]['column']
print(f"get value {column_name} from {row} of table {table}")
try:
value = getattr(row, column_name)
if isinstance(value, datetime):
entry[column_name] = str(value)
else:
entry[column_name] = value
except AttributeError as error:
print("could not get value")
entries.append(entry)
db[table] = entries
export_file = Path(export_file_name)
match export_type:
case "JSON":
json_dump = json.dumps(db, indent=4)
with open(export_file_name, "w") as dump_file:
dump_file.write(json_dump)
case "YAML":
export_file = Path(export_file_name)
case "SQLite":
export_file = Path(export_file_name)
case _:
print("unknown export type")
if export_file.exists():
print(f"{export_file} exists")
+16 -3
View File
@@ -1,7 +1,20 @@
from sqlalchemy import Column, String, DateTime, Integer from sqlalchemy import Column, String, DateTime, Integer
from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker, declarative_base
class Base(DeclarativeBase): # class Base(DeclarativeBase):
pass # pass
class BaseModel:
@classmethod
def model_lookup_by_table_name(cls, table_name):
registry_instance = getattr(cls, "registry")
for mapper_ in registry_instance.mappers:
model = mapper_.class_
model_class_name = model.__tablename__
if model_class_name == table_name:
return model
Base = declarative_base(cls=BaseModel)
+4 -4
View File
@@ -1,4 +1,4 @@
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.dialects.mysql import BIT from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@@ -106,7 +106,7 @@ class Artist(Base):
comic_works = relationship("ComicWork") comic_works = relationship("ComicWork")
class Worktype(Base): class WorkType(Base):
__tablename__ = "worktype" __tablename__ = "worktype"
id = Column(String, primary_key=True) id = Column(String, primary_key=True)
created_date = Column(DateTime) created_date = Column(DateTime)
@@ -126,5 +126,5 @@ class ComicWork(Base):
comic = relationship("Comic", back_populates="comic_works") comic = relationship("Comic", back_populates="comic_works")
artist_id = Column(String, ForeignKey("artist.id"), nullable=False) artist_id = Column(String, ForeignKey("artist.id"), nullable=False)
artist = relationship("Artist", back_populates="comic_works") artist = relationship("Artist", back_populates="comic_works")
worktype_id = Column(String, ForeignKey("worktype.id"), nullable=False) work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
worktype = relationship("Worktype", back_populates="comic_works") work_type = relationship("WorkType", back_populates="comic_works")
+6 -7
View File
@@ -1,7 +1,6 @@
from typing import List from typing import List
from PyQt5.QtCore import QAbstractTableModel from PySide6.QtCore import QModelIndex, QAbstractTableModel
from PySide6.QtCore import QModelIndex
from PySide6.QtGui import Qt from PySide6.QtGui import Qt
from gui.data_view import DataViewMeta from gui.data_view import DataViewMeta
@@ -14,19 +13,19 @@ class DataViewModel(QAbstractTableModel):
self._config = None self._config = None
self._data = List[DataViewMeta] self._data = List[DataViewMeta]
def rowCount(self, parent = QModelIndex()): def rowCount(self, parent=QModelIndex()):
return len(self._data) return len(self._data)
def columnCount(self, parent = QModelIndex()): def columnCount(self, parent=QModelIndex()):
return 0 return 0
def headerData(self, section, orientation, role = Qt.ItemDataRole.DisplayRole): def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
return None return None
def data(self, index, role = Qt.ItemDataRole.DisplayRole): def data(self, index, role=Qt.ItemDataRole.DisplayRole):
return None return None
def setData(self, index, value, role = Qt.ItemDataRole.EditRole): def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
return False return False
def flags(self, index): def flags(self, index):
+2 -2
View File
@@ -1,7 +1,7 @@
from pathlib import Path from pathlib import Path
from PySide6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QFileDialog, \ from PySide6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QFileDialog, \
QGroupBox, QCheckBox, QComboBox QCheckBox, QComboBox
class ExportKontorDialog(QDialog): class ExportKontorDialog(QDialog):
@@ -10,7 +10,7 @@ class ExportKontorDialog(QDialog):
self.parent = parent self.parent = parent
self.kontor_db = kontor_db self.kontor_db = kontor_db
self.file_name = None self.file_name = "data.json"
self.tables = [] self.tables = []
self._table_options = {} self._table_options = {}
+5 -4
View File
@@ -34,8 +34,8 @@ class MainWindow(QMainWindow):
parent_layout = QVBoxLayout() parent_layout = QVBoxLayout()
self.central_widget.setLayout(parent_layout) self.central_widget.setLayout(parent_layout)
self.tabs = QTabWidget() self.tabs = QTabWidget()
self.tabs.addTab(self.generate_data_tab("comic"), "Comics") self.tabs.addTab(self.generate_data_tab("comic", Comic), "Comics")
self.tabs.addTab(self.generate_data_tab("media_file"), "MediaFile") self.tabs.addTab(self.generate_data_tab("media_file", MediaFile), "MediaFile")
self.tabs.currentChanged.connect(self._tab_changed) self.tabs.currentChanged.connect(self._tab_changed)
#label.setAlignment(Qt.AlignmentFlag.AlignCenter) #label.setAlignment(Qt.AlignmentFlag.AlignCenter)
parent_layout.addWidget(self.tabs) parent_layout.addWidget(self.tabs)
@@ -112,6 +112,7 @@ class MainWindow(QMainWindow):
print(export_dlg.get_tables_to_export()) print(export_dlg.get_tables_to_export())
print(f"export DB to {export_dlg.file_name}") print(f"export DB to {export_dlg.file_name}")
self.statusBar.showMessage(f"export DB to {export_dlg.file_name}", 3000) self.statusBar.showMessage(f"export DB to {export_dlg.file_name}", 3000)
self.kontor_db.export_db(export_dlg.current_export_type, export_dlg.file_name, export_dlg.get_tables_to_export())
else: else:
self.statusBar.showMessage("Export cancelled", 3000) self.statusBar.showMessage("Export cancelled", 3000)
@@ -121,9 +122,9 @@ class MainWindow(QMainWindow):
def _tab_changed(self, tab_index): def _tab_changed(self, tab_index):
self.data[tab_index].refresh() self.data[tab_index].refresh()
def generate_data_tab(self, table_name): def generate_data_tab(self, table_name, table):
data_tab = QWidget() data_tab = QWidget()
table_config = KontorModelConfig(self.kontor_db, self, table_name) table_config = KontorModelConfig(self.kontor_db, self, table_name, table)
model = KontorTableModel(table_config) model = KontorTableModel(table_config)
layout = QVBoxLayout() layout = QVBoxLayout()
self.data.append(model) self.data.append(model)
+18 -13
View File
@@ -6,25 +6,18 @@ from database import KontorDB
class KontorModelConfig: class KontorModelConfig:
def __init__(self, kontor_db: KontorDB, main_window, table_name: str): def __init__(self, kontor_db: KontorDB, main_window, table_name: str, table):
self.header = {} self.header = {}
self.filter = {} self.filter = {}
self.main_window = main_window self.main_window = main_window
self._table = table_name self._table_name = table_name
self._table_id = None self._table = table
self.kontor_db = kontor_db self.kontor_db = kontor_db
self.get_table_config() self.get_table_config()
def get_table_id(self):
if self._table_id is not None:
return
self._table_id = self.kontor_db.get_table_id(self._table)
def get_table_config(self): def get_table_config(self):
if self._table_id is None: self.header = self.kontor_db.get_column_meta_data(self._table_name)
self.get_table_id() self.filter = self.kontor_db.get_filters(self._table_name)
self.header = self.kontor_db.get_column_meta_data(self._table_id, self._table)
self.filter = self.kontor_db.get_filters(self._table_id)
def get_filter(self) -> str: def get_filter(self) -> str:
filter_rule = "" filter_rule = ""
@@ -41,8 +34,20 @@ class KontorModelConfig:
# print(f"{filter_rule=}") # print(f"{filter_rule=}")
return filter_rule return filter_rule
def filters(self) -> dict:
_filters = {}
# print(self.filter["download"].isChecked())
for column, filter_info in self.filter.items():
# print(column, filter_info)
if filter_info['widget'].isChecked():
_filters[column] = True
# print(f"{filter_rule=}")
return _filters
def get_data(self) -> list: def get_data(self) -> list:
data = self.kontor_db.get_data(self._table, self.header, self.get_filter()) # data = self.kontor_db.get_data(self._table_name, self.header, self.get_filter())
# data.clear()
data = self.kontor_db.data(self._table, self.header, self.filters())
# print(f"KontorModelConfig.get_data: {len(data)}") # print(f"KontorModelConfig.get_data: {len(data)}")
# comics = self.kontor_db.session.query(Comic).all() # comics = self.kontor_db.session.query(Comic).all()
# print(f'{len(comics)} Comics loaded') # print(f'{len(comics)} Comics loaded')
+1 -1
View File
@@ -57,7 +57,7 @@ class KontorTableModel(QAbstractTableModel):
else: else:
return self._main_window.cross return self._main_window.cross
if isinstance(value, int): if isinstance(value, int):
print('{}:: {}: {}'.format(index, value, type(value))) # print('{}:: {}: {}'.format(index, value, type(value)))
if value == 1: if value == 1:
return self._main_window.tick return self._main_window.tick
else: else: