add CRUD for WorkType

This commit is contained in:
Thomas Peetz
2025-05-13 00:42:41 +02:00
parent 06a48a03ac
commit 3537642df9
49 changed files with 514 additions and 215 deletions
+86
View File
@@ -0,0 +1,86 @@
from db.models.admin import (
Assignment,
Token,
Profile,
Permission,
MailAccount,
ModuleData,
Mail,
)
from db.models.bookshelf import (
ArticleAuthor,
BookAuthor,
BookshelfPublisher,
Article,
Book,
Author,
)
from db.models.comic import (
Issue,
StoryArc,
TradePaperback,
Volume,
ComicWork,
Artist,
Comic,
Publisher,
WorkType,
)
from db.models.media import (
MediaFile,
MediaActor,
MediaActorFile,
MediaArticle,
MediaVideo,
)
from db.models.metadata import MetaDataColumn, MetaDataTable
from db.models.tysc import (
Card,
CardSet,
Rooster,
Team,
FieldPosition,
Player,
Vendor,
Sport,
)
registry = {
Card.__tablename__: Card,
CardSet.__tablename__: CardSet,
Rooster.__tablename__: Rooster,
Team.__tablename__: Team,
FieldPosition.__tablename__: FieldPosition,
Player.__tablename__: Player,
Vendor.__tablename__: Vendor,
Sport.__tablename__: Sport,
Issue.__tablename__: Issue,
TradePaperback.__tablename__: TradePaperback,
StoryArc.__tablename__: StoryArc,
Volume.__tablename__: Volume,
ComicWork.__tablename__: ComicWork,
Artist.__tablename__: Artist,
Comic.__tablename__: Comic,
Publisher.__tablename__: Publisher,
WorkType.__tablename__: WorkType,
ArticleAuthor.__tablename__: ArticleAuthor,
BookAuthor.__tablename__: BookAuthor,
BookshelfPublisher.__tablename__: BookshelfPublisher,
Article.__tablename__: Article,
Book.__tablename__: Book,
Author.__tablename__: Author,
MediaFile.__tablename__: MediaFile,
MediaActor.__tablename__: MediaActor,
MediaActorFile.__tablename__: MediaActorFile,
MediaArticle.__tablename__: MediaArticle,
MediaVideo.__tablename__: MediaVideo,
MetaDataColumn.__tablename__: MetaDataColumn,
MetaDataTable.__tablename__: MetaDataTable,
Assignment.__tablename__: Assignment,
Token.__tablename__: Token,
Profile.__tablename__: Profile,
Permission.__tablename__: Permission,
ModuleData.__tablename__: ModuleData,
MailAccount.__tablename__: MailAccount,
Mail.__tablename__: Mail
}
@@ -3,16 +3,16 @@ from datetime import datetime
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.orm import relationship, mapped_column, Mapped
from .base import Base, BaseMixin
from db.models.base import Base, BaseMixin
class Profile(Base, BaseMixin):
__tablename__ = 'profile'
first_name = Column(String(255))
last_name = Column(String(255))
user_name = Column(String(255), nullable=False)
email = Column(String(255))
password = Column(String(255))
first_name = Column(String)
last_name = Column(String)
user_name = Column(String, nullable=False)
email = Column(String)
password = Column(String)
enabled = Column(Boolean)
assignments = relationship("Assignment")
tokens = relationship("Token")
@@ -30,11 +30,11 @@ class Profile(Base, BaseMixin):
class Token(Base, BaseMixin):
__tablename__ = "token"
token = Column(String(255), nullable=False, unique=True)
name = Column(String(255))
token = Column(String, nullable=False, unique=True)
name = Column(String)
last_used_date: Mapped[datetime] = mapped_column()
enabled = Column(Boolean)
profile_id = Column(String(255), ForeignKey("profile.id"), nullable=False)
profile_id = Column(String, ForeignKey("profile.id"), nullable=False)
profile = relationship("Profile", back_populates="tokens")
@@ -1,7 +1,7 @@
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from .base import Base, BaseMixin
from db.models.base import Base, BaseMixin
class Article(Base, BaseMixin):
@@ -1,12 +1,22 @@
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.orm import relationship
import uuid
from datetime import datetime
from typing import List, Optional
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean, func
from sqlalchemy.orm import relationship, Mapped, mapped_column
from .base import Base, BaseMixin
from db.models.base import Base, BaseMixin
class Publisher(Base, BaseMixin):
class Publisher(Base):
__tablename__ = "publisher"
id: Mapped[str] = mapped_column(primary_key=True, default=uuid.uuid4)
created_date: Mapped[datetime] = mapped_column(default=func.now())
last_modified_date: Mapped[datetime] = mapped_column(default=func.now())
version: Mapped[int] = mapped_column(default=0)
name = Column(String, unique=True)
parent_publisher_id: Mapped[Optional[str]] = mapped_column(ForeignKey('publisher.id'))
parent_publisher: Mapped[Optional['Publisher']] = relationship("Publisher", back_populates="imprints", remote_side=[id])
imprints: Mapped[List['Publisher']] = relationship('Publisher', back_populates="parent_publisher")
comics = relationship("Comic")
def __repr__(self):
@@ -23,6 +33,7 @@ class Comic(Base, BaseMixin):
publisher = relationship("Publisher", back_populates="comics")
current_order = Column(Boolean)
completed = Column(Boolean)
weblink = Column(String, nullable=True)
issues = relationship("Issue")
story_arcs = relationship("StoryArc")
trade_paperbacks = relationship("TradePaperback")
@@ -74,6 +85,7 @@ class Issue(Base, BaseMixin):
class Artist(Base, BaseMixin):
__tablename__ = "artist"
name = Column(String, nullable=False)
weblink = Column(String, nullable=True)
comic_works = relationship("ComicWork")
@@ -97,3 +109,4 @@ class ComicWork(Base, BaseMixin):
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")
@@ -6,16 +6,17 @@ from logging import Logger
from pathlib import Path
from typing import Any
from sqlalchemy import UUID, select
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import sessionmaker
from .tysc import Card, CardSet, Rooster, Team, FieldPosition, Player, Vendor, Sport
from .comic import Issue, TradePaperback, StoryArc, Volume, ComicWork, Artist, Comic, Publisher, WorkType
from .bookshelf import ArticleAuthor, BookAuthor, BookshelfPublisher, Article, Book, Author
from .admin import Mail, MailAccount, ModuleData, Permission, Profile, Token, Assignment
from .metadata import MetaDataTable, MetaDataColumn
from .media import MediaVideo, MediaArticle, MediaFile, MediaActor, MediaActorFile
from db.models import registry
from db.models.tysc import Card, CardSet, Rooster, Team, FieldPosition, Player, Vendor, Sport
from db.models.comic import Issue, TradePaperback, StoryArc, Volume, ComicWork, Artist, Comic, Publisher, WorkType
from db.models.bookshelf import ArticleAuthor, BookAuthor, BookshelfPublisher, Article, Book, Author
from db.models.admin import Mail, MailAccount, ModuleData, Permission, Profile, Token, Assignment
from db.models.metadata import MetaDataTable, MetaDataColumn
from db.models.media import MediaVideo, MediaArticle, MediaFile, MediaActor, MediaActorFile
class ColumnEntry(Enum):
@@ -46,57 +47,8 @@ class KontorDB:
def __init__(self, db_engine: Any, log: Logger):
self.engine = db_engine
self.registry = {}
self.init_registry()
self.log = log
def init_registry(self):
self.registry[Card.__tablename__] = Card
self.registry[CardSet.__tablename__] = CardSet
self.registry[Rooster.__tablename__] = Rooster
self.registry[Team.__tablename__] = Team
self.registry[FieldPosition.__tablename__] = FieldPosition
self.registry[Player.__tablename__] = Player
self.registry[Vendor.__tablename__] = Vendor
self.registry[Sport.__tablename__] = Sport
self.registry[Issue.__tablename__] = Issue
self.registry[TradePaperback.__tablename__] = TradePaperback
self.registry[StoryArc.__tablename__] = StoryArc
self.registry[Volume.__tablename__] = Volume
self.registry[ComicWork.__tablename__] = ComicWork
self.registry[Artist.__tablename__] = Artist
self.registry[Comic.__tablename__] = Comic
self.registry[Publisher.__tablename__] = Publisher
self.registry[WorkType.__tablename__] = WorkType
self.registry[ArticleAuthor.__tablename__] = ArticleAuthor
self.registry[BookAuthor.__tablename__] = BookAuthor
self.registry[BookshelfPublisher.__tablename__] = BookshelfPublisher
self.registry[Article.__tablename__] = Article
self.registry[Book.__tablename__] = Book
self.registry[Author.__tablename__] = Author
self.registry[MediaFile.__tablename__] = MediaFile
self.registry[MediaActor.__tablename__] = MediaActor
self.registry[MediaActorFile.__tablename__] = MediaActorFile
self.registry[MediaArticle.__tablename__] = MediaArticle
self.registry[MediaVideo.__tablename__] = MediaVideo
self.registry[MetaDataColumn.__tablename__] = MetaDataColumn
self.registry[MetaDataTable.__tablename__] = MetaDataTable
self.registry[Assignment.__tablename__] = Assignment
self.registry[Token.__tablename__] = Token
self.registry[Profile.__tablename__] = Profile
self.registry[Permission.__tablename__] = Permission
self.registry[ModuleData.__tablename__] = ModuleData
self.registry[MailAccount.__tablename__] = MailAccount
self.registry[Mail.__tablename__] = Mail
def get_table_names(self) -> list:
result = []
__session__ = sessionmaker(self.engine)
with __session__() as session:
tables = session.scalars(select(MetaDataTable)).all()
result = [table.table_name for table in tables]
return result
def get_table_by_name(self, table_name: str) -> dict:
result = {}
__session__ = sessionmaker(self.engine)
@@ -130,19 +82,6 @@ class KontorDB:
order += 1
return meta_data
def get_columns(self, table_name: str) -> dict:
columns = {}
__session__ = sessionmaker(self.engine)
table_info = self.get_table_by_name(table_name)
_filters = {'table_id': table_info['id']}
with __session__() as session:
for column in session.query(MetaDataColumn).filter_by(**_filters).all():
columns[column.column_name] = {
ColumnEntry.COLUMN_ORDER: column.column_order,
ColumnEntry.COLUMN_TYPE: column.column_type
}
return columns
def get_filters(self, table_name: str) -> dict:
_filter_map = {}
__session__ = sessionmaker(self.engine)
@@ -159,7 +98,7 @@ class KontorDB:
def data(self, table_name: str, columns: dict, filters: dict) -> list:
data = []
__session__ = sessionmaker(self.engine)
table = self.registry[table_name]
table = registry[table_name]
with __session__() as session:
entries = []
if len(filters) == 0:
@@ -363,7 +302,7 @@ class KontorDB:
update_list[link.id] = link.title
return update_list
def get_download_list(self) -> list[UUID]:
def get_download_list(self) -> list[str]:
download_list = []
__session__ = sessionmaker(self.engine)
_filter = {'should_download': True}
@@ -8,7 +8,7 @@ from bs4 import BeautifulSoup
from sqlalchemy import Boolean, Column, False_, String, ForeignKey
from sqlalchemy.orm import relationship
from .base import Base, BaseMixin, BaseVideoMixin
from db.models.base import Base, BaseMixin, BaseVideoMixin
class MediaFile(Base, BaseMixin, BaseVideoMixin):
@@ -1,7 +1,7 @@
from sqlalchemy import Column, String, ForeignKey, Integer, Boolean
from sqlalchemy.orm import relationship
from .base import Base, BaseMixin
from db.models.base import Base, BaseMixin
class MetaDataTable(Base, BaseMixin):
@@ -1,7 +1,7 @@
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Boolean
from sqlalchemy.orm import relationship
from .base import Base, BaseMixin
from db.models.base import Base, BaseMixin
class Sport(Base, BaseMixin):
+28
View File
@@ -0,0 +1,28 @@
from typing import List
from sqlalchemy.orm import Session
from db.models.metadata import MetaDataColumn, MetaDataTable
from db.schemas.metadata import MetaDataTableResponse, MetaDataColumnResponse
def get_tables(db: Session) -> List[MetaDataTableResponse]:
tables = db.query(MetaDataTable).all()
results: List[MetaDataTableResponse] = [MetaDataTableResponse(id=table.id, name=table.table_name) for table in tables]
return results
def get_columns_for_table(db: Session, table: MetaDataTableResponse)-> List[MetaDataColumnResponse]:
columns = db.query(MetaDataColumn).filter_by(table_id = table.id).all()
results: List[MetaDataColumnResponse] = []
for column in columns:
result: MetaDataColumnResponse = MetaDataColumnResponse(
id=str(column.id),
name=column.column_name,
label=column.column_label,
order=column.column_order,
ref_column=column.ref_column,
column_type=column.column_type)
results.append(result)
return results
+15
View File
@@ -0,0 +1,15 @@
from pydantic import BaseModel, PositiveInt
class MetaDataTableResponse(BaseModel):
id: str
name: str
class MetaDataColumnResponse(BaseModel):
id: str
name: str
label: str
order: PositiveInt
ref_column: str | None
column_type: str
+2 -2
View File
@@ -80,7 +80,7 @@ def is_file_downloaded(media_file: dict, dir: Path) -> FileStatus:
def update_status(item_id: UUID, file_info: dict):
update = requests.put(f"http://127.0.0.1:8800/media/files/{item_id}", json=file_info)
update = requests.put(f"http://127.0.0.1:8800/api/media/files/{item_id}", json=file_info)
log.info(f"update status: {update.status_code}")
log.info(f"update result: {update.json()}")
@@ -97,7 +97,7 @@ def rename_file(file_info: dict):
if __name__ == '__main__':
log = get_logger(args.verbose, args.config)
log.info('kontor.download started')
response = requests.get("http://127.0.0.1:8800/media/files?download=true")
response = requests.get("http://127.0.0.1:8800/api/media/files?download=true")
log.info(f"Status: {response.status_code}")
data = response.json()
log.info(f"data: {len(data)}")
+35 -7
View File
@@ -2,17 +2,17 @@
import data from json file to MariaDB
"""
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime
import json
import yaml
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from platformdirs import PlatformDirs
from pathlib import Path
from schema.base import Base
from schema.database import KontorDB
from db.models import registry
from db.models.base import Base
from config import get_logger
from schema.database import ExportType
from db.repository.metadata import get_tables, get_columns_for_table
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('--verbose', '-v', action='count', default=0)
@@ -38,6 +38,34 @@ if __name__ == '__main__':
engine = create_engine(connect_string)
Base.metadata.create_all(bind=engine, checkfirst=True)
__session__ = sessionmaker(bind=engine)
kontor_db = KontorDB(engine, logger)
kontor_db.export_db(ExportType.JSON, args.file)
with __session__() as db:
data = {}
tables = get_tables(db)
for table in tables:
# logger.info(f"Table {table.name} with {table.id}")
columns = get_columns_for_table(db, table)
model = registry[table.name]
rows = db.query(model).all()
entries = []
for row in rows:
entry = {}
for column in columns:
# logger.info(f" Column {column.order} {column.name} with {column.id}")
try:
value = getattr(row, column.name)
if isinstance(value, datetime):
entry[column.name] = str(value)
else:
entry[column.name] = value
except AttributeError as error:
logger.info(f"{error}")
entries.append(entry)
data[table.name] = entries
logger.info(f"{table.name}: {len(entries)} exported")
json_dump = json.dumps(data, indent=4)
with open(args.file, "w") as dump_file:
dump_file.write(json_dump)
logger.info(f"{len(data)} tables exported")
#kontor_db = KontorDB(engine, logger)
#kontor_db.export_db(ExportType.JSON, args.file)
logger.info('kontor.export finished')
-1
View File
@@ -7,7 +7,6 @@ from typing import Dict, List
from config import get_logger, get_database_cursors
import json
import psycopg2
from psycopg2.sql import SQL
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
+4 -4
View File
@@ -37,7 +37,7 @@ def get_logger(level: int, config: str):
if __name__ == '__main__':
log = get_logger(args.verbose, args.config)
log.info('kontor.update_titles started')
response = requests.get("http://127.0.0.1:8800/media/files?review=true")
response = requests.get("http://127.0.0.1:8800/api/media/files?review=true")
log.info(f"Status: {response.status_code}")
data = response.json()
log.info(f"data: {len(data)}")
@@ -49,11 +49,11 @@ if __name__ == '__main__':
soup = BeautifulSoup(r.content, "html.parser")
title = soup.title.string
item['title'] = title
item['review'] = 0
item['review'] = False
except:
item['title'] = None
item['review'] = 1
update = requests.put(f"http://127.0.0.1:8800/media/files/{item['id']}", json=item)
item['review'] = True
update = requests.put(f"http://127.0.0.1:8800/api/media/files/{item['id']}", json=item)
log.info(f"update status: {update.status_code}")
log.info(f"update result: {update.json()}")
log.info('kontor.update_titles finished')