diff --git a/python/kontor/controllers/clibase.py b/python/kontor/controllers/clibase.py index d467413..0491128 100644 --- a/python/kontor/controllers/clibase.py +++ b/python/kontor/controllers/clibase.py @@ -40,6 +40,6 @@ class CliBase(Controller): ) def gui(self): application = QApplication([]) - window = MainWindow(self.app.session, self.app.engine, self.app.log) + window = MainWindow(self.app.engine, self.app.config, self.app.log) window.show() application.exec() diff --git a/python/kontor/controllers/database.py b/python/kontor/controllers/database.py index b0be6eb..04fe2fb 100644 --- a/python/kontor/controllers/database.py +++ b/python/kontor/controllers/database.py @@ -26,7 +26,7 @@ class Database(Controller): } 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.engine, self.app.log) + kontor_db = KontorDB(self.app.engine, self.app.log) kontor_db.export_db(data['export_type'], data['db_file']) self.app.render(data, 'command1.jinja2') @@ -47,6 +47,6 @@ class Database(Controller): } 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.engine, self.app.log) + kontor_db = KontorDB(self.app.engine, self.app.log) self.app.render(data, 'import.jinja2') kontor_db.import_db(data['db_file'], self.app.pargs.dry_run) diff --git a/python/kontor/controllers/media.py b/python/kontor/controllers/media.py index 2335b72..e39c724 100644 --- a/python/kontor/controllers/media.py +++ b/python/kontor/controllers/media.py @@ -15,11 +15,8 @@ class Media(Controller): help='update title for mediafiles', ) def update_title(self): - if self.app.pargs.dry_run: - print('print command to shell') - self.app.render({}, 'update.jinja2') - - + kontor_db = KontorDB(self.app.engine, self.app.config, self.app.log) + kontor_db.update_title(self.app.pargs.dry_run) @ex( label='download', @@ -37,9 +34,8 @@ class Media(Controller): } if self.app.pargs.media_dir is not None: data['media_dir'] = self.app.pargs.media_dir - if self.app.pargs.dry_run: - print('print command to shell') - self.app.render(data, 'download.jinja2') + kontor_db = KontorDB(self.app.engine, self.app.config, self.app.log) + kontor_db.download_file(self.app.pargs.dry_run) @ex( help='add url to database', @@ -58,7 +54,7 @@ class Media(Controller): data['link_url'] = self.app.pargs.link if self.app.pargs.dry_run: print(f"add url {data['link_url']} to database") - kontor_db = KontorDB(self.app.session, self.app.engine, self.app.log) + kontor_db = KontorDB(self.app.engine, self.app.config, self.app.log) kontor_db.add_link(self.app.pargs.link, self.app.pargs.dry_run) else: diff --git a/python/kontor/database/__init__.py b/python/kontor/database/__init__.py index 5feec11..0c524fa 100644 --- a/python/kontor/database/__init__.py +++ b/python/kontor/database/__init__.py @@ -1,8 +1,15 @@ import json +import re +import subprocess import uuid from datetime import datetime from pathlib import Path +from typing import Any +import requests +from bs4 import BeautifulSoup +from cement.core.config import ConfigHandler +from sqlalchemy import Engine from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import sessionmaker @@ -16,9 +23,9 @@ from .media import MediaFile, MediaArticle, MediaVideo class KontorDB: - def __init__(self, db_session, db_engine, log): - self.session = db_session + def __init__(self, db_engine: Engine, config: ConfigHandler, log): self.engine = db_engine + self.config = config self.log = log self.registry = {} self.init_registry() @@ -50,37 +57,41 @@ class KontorDB: self.registry['media_file'] = MediaFile self.registry['media_article'] = MediaArticle self.registry['media_video'] = MediaVideo + self.registry['meta_data_table'] = MetaDataTable + self.registry[MetaDataColumn.__tablename__] = MetaDataColumn def get_table_names(self) -> list: result = [] __session__ = sessionmaker(self.engine) with __session__() as session: - tables = self.session.query(MetaDataTable).all() + tables = 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 + __session__ = sessionmaker(self.engine) + with __session__() as session: + if view_only: + for (_, column) in (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 (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 = {} @@ -193,20 +204,23 @@ class KontorDB: def import_table(self, table_name, items, dry_run: bool): existing_ids = self.get_ids(table_name) for item in items: - self.log.debug(f"{item}") + # self.log.debug(f"{item}") current_id = item['id'] - found_item = self.session.query(self.registry[table_name]).get(current_id) - self.log.debug(f"found: {found_item}") - if found_item is not None: - changed = self.update_entry(found_item, item, dry_run) - if changed: - print(f"{current_id} has changed") - existing_ids.remove(current_id) - else: - self.log.info("item to import not found in database, add new one...") + found_item = None + __session__ = sessionmaker(self.engine) + with __session__() as session: + found_item = session.query(self.registry[table_name]).get(current_id) + self.log.debug(f"found: {found_item}") + if found_item is not None: + changed = self.update_entry(found_item, item, dry_run) + if changed: + print(f"{current_id} has changed") + existing_ids.remove(current_id) + else: + self.log.info("item to import not found in database, add new one...") + self.add_entry(table_name, item, session, dry_run) if len(existing_ids) > 0: print("remaining items") - self.session.commit() def get_ids(self, table_name: str) -> list: existing_ids = [] @@ -217,14 +231,15 @@ class KontorDB: existing_ids.append(getattr(item, 'id')) return existing_ids - def add_entry(self, update_item: dict): - media_file = MediaFile() - __session__ = sessionmaker(self.engine) - with __session__() as session: - for key in update_item.keys(): - update_value = update_item[key] - setattr(media_file, key, update_value) - session.add(media_file) + def add_entry(self, table_name: str, update_item: dict, session, dry_run: bool): + add_item = self.registry[table_name]() + for key in update_item.keys(): + update_value = update_item[key] + setattr(add_item, key, update_value) + if dry_run: + self.log.info(f"add item {type(add_item)} with id {update_item['id']}") + else: + session.add(add_item) session.commit() def update_entry(self, existing_item, update_item: dict, dry_run: bool) -> bool: @@ -233,7 +248,7 @@ class KontorDB: update_value = update_item[key] existing_value = getattr(existing_item, key) if type(existing_value) is not type(update_value): - self.log.debug(f"compare {type(existing_value)} with {type(update_value)}") + # self.log.debug(f"compare {type(existing_value)} with {type(update_value)}") existing_value = str(existing_value) if existing_value != update_value: print(f"{key} has changed: {existing_value} != {update_value}") @@ -261,5 +276,81 @@ class KontorDB: session.commit() self.log.info(f"entry {media_file} successfully added") except IntegrityError as error: - self.session.rollback() + session.rollback() self.log.info(error.orig) + + def update_title(self, dry_run=False): + self.log.info(f"get links to review of media_file") + __session__ = sessionmaker(self.engine) + with __session__() as session: + links = session.query(MediaFile).filter(MediaFile.review == 1).all() + self.log.info(f"try to update {len(links)} items") + for link in links: + url = link.url + if url is None: + self.log.info(f"url has not been set for {link.id}") + continue + self.log.info('get title for url {}'.format(url)) + if dry_run: + continue + try: + r = requests.get(url) + soup = BeautifulSoup(r.content, "html.parser") + title = soup.title.string + except: + self.log.info("Sorry, could not retrieve title") + continue + self.log.info('ID {} has title {}'.format(link.id, title)) + link.title = title + link.review = 0 + session.commit() + + def download_file(self, dry_run=False): + self.log.info(f"download marked files of media_file") + __session__ = sessionmaker(self.engine) + with __session__() as session: + links = session.query(MediaFile).filter(MediaFile.should_download == 1).all() + self.log.info(f"try to download {len(links)} items") + for link in links: + url = link.url + if url is None: + self.log.info(f"url has not been set for {link.id}") + continue + if dry_run: + self.log.info(f"download {link.url} to {self.config.get('media', 'dir')}") + continue + filename = self.download_url(link) + if filename is None: + link.file_name = filename + link.should_download = 0 + session.commit() + + def parse_output(self, lines_list): + file_name = "" + for line in lines_list: + if 'has already been downloaded' in line: + end_len = len(' has already been downloaded') + file_name = line[11:-end_len] + self.log.info('found file: "%s"', file_name) + if 'Destination' in line: + line_len = len(line) + start_len = len('[download] Destination: ') + file_len = line_len - start_len + file_name = line[-file_len:] + self.log.info('new file: "%s"', file_name) + return file_name + + def download_url(self, video_url): + media_dir = Path(self.config.get('media', 'dir')) + if not media_dir.exists(): + media_dir = Path().absolute() + self.log.info(f"download video to {media_dir}") + result = subprocess.run([self.config.get('media', 'yt-dlp'), video_url], cwd=media_dir, capture_output=True, + text=True) + if result.returncode == 0: + output = result.stdout + output = re.sub(' +', ' ', output) + lines_list = output.splitlines() + return self.parse_output(lines_list) + else: + return None diff --git a/python/kontor/database/base.py b/python/kontor/database/base.py index fa2b68a..c976167 100644 --- a/python/kontor/database/base.py +++ b/python/kontor/database/base.py @@ -1,5 +1,20 @@ -from sqlalchemy.orm import DeclarativeBase +import uuid +from datetime import datetime + +from sqlalchemy import Integer, func, String +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column class Base(DeclarativeBase): pass + + +class BaseMixin: + # id = Column(String, primary_key=True) + id: Mapped[str] = mapped_column(primary_key=True, default=uuid.uuid4()) + # created_date = Column(DateTime) + created_date: Mapped[datetime] = mapped_column(default=func.now()) + # last_modified_date = Column(DateTime) + last_modified_date: Mapped[datetime] = mapped_column(default=func.now()) + # version = Column(Integer) + version: Mapped[int] = mapped_column(default=0) diff --git a/python/kontor/database/bookshelf.py b/python/kontor/database/bookshelf.py index 0e09fd0..ab0fe5a 100644 --- a/python/kontor/database/bookshelf.py +++ b/python/kontor/database/bookshelf.py @@ -2,47 +2,31 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, String from sqlalchemy.dialects.mysql import BIT from sqlalchemy.orm import relationship -from .base import Base +from .base import Base, BaseMixin -class Article(Base): +class Article(Base, BaseMixin): __tablename__ = 'article' - 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) article_authors = relationship("ArticleAuthor") -class Author(Base): +class Author(Base, BaseMixin): __tablename__ = 'author' - id = Column(String, primary_key=True) - created_date = Column(DateTime) - last_modified_date = Column(DateTime) - version = Column(Integer) first_name = Column(String(255)) last_name = Column(String(255)) article_authors = relationship("ArticleAuthor") book_authors = relationship("BookAuthor") -class BookshelfPublisher(Base): +class BookshelfPublisher(Base, BaseMixin): __tablename__ = 'bookshelf_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) books = relationship("Book") -class Book(Base): +class Book(Base, BaseMixin): __tablename__ = 'book' - id = Column(String, primary_key=True) - created_date = Column(DateTime) - last_modified_date = Column(DateTime) - version = Column(Integer) isbn = Column(String(255), unique=True) title = Column(String(255)) year = Column(Integer, nullable=False) @@ -51,24 +35,16 @@ class Book(Base): book_authors = relationship("BookAuthor") -class ArticleAuthor(Base): +class ArticleAuthor(Base, BaseMixin): __tablename__ = 'article_author' - id = Column(String, primary_key=True) - created_date = Column(DateTime) - last_modified_date = Column(DateTime) - version = Column(Integer) article_id = Column(String, ForeignKey('article.id'), nullable=False) article = relationship('Article', back_populates="article_authors") author_id = Column(String, ForeignKey('author.id'), nullable=False) author = relationship('Author', back_populates="article_authors") -class BookAuthor(Base): +class BookAuthor(Base, BaseMixin): __tablename__ = 'book_author' - id = Column(String, primary_key=True) - created_date = Column(DateTime) - last_modified_date = Column(DateTime) - version = Column(Integer) author_id = Column(String, ForeignKey('author.id'), nullable=False) author = relationship('Author', back_populates="book_authors") book_id = Column(String, ForeignKey('book.id'), nullable=False) diff --git a/python/kontor/database/comic.py b/python/kontor/database/comic.py index 49d222f..fe6ec19 100644 --- a/python/kontor/database/comic.py +++ b/python/kontor/database/comic.py @@ -2,15 +2,11 @@ from sqlalchemy import Column, DateTime, ForeignKey, Integer, String from sqlalchemy.dialects.mysql import BIT from sqlalchemy.orm import relationship -from .base import Base +from .base import Base, BaseMixin -class Publisher(Base): +class Publisher(Base, BaseMixin): __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") @@ -21,12 +17,8 @@ class Publisher(Base): return self.__repr__() -class Comic(Base): +class Comic(Base, BaseMixin): __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") @@ -45,24 +37,16 @@ class Comic(Base): return f'{self.title}({self.id})' -class Volume(Base): +class Volume(Base, BaseMixin): __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): +class TradePaperback(Base, BaseMixin): __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) @@ -70,23 +54,15 @@ class TradePaperback(Base): comic = relationship("Comic", back_populates="trade_paperbacks") -class StoryArc(Base): +class StoryArc(Base, BaseMixin): __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): +class Issue(Base, BaseMixin): __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)) @@ -96,32 +72,26 @@ class Issue(Base): volume = relationship("Volume", back_populates="issues") -class Artist(Base): +class Artist(Base, BaseMixin): __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): +class WorkType(Base, BaseMixin): __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") + def __repr__(self): + return f'Worktype({self.id} {self.version} {self.name} {len(self.comic_works)})' -class ComicWork(Base): + def __str__(self): + return f'{self.name}({self.id})' + + +class ComicWork(Base, BaseMixin): __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) diff --git a/python/kontor/database/media.py b/python/kontor/database/media.py index 85e6155..d9d1bf2 100644 --- a/python/kontor/database/media.py +++ b/python/kontor/database/media.py @@ -1,15 +1,11 @@ from sqlalchemy import Column, DateTime, Integer, String from sqlalchemy.dialects.mysql import BIT -from .base import Base +from .base import Base, BaseMixin -class MediaFile(Base): +class MediaFile(Base, BaseMixin): __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)) @@ -25,23 +21,15 @@ class MediaFile(Base): return f'{self.title}({self.id})' -class MediaArticle(Base): +class MediaArticle(Base, BaseMixin): __tablename__ = 'media_article' - id = Column(String, primary_key=True) - created_date = Column(DateTime) - last_modified_date = Column(DateTime) - version = Column(Integer) review = Column(BIT(1)) title = Column(String(255)) url = Column(String(255), unique=True) -class MediaVideo(Base): +class MediaVideo(Base, BaseMixin): __tablename__ = 'media_video' - 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)) diff --git a/python/kontor/database/metadata.py b/python/kontor/database/metadata.py index d7dd4c0..21d4d49 100644 --- a/python/kontor/database/metadata.py +++ b/python/kontor/database/metadata.py @@ -2,15 +2,11 @@ from sqlalchemy import Column, String, ForeignKey, DateTime, Integer, Boolean from sqlalchemy.dialects.mysql import BIT from sqlalchemy.orm import relationship -from .base import Base +from .base import Base, BaseMixin -class MetaDataTable(Base): +class MetaDataTable(Base, BaseMixin): __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") @@ -21,12 +17,8 @@ class MetaDataTable(Base): return f'{self.table_name}({self.id})' -class MetaDataColumn(Base): +class MetaDataColumn(Base, BaseMixin): __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) diff --git a/python/kontor/database/tysc.py b/python/kontor/database/tysc.py index 733d7ed..ef8bc5d 100644 --- a/python/kontor/database/tysc.py +++ b/python/kontor/database/tysc.py @@ -2,29 +2,21 @@ from sqlalchemy import Column, DateTime, Integer, String, ForeignKey, UniqueCons from sqlalchemy.dialects.mysql import BIT from sqlalchemy.orm import relationship -from .base import Base +from .base import Base, BaseMixin -class Sport(Base): +class Sport(Base, BaseMixin): __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): +class Team(Base, BaseMixin): __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) @@ -32,16 +24,12 @@ class Team(Base): roosters = relationship("Rooster") -class FieldPosition(Base): +class FieldPosition(Base, BaseMixin): __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) @@ -49,15 +37,11 @@ class FieldPosition(Base): roosters = relationship("Rooster") -class Player(Base): +class Player(Base, BaseMixin): __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") @@ -66,15 +50,11 @@ class Player(Base): return f"{self.last_name}, {self.first_name}" -class Rooster(Base): +class Rooster(Base, BaseMixin): __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") @@ -84,26 +64,19 @@ class Rooster(Base): position = relationship("FieldPosition", back_populates="roosters") cards = relationship("Card") -class Vendor(Base): + +class Vendor(Base, BaseMixin): __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): +class CardSet(Base, BaseMixin): __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)) @@ -112,15 +85,11 @@ class CardSet(Base): cards = relationship("Card") -class Card(Base): +class Card(Base, BaseMixin): __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) diff --git a/python/kontor/gui/main_window.py b/python/kontor/gui/main_window.py index 6e775bc..eaba732 100644 --- a/python/kontor/gui/main_window.py +++ b/python/kontor/gui/main_window.py @@ -1,6 +1,10 @@ +from typing import Any + from PySide6.QtGui import QAction, QIcon from PySide6.QtWidgets import QWidget, QVBoxLayout, QMenu, QMessageBox, QTabWidget, QTableView from PySide6.QtWidgets import QLabel, QMainWindow +from cement.core.config import ConfigHandler +from sqlalchemy import Engine from ..database import KontorDB from ..database.media import MediaFile @@ -12,7 +16,7 @@ from .table_model import KontorTableModel class MainWindow(QMainWindow): - def __init__(self, session, engine, log): + def __init__(self, engine: Engine, config: ConfigHandler, log): super().__init__() self.tick = QIcon('kontor/res/tick.png') @@ -30,7 +34,7 @@ class MainWindow(QMainWindow): self.data = [] self.filter = {} - self.kontor_db = KontorDB(session, engine, log) + self.kontor_db = KontorDB(engine, config, log) self.log = log self.central_widget = QWidget() parent_layout = QVBoxLayout() @@ -55,7 +59,9 @@ class MainWindow(QMainWindow): self.refreshAction = QAction(self.circle_icon, "&Refresh", self) self.refreshAction.triggered.connect(self.refresh) self.updateTitleAction = QAction("&Update Titles", self) + self.updateTitleAction.triggered.connect(self.update_title) self.downloadAction = QAction("&Download Videos", self) + self.downloadAction.triggered.connect(self.download_file) self.exitAction = QAction("&Beenden", self) self.exitAction.setShortcut("Alt+F4") self.exitAction.triggered.connect(self.close) @@ -118,6 +124,16 @@ class MainWindow(QMainWindow): else: self.statusBar.showMessage("Export cancelled", 3000) + def update_title(self): + self.log.info("update title for table MediaFile") + self.statusBar.showMessage("update title for table MediaFile", 3000) + self.kontor_db.update_title() + + def download_file(self): + self.log.info("download videos for table MediaFile") + self.statusBar.showMessage("download videos for table MediaFile", 3000) + self.kontor_db.download_file() + def refresh(self): self.data[self.tabs.currentIndex()].refresh() diff --git a/python/kontor/main.py b/python/kontor/main.py index 9460b56..c0a3631 100644 --- a/python/kontor/main.py +++ b/python/kontor/main.py @@ -10,17 +10,20 @@ from .controllers.clibase import CliBase from .controllers.database import Database # configuration defaults -CONFIG = init_defaults('kontor', 'mariadb','output.json', 'output.yaml') +CONFIG = init_defaults('kontor', 'mariadb', 'media') CONFIG['kontor']['foo'] = 'bar' CONFIG['mariadb']['user'] = 'kontor' CONFIG['mariadb']['password'] = 'kontor' CONFIG['mariadb']['host'] = '127.0.0.1' CONFIG['mariadb']['port'] = '3306' CONFIG['mariadb']['database'] = 'kontor' +CONFIG['media']['ytdlp'] = '/home/tpeetz/bin/yt-dlp' +CONFIG['media']['dir'] = '/data/media' META = init_defaults('output.json', 'output.yaml') META['output.json']['overridable'] = True META['output.yaml']['overridable'] = True + def extend_sqlalchemy(app): app.log.info('extending kontor application with sqlalchemy') connect_string = ('mariadb+mariadbconnector://{}:{}@{}:{}/{}'.format( @@ -34,15 +37,9 @@ def extend_sqlalchemy(app): engine = create_engine(connect_string) Base.metadata.create_all(bind=engine) __session__ = sessionmaker(bind=engine) - app.extend('session', __session__()) app.extend('engine', engine) -def close_session(app): - app.log.info('close session') - app.session.close() - - class Kontor(App): """Kontor primary application.""" @@ -79,7 +76,6 @@ class Kontor(App): hooks = [ ('post_setup', extend_sqlalchemy), - ('pre_close', close_session), ] # register handlers handlers = [ diff --git a/python/requirements.txt b/python/requirements.txt index 06a15f9..f6e0136 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -5,3 +5,4 @@ cement[colorlog] mariadb sqlalchemy PySide6 +beautifulsoup4