merged command line and gui app in one command

This commit is contained in:
Thomas Peetz
2025-01-14 13:10:24 +01:00
parent 54bc17ee7d
commit 453ad2af7b
69 changed files with 589 additions and 183 deletions
@@ -95,6 +95,9 @@ venv.bak/
.spyderproject
.spyproject
# PyCharm
.idea/
# Rope project settings
.ropeproject
-60
View File
@@ -1,60 +0,0 @@
from cement import Controller, ex
from cement.utils.version import get_version_banner
from ..core.version import get_version
VERSION_BANNER = """
Kontor CLI Tool %s
%s
""" % (get_version(), get_version_banner())
class CliBase(Controller):
class Meta:
label = 'base'
# text displayed at the top of --help output
description = 'Kontor CLI Tool'
# text displayed at the bottom of --help output
epilog = 'Usage: kontor command1 --foo bar'
# controller level arguments. ex: 'kontor --version'
arguments = [
### add a version banner
( [ '-v', '--version' ],
{ 'action' : 'version',
'version' : VERSION_BANNER } ),
]
def _default(self):
"""Default action if no sub-command is passed."""
self.app.args.print_help()
@ex(
help='example sub command1',
# sub-command level arguments. ex: 'kontor command1 --foo bar'
arguments=[
### add a sample foo option under subcommand namespace
( [ '-f', '--foo' ],
{ 'help' : 'notorious foo option',
'action' : 'store',
'dest' : 'foo' } ),
],
)
def command1(self):
"""Example sub-command."""
data = {
'foo' : 'bar',
}
### do something with arguments
if self.app.pargs.foo is not None:
data['foo'] = self.app.pargs.foo
self.app.render(data, 'command1.jinja2')
-183
View File
@@ -1,183 +0,0 @@
import json
from datetime import datetime
from pathlib import Path
import mariadb
from sqlalchemy import create_engine, select, text, MetaData, join
from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker
from .base import Base
from .comic import Comic
from .metadata import MetaDataTable, MetaDataColumn
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']
)
connect_string = ('mariadb+mariadbconnector://{}:{}@{}:{}/{}'
.format(
db_config['mariadb']['user'],
db_config['mariadb']['password'],
db_config['mariadb']['host'],
db_config['mariadb']['port'],
db_config['mariadb']['database']))
# engine = create_engine(connect_string, echo=True)
engine = create_engine(connect_string)
Base.metadata.create_all(bind=engine)
__session__ = sessionmaker(bind=engine)
self.session = __session__()
def get_table_names(self) -> list:
tables = self.session.query(MetaDataTable).all()
result = [table.table_name for table in tables]
return result
def get_column_meta_data(self, table_name: str, view_only=True) -> dict:
meta_data = {}
order = 0
if view_only:
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()):
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
def get_filters(self, table_name):
_filter_map = {}
for (_, column) in (self.session.query(MetaDataTable, MetaDataColumn).
filter(MetaDataTable.id == MetaDataColumn.table_id).
filter(MetaDataTable.table_name == table_name).
filter(MetaDataColumn.show_filter == 1).all()):
_filter_map[column.column_name] = {'label': column.filter_label, 'widget': None}
print(f"retrieved {len(_filter_map)} filters: {_filter_map}")
return _filter_map
def data(self, table, columns: dict, filters) -> list:
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:
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)}")
if table_name == 'comic' and len(where_clause) == 0:
data.clear()
comics = self.session.query(Comic).all()
for item in comics:
# print(item)
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(item, ref_table)
value = getattr(ref, "name")
# print(f"{value=}")
row.append(value)
else:
row.append(getattr(item, column_name))
# print(repr(row))
data.append(row)
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
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")
-20
View File
@@ -1,20 +0,0 @@
from sqlalchemy import Column, String, DateTime, Integer
from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker, declarative_base
# class Base(DeclarativeBase):
# 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)
-131
View File
@@ -1,131 +0,0 @@
from sqlalchemy import Column, DateTime, Integer, String, ForeignKey, UniqueConstraint
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship
from .base import Base
class Sport(Base):
__tablename__ = "sport"
__table_args__ = (
UniqueConstraint("name"),
)
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(255), nullable=False, index=True, unique=True)
teams = relationship("Team")
positions = relationship("FieldPosition")
class Team(Base):
__tablename__ = "team"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(255), nullable=False, index=True, unique=True)
short_name = Column(String(255), nullable=False, )
sport_id = Column(String, ForeignKey("sport.id"), nullable=False)
sport = relationship("Sport", back_populates="positions")
roosters = relationship("Rooster")
class FieldPosition(Base):
__tablename__ = "field_position"
__table_args__ = (
UniqueConstraint("name", "sport_id"),
UniqueConstraint("short_name", "sport_id"),
)
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(255), nullable=False, index=True)
short_name = Column(String(255), nullable=False)
sport_id = Column(String, ForeignKey("sport.id"), nullable=False, index=True)
sport = relationship("Sport", back_populates="positions")
roosters = relationship("Rooster")
class Player(Base):
__tablename__ = "player"
__table_args__ = (
UniqueConstraint("first_name", "last_name"),
)
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
first_name = Column(String(255), nullable=False, index=True)
last_name = Column(String(255), nullable=False, index=True)
roosters = relationship("Rooster")
def get_full_name(self) -> str:
return f"{self.last_name}, {self.first_name}"
class Rooster(Base):
__tablename__ = "rooster"
__table_args__ = (
UniqueConstraint("year", "team_id", "player_id", "position_id"),
)
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
year = Column(Integer)
team_id = Column(String, ForeignKey("team.id"), nullable=False, index=True)
team = relationship("Team", back_populates="roosters")
player_id = Column(String, ForeignKey("player.id"), nullable=False, index=True)
player = relationship("Player", back_populates="roosters")
position_id = Column(String, ForeignKey("field_position.id"), nullable=False, index=True)
position = relationship("roosters")
cards = relationship("Card")
class Vendor(Base):
__tablename__ = "vendor"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(255), nullable=False, unique=True, index=True)
card_sets = relationship("CardSet")
cards = relationship("Card")
class CardSet(Base):
__tablename__ = "card_set"
__table_args__ = (
UniqueConstraint("name", "vendor_id"),
)
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(255), index=True)
parallel_set = Column(BIT(1))
insert_set = Column(BIT(1))
vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False, index=True)
vendor = relationship("Vendor", back_populates="card_sets")
cards = relationship("Card")
class Card(Base):
__tablename__ = "card"
__table_args__ = (
UniqueConstraint("card_number", "year", "vendor_id", "card_set_id"),
)
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
card_number = Column(Integer, index=True)
year = Column(Integer, index=True)
card_set_id = Column(String, ForeignKey("card_set.id"), nullable=False)
card_set = relationship("cards")
rooster_id = Column(String, ForeignKey("rooster.id"), nullable=False)
rooster = relationship("cards")
vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False)
vendor = relationship("Vendor", back_populates="cards")
+42
View File
@@ -0,0 +1,42 @@
from PySide6.QtWidgets import QApplication
from cement import Controller, ex
from cement.utils.version import get_version_banner
from ..core.version import get_version
from ..gui.main_window import MainWindow
VERSION_BANNER = """
Kontor CLI Tool %s
%s
""" % (get_version(), get_version_banner())
class CliBase(Controller):
class Meta:
label = 'base'
# text displayed at the top of --help output
description = 'Kontor CLI Tool'
# text displayed at the bottom of --help output
epilog = 'Usage: kontor gui|database'
# controller level arguments. ex: 'kontor --version'
arguments = [
### add a version banner
(['-v', '--version'],
{'action': 'version',
'version': VERSION_BANNER}),
]
def _default(self):
"""Default action if no sub-command is passed."""
self.gui()
@ex(
help='start GUI'
)
def gui(self):
application = QApplication([])
window = MainWindow(self.app.session, self.app.log)
window.show()
application.exec()
+32
View File
@@ -0,0 +1,32 @@
from cement import Controller, ex
from ..database import KontorDB
class Database(Controller):
class Meta:
label = 'database'
stacked_type = 'nested'
stacked_on = 'base'
@ex(
help='export database to given file',
arguments=[
(['-f', '--file'],
{'help': 'file to store database content',
'action': 'store',
'dest': 'db_file'})
],
)
def export(self):
data = {
'db_file': 'data.json',
'export_type': 'JSON',
}
if self.app.pargs.db_file is not None:
data['db_file'] = self.app.pargs.db_file
kontor_db = KontorDB(self.app.session, self.app.log)
table_list = kontor_db.get_table_names()
kontor_db.export_db(data['export_type'], data['db_file'], table_list)
self.app.render(data, 'command1.jinja2')
@@ -2,39 +2,18 @@ import json
from datetime import datetime
from pathlib import Path
import mariadb
from sqlalchemy import create_engine, select, text, MetaData, join
from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker
from .base import Base
from .comic import Comic, Artist, Publisher, ComicWork, WorkType, StoryArc, Volume, Issue, TradePaperback
from .tysc import Sport, Team, Card, CardSet, Vendor, Rooster, Player, FieldPosition
from .media import MediaFile
from .comic import Comic, Artist, Publisher, Issue, StoryArc, TradePaperback, Volume, ComicWork, WorkType
from .metadata import MetaDataTable, MetaDataColumn
from .tysc import Card, CardSet, Sport, Team, FieldPosition, Rooster, Player, Vendor
from .media import MediaFile
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']
)
connect_string = ('mariadb+mariadbconnector://{}:{}@{}:{}/{}'
.format(
db_config['mariadb']['user'],
db_config['mariadb']['password'],
db_config['mariadb']['host'],
db_config['mariadb']['port'],
db_config['mariadb']['database']))
# engine = create_engine(connect_string, echo=True)
engine = create_engine(connect_string)
Base.metadata.create_all(bind=engine)
__session__ = sessionmaker(bind=engine)
self.session = __session__()
def __init__(self, db_session, log):
self.session = db_session
self.log = log
self.registry = {}
self.init_registry()
@@ -58,7 +37,6 @@ class KontorDB:
self.registry['worktype'] = WorkType
self.registry['media_file'] = MediaFile
def get_table_names(self) -> list:
tables = self.session.query(MetaDataTable).all()
result = [table.table_name for table in tables]
@@ -94,7 +72,7 @@ class KontorDB:
filter(MetaDataTable.table_name == table_name).
filter(MetaDataColumn.show_filter == 1).all()):
_filter_map[column.column_name] = {'label': column.filter_label, 'widget': None}
print(f"retrieved {len(_filter_map)} filters: {_filter_map}")
self.log.info(f"retrieved {len(_filter_map)} filters: {_filter_map}")
return _filter_map
def data(self, table, columns: dict, filters) -> list:
@@ -121,52 +99,8 @@ class KontorDB:
data.append(row)
return data
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)}")
if table_name == 'comic' and len(where_clause) == 0:
data.clear()
comics = self.session.query(Comic).all()
for item in comics:
# print(item)
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(item, ref_table)
value = getattr(ref, "name")
# print(f"{value=}")
row.append(value)
else:
row.append(getattr(item, column_name))
# print(repr(row))
data.append(row)
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
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}")
self.log.info(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)
@@ -177,8 +111,8 @@ class KontorDB:
continue
rows = self.session.query(model).all()
entries = []
print(f"found {len(rows)} entries")
print(f"found {len(columns)} columns")
self.log.debug(f"found {len(rows)} entries")
self.log.debug(f"found {len(columns)} columns")
for row in rows:
# print(row)
entry = {}
@@ -193,7 +127,7 @@ class KontorDB:
else:
entry[column_name] = value
except AttributeError as error:
print("could not get value")
self.log.debug("could not get value")
entries.append(entry)
db[table] = entries
export_file = Path(export_file_name)
@@ -207,6 +141,6 @@ class KontorDB:
case "SQLite":
export_file = Path(export_file_name)
case _:
print("unknown export type")
self.log.debug("unknown export type")
if export_file.exists():
print(f"{export_file} exists")
self.log.debug(f"{export_file} exists")
+5
View File
@@ -0,0 +1,5 @@
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
@@ -20,6 +20,7 @@ class MetaDataTable(Base):
def __str__(self):
return f'{self.table_name}({self.id})'
class MetaDataColumn(Base):
__tablename__ = 'meta_data_column'
id = Column(String, primary_key=True)
@@ -2,23 +2,24 @@ from PySide6.QtGui import QAction, QIcon
from PySide6.QtWidgets import QWidget, QVBoxLayout, QMenu, QMessageBox, QTabWidget, QTableView
from PySide6.QtWidgets import QLabel, QMainWindow
from database import KontorDB
from dialogs import ExportKontorDialog, ImportKontorDialog
from model_config import KontorModelConfig
from table_model import KontorTableModel
from media_file_model import MediaFileTableModel
from ..database import KontorDB
from ..database.media import MediaFile
from ..database.comic import Comic
from .dialogs import ExportKontorDialog, ImportKontorDialog
from .model_config import KontorModelConfig
from .table_model import KontorTableModel
class MainWindow(QMainWindow):
def __init__(self, config):
def __init__(self, session, log):
super().__init__()
self.tick = QIcon('res/tick.png')
self.cross = QIcon('res/cross.png')
self.import_icon = QIcon("res/application-import.png")
self.export_icon = QIcon("res/application-export.png")
self.circle_icon = QIcon("res/arrow-circle-double.png")
self.tick = QIcon('kontor/res/tick.png')
self.cross = QIcon('kontor/res/cross.png')
self.import_icon = QIcon("kontor/res/application-import.png")
self.export_icon = QIcon("kontor/res/application-export.png")
self.circle_icon = QIcon("kontor/res/arrow-circle-double.png")
self.setWindowTitle("Kontor")
self.setMinimumSize(800, 500)
@@ -29,7 +30,8 @@ class MainWindow(QMainWindow):
self.data = []
self.filter = {}
self.kontor_db = KontorDB(config)
self.kontor_db = KontorDB(session, log)
self.log = log
self.central_widget = QWidget()
parent_layout = QVBoxLayout()
self.central_widget.setLayout(parent_layout)
@@ -109,8 +111,8 @@ class MainWindow(QMainWindow):
def export_to_file(self):
export_dlg = ExportKontorDialog(self, self.kontor_db)
if export_dlg.exec():
print(export_dlg.get_tables_to_export())
print(f"export DB to {export_dlg.file_name}")
self.log.info(export_dlg.get_tables_to_export())
self.log.info(f"export DB to {export_dlg.file_name}")
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:
@@ -1,7 +1,7 @@
import mariadb
from PySide6.QtWidgets import QHBoxLayout, QCheckBox
from database import KontorDB
from ..database import KontorDB
class KontorModelConfig:
@@ -19,21 +19,6 @@ class KontorModelConfig:
self.header = self.kontor_db.get_column_meta_data(self._table_name)
self.filter = self.kontor_db.get_filters(self._table_name)
def get_filter(self) -> str:
filter_rule = ""
# print(self.filter["download"].isChecked())
for column, filter_info in self.filter.items():
# print(column, filter_info)
if filter_info['widget'].isChecked():
# print(column, filter_info, filter_rule, len(filter_rule))
if len(filter_rule) < 1:
filter_rule += "WHERE "
if len(filter_rule) > 8:
filter_rule += " AND "
filter_rule += f"{column} is true"
# print(f"{filter_rule=}")
return filter_rule
def filters(self) -> dict:
_filters = {}
# print(self.filter["download"].isChecked())
@@ -3,7 +3,7 @@ from datetime import datetime
from PySide6.QtCore import QAbstractTableModel, QModelIndex
from PySide6.QtGui import Qt
from gui.model_config import KontorModelConfig
from .model_config import KontorModelConfig
class KontorTableModel(QAbstractTableModel):
@@ -1,4 +1,3 @@
from cement import App, TestApp, init_defaults
from cement.core.exc import CaughtSignal
from sqlalchemy import create_engine
@@ -7,6 +6,7 @@ from sqlalchemy.orm import sessionmaker
from .core.exc import KontorError
from .database.base import Base
from .controllers.clibase import CliBase
from .controllers.database import Database
# configuration defaults
CONFIG = init_defaults('kontor', 'mariadb')
@@ -17,6 +17,7 @@ CONFIG['mariadb']['host'] = '127.0.0.1'
CONFIG['mariadb']['port'] = '3306'
CONFIG['mariadb']['database'] = 'kontor'
def extend_sqlalchemy(app):
app.log.info('extending kontor application with sqlalchemy')
connect_string = ('mariadb+mariadbconnector://{}:{}@{}:{}/{}'.format(
@@ -33,6 +34,11 @@ def extend_sqlalchemy(app):
app.extend('session', __session__())
def close_session(app):
app.log.info('close session')
app.session.close()
class Kontor(App):
"""Kontor primary application."""
@@ -66,14 +72,16 @@ class Kontor(App):
hooks = [
('post_setup', extend_sqlalchemy),
('pre_close', close_session),
]
# register handlers
handlers = [
CliBase
CliBase,
Database,
]
class KontorTest(TestApp,Kontor):
class KontorTest(TestApp, Kontor):
"""A sub-class of Kontor that is better suited for testing."""
class Meta:

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

Before

Width:  |  Height:  |  Size: 836 B

After

Width:  |  Height:  |  Size: 836 B

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

Before

Width:  |  Height:  |  Size: 634 B

After

Width:  |  Height:  |  Size: 634 B

-16
View File
@@ -1,16 +0,0 @@
# This is a sample Python script.
# Press Umschalt+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}') # Press Strg+F8 to toggle the breakpoint.
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
print_hi('PyCharm')
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
-2
View File
@@ -1,2 +0,0 @@
deployment/
kontor.bin
-18
View File
@@ -1,18 +0,0 @@
from sqlalchemy.orm import DeclarativeBase, relationship, sessionmaker, declarative_base
class Base(DeclarativeBase):
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)
-130
View File
@@ -1,130 +0,0 @@
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship
from .base import Base
class Publisher(Base):
__tablename__ = "publisher"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(length=255), unique=True)
comics = relationship("Comic")
def __repr__(self):
return f'Publisher({self.id} {self.name})'
def __str__(self):
return self.__repr__()
class Comic(Base):
__tablename__ = 'comic'
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
title = Column(String(length=255), unique=True)
publisher_id = Column(String, ForeignKey('publisher.id'), nullable=False)
publisher = relationship("Publisher", back_populates="comics")
current_order = Column(BIT(1))
completed = Column(BIT(1))
issues = relationship("Issue")
story_arcs = relationship("StoryArc")
trade_paperbacks = relationship("TradePaperback")
volumes = relationship("Volume")
comic_works = relationship("ComicWork")
def __repr__(self):
return f'Comic({self.id} {self.version} {self.title} {self.publisher.name})'
def __str__(self):
return f'{self.title}({self.id})'
class Volume(Base):
__tablename__ = "volume"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(length=255), nullable=False)
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="volumes")
issues = relationship("Issue")
class TradePaperback(Base):
__tablename__ = "trade_paperback"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(length=255), nullable=False)
issue_start = Column(Integer)
issue_end = Column(Integer)
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="trade_paperbacks")
class StoryArc(Base):
__tablename__ = "story_arc"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(length=255), nullable=False)
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="story_arcs")
class Issue(Base):
__tablename__ = "issue"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
issue_number = Column(String(255))
in_stock = Column(BIT(1))
is_read = Column(BIT(1))
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="issues")
volume_id = Column(String, ForeignKey("volume.id"), nullable=True)
volume = relationship("Volume", back_populates="issues")
class Artist(Base):
__tablename__ = "artist"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(length=255), nullable=False)
comic_works = relationship("ComicWork")
class WorkType(Base):
__tablename__ = "worktype"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
name = Column(String(length=255), nullable=False, unique=True)
comic_works = relationship("ComicWork")
class ComicWork(Base):
__tablename__ = "comic_work"
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="comic_works")
artist_id = Column(String, ForeignKey("artist.id"), nullable=False)
artist = relationship("Artist", back_populates="comic_works")
work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
work_type = relationship("WorkType", back_populates="comic_works")
-25
View File
@@ -1,25 +0,0 @@
from sqlalchemy import Column, DateTime, Integer, String
from sqlalchemy.dialects.mysql import BIT
from database.base import Base
class MediaFile(Base):
__tablename__ = 'media_file'
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
cloud_link = Column(String(255))
file_name = Column(String(255))
path = Column(String(255))
review = Column(BIT(1))
title = Column(String(255))
url = Column(String(255))
should_download = Column(BIT(1))
def __repr__(self):
return f'MediaFile({self.id} {self.title} {self.title})'
def __str__(self):
return f'{self.title}({self.id})'
-49
View File
@@ -1,49 +0,0 @@
from sqlalchemy import Column, String, ForeignKey, DateTime, Integer, Boolean
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship
from database import Base
class MetaDataTable(Base):
__tablename__ = 'meta_data_table'
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
table_name = Column(String(255), unique=True)
table_columns = relationship("MetaDataColumn")
def __repr__(self):
return f'MetaDataTable({self.id} {self.table_name})'
def __str__(self):
return f'{self.table_name}({self.id})'
class MetaDataColumn(Base):
__tablename__ = 'meta_data_column'
id = Column(String, primary_key=True)
created_date = Column(DateTime)
last_modified_date = Column(DateTime)
version = Column(Integer)
column_modifier = Column(String(255), nullable=True)
column_name = Column(String(255))
column_order = Column(Integer)
column_sync_name = Column(String(255))
column_type = Column(String(255))
table_id = Column(String, ForeignKey('meta_data_table.id'))
table = relationship("MetaDataTable", back_populates="table_columns")
column_label = Column(String(255))
filter_label = Column(String(255))
is_shown = Column(BIT(1))
show_filter = Column(BIT(1))
ref_column = Column(String, nullable=True)
def __repr__(self):
if self.column_name is None:
return f'MetaDataColumn({self.id} {self.table.table_name}.__)'
else:
return f'MetaDataColumn({self.id} {self.table.table_name}.{self.column_name})'
def __str__(self):
return f'{self.column_name}({self.id})'
-21
View File
@@ -1,21 +0,0 @@
"""
PyQT6 GUI for Kontor
"""
import sys
from pathlib import Path
from platformdirs import PlatformDirs
from PySide6.QtWidgets import QApplication
import yaml
from gui.main_window import MainWindow
if __name__ == '__main__':
app = QApplication(sys.argv)
dirs = PlatformDirs("kontor")
database_config = Path(dirs.user_config_dir, 'database-config.yaml')
with open(database_config, 'rt') as f:
db_config = yaml.safe_load(f.read())
window = MainWindow(db_config)
window.show()
app.exec()
-98
View File
@@ -1,98 +0,0 @@
[app]
# title of your application
title = kontor
# project directory. the general assumption is that project_dir is the parent directory
# of input_file
project_dir = /home/tpeetz/projects/kontor/qt
# source file path
input_file = /home/tpeetz/projects/kontor/qt/kontor.py
# directory where exec is stored
exec_directory = .
# path to .pyproject project file
project_file =
# application icon
icon = /usr/local/lib/python3.11/dist-packages/PySide6/scripts/deploy_lib/pyside_icon.jpg
[python]
# python path
python_path = /usr/bin/python3
# python packages to install
packages = Nuitka==2.4.8
# buildozer = for deploying Android application
android_packages = buildozer==1.5.0,cython==0.29.33
[qt]
# comma separated path to qml files required
# normally all the qml files required by the project are added automatically
qml_files =
# excluded qml plugin binaries
excluded_qml_plugins =
# qt modules used. comma separated
modules = Gui,DBus,Core,Widgets
# qt plugins used by the application
plugins = platformthemes,imageformats,platforms,generic,iconengines,egldeviceintegrations,styles,xcbglintegrations,platforms/darwin,platforminputcontexts,accessiblebridge
[android]
# path to pyside wheel
wheel_pyside =
# path to shiboken wheel
wheel_shiboken =
# plugins to be copied to libs folder of the packaged application. comma separated
plugins =
[nuitka]
# usage description for permissions requested by the app as found in the info.plist file
# of the app bundle
# eg = extra_args = --show-modules --follow-stdlib
macos.permissions =
# mode of using nuitka. accepts standalone or onefile. default is onefile.
mode = onefile
# (str) specify any extra nuitka arguments
extra_args = --quiet --noinclude-qt-translations
[buildozer]
# build mode
# possible options = [release, debug]
# release creates an aab, while debug creates an apk
mode = debug
# contrains path to pyside6 and shiboken6 recipe dir
recipe_dir =
# path to extra qt android jars to be loaded by the application
jars_dir =
# if empty uses default ndk path downloaded by buildozer
ndk_path =
# if empty uses default sdk path downloaded by buildozer
sdk_path =
# other libraries to be loaded. comma separated.
# loaded at app startup
local_libs =
# architecture of deployed platform
# possible values = ["aarch64", "armv7a", "i686", "x86_64"]
arch =
@@ -4,3 +4,4 @@ cement[yaml]
cement[colorlog]
mariadb
sqlalchemy
PySide6