"""Pyshelf's Main Storage Class.""" import re import os from sqlalchemy import create_engine, select from sqlalchemy.orm import Session from .models import Book, Collection class Storage: """Contains all methods for system storage.""" def __init__(self, config): """Initialize storage object.""" self.config = config self.sql = self.config.catalogue_db self.user = self.config.user self.password = self.config.password self.db_host = self.config.db_host self.db_port = self.config.db_port self.engine = create_engine(self.get_connection_string(), pool_pre_ping=True) def get_connection_string(self): """Get connection string. Engine type references config.json:DB_ENGINE. """ if self.config.db_engine == "sqlite": if os.path.exists(f"{self.config.root}/pyshelf.db"): return f"sqlite:////{self.config.root}/pyshelf.db" else: sqlite_file = open(f'{self.config.root}/pyshelf.db', 'w') sqlite_file.close() return f"sqlite://{self.config.root}/pyshelf.db" elif self.config.db_engine == "psql": return f"postgresql://{self.user}:{self.password}\ @{self.db_host}:{self.db_port}/{self.sql}" elif self.config.db_engine == "mysql": return f"mysql://{self.user}:{self.password}\ @{self.db_host}:{self.db_port}/{self.sql}" def create_tables(self): """Create table structure.""" tables = [Book, Collection] for table in tables: table.metadata.create_all(self.engine) def insert_book(self, book): """Insert a new book into the database. :returns: True if succeeds False if not """ with Session(self.engine) as session: try: try: cover_image = book[2].data except Exception: cover_image = book[2] if not book[2]: # If cover image is missing unset entry cover_image = None if not book[1]: pass _book = Book( title=book[0], author=book[1], cover=cover_image, file_name=book[3], description=book[4], identifier=book[5], publisher=book[6], rights=book[8], tags=book[9], ) session.add(_book) session.commit() session.close() self.config.logger.info(book[0][0:80]) return True except Exception as e: self.config.logger.error(f"{book[0][0:80]} :: {e}") def book_paths_list(self): """Get file paths from database for comparison to system files.""" session = Session(self.engine) _result = session.scalars(select(Book.file_name)).fetchall() session.close() return _result def make_collections(self): """Make collections.""" # TODO: Check this still works with the switch to sqlalchemy self.config.logger.info("Making collections.") _title_regx = re.compile(r"^[0-9][0-9]*|-|\ \B") session = Session(self.engine) _set = session.execute(select(Book.book_id, Book.file_name)).all() if _set.__len__() > 0: for book in _set: path = self.config.book_path + "/" _collections = [] _pathing = book[1].split(path)[1].split("/") try: _pathing.pop(0) _pathing.pop(-1) except IndexError: continue for _p in _pathing: _s = _p.replace("'", "") _x = re.sub(_title_regx, "", _s) _s = _x.strip() _sess = Session(self.engine) _q = _sess.execute( select(Collection.collection_id).where( Collection.collection == _s, Collection.book_id == book.book_id, ) ) _sess.close() if _q.fetchone() is None: _collection = Collection( collection=_s, book_id=book.book_id) with Session(self.engine) as _sess: try: _sess.add(_collection) _sess.commit() _sess.close() self.config.logger.info( f"Collection {_s} added.") except Exception as e: self.config.logger.error( f"Collection {_s} failed: {e}") _collections.append(_p) self.config.logger.info("Finished making collections.")