Misc changes:
- Rename BookcaseLocation -> BookcaseShelf - Remove `name` from BookcaseShelfs, and use columns and rows to index them instead - Add `amount` to BookcaseItem, and make isbn unique - Remove ability to use scanner tool without database - Add a lot of real bookshelfs to seeding data
This commit is contained in:
parent
98eed07417
commit
4e868c3324
|
@ -32,7 +32,7 @@ def configure_admin(app):
|
|||
admin.add_view(ModelView(Author, db.session))
|
||||
admin.add_view(ModelView(Bookcase, db.session))
|
||||
admin.add_view(ModelView(BookcaseItem, db.session))
|
||||
admin.add_view(ModelView(BookcaseLocation, db.session))
|
||||
admin.add_view(ModelView(BookcaseShelf, db.session))
|
||||
admin.add_view(ModelView(Category, db.session))
|
||||
admin.add_view(ModelView(Language, db.session))
|
||||
admin.add_view(ModelView(MediaType, db.session))
|
|
@ -14,12 +14,12 @@ from .mixins import (
|
|||
UniqueNameMixin,
|
||||
)
|
||||
if TYPE_CHECKING:
|
||||
from .BookcaseLocation import BookcaseLocation
|
||||
from .BookcaseShelf import BookcaseShelf
|
||||
|
||||
class Bookcase(Base, UidMixin, UniqueNameMixin):
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
|
||||
locations: Mapped[list[BookcaseLocation]] = relationship(back_populates='bookcase')
|
||||
shelfs: Mapped[list[BookcaseShelf]] = relationship(back_populates='bookcase')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
|
|||
|
||||
from sqlalchemy import (
|
||||
Integer,
|
||||
SmallInteger,
|
||||
String,
|
||||
ForeignKey,
|
||||
)
|
||||
|
@ -23,23 +24,22 @@ from .xref_tables import (
|
|||
)
|
||||
if TYPE_CHECKING:
|
||||
from .Author import Author
|
||||
from .BookcaseLocation import BookcaseLocation
|
||||
from .BookcaseShelf import BookcaseShelf
|
||||
from .Category import Category
|
||||
from .Language import Language
|
||||
from .MediaType import MediaType
|
||||
|
||||
class BookcaseItem(Base, UidMixin, UniqueNameMixin):
|
||||
# NOTE: This is kept non-unique in case we have
|
||||
# multiple copies of the same book.
|
||||
isbn: Mapped[int | None] = mapped_column(String, index=True)
|
||||
isbn: Mapped[int] = mapped_column(String, unique=True, index=True)
|
||||
owner: Mapped[str] = mapped_column(String, default='PVV')
|
||||
amount: Mapped[int] = mapped_column(SmallInteger, default=1)
|
||||
|
||||
fk_media_type_uid: Mapped[int] = mapped_column(Integer, ForeignKey('MediaType.uid'))
|
||||
fk_bookcase_location_uid: Mapped[int | None] = mapped_column(Integer, ForeignKey('BookcaseLocation.uid'))
|
||||
fk_bookcase_shelf_uid: Mapped[int | None] = mapped_column(Integer, ForeignKey('BookcaseShelf.uid'))
|
||||
fk_language_uid: Mapped[int] = mapped_column(Integer, ForeignKey('Language.uid'))
|
||||
|
||||
media_type: Mapped[MediaType] = relationship(back_populates='items')
|
||||
location: Mapped[BookcaseLocation] = relationship(back_populates='items')
|
||||
shelf: Mapped[BookcaseShelf] = relationship(back_populates='items')
|
||||
language: Mapped[Language] = relationship()
|
||||
|
||||
categories: Mapped[set[Category]] = relationship(
|
||||
|
|
|
@ -4,7 +4,9 @@ from typing import TYPE_CHECKING
|
|||
from sqlalchemy import (
|
||||
Integer,
|
||||
ForeignKey,
|
||||
SmallInteger,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import (
|
||||
Mapped,
|
||||
|
@ -13,28 +15,39 @@ from sqlalchemy.orm import (
|
|||
)
|
||||
|
||||
from .Base import Base
|
||||
from .mixins import (
|
||||
UidMixin,
|
||||
UniqueNameMixin,
|
||||
)
|
||||
from .mixins import UidMixin
|
||||
if TYPE_CHECKING:
|
||||
from .Bookcase import Bookcase
|
||||
from .BookcaseItem import BookcaseItem
|
||||
|
||||
class BookcaseLocation(Base, UidMixin, UniqueNameMixin):
|
||||
# NOTE: Booshelfs are 0 indexed for both rows and columns,
|
||||
# where cell 0-0 is placed in the lower right corner.
|
||||
|
||||
class BookcaseShelf(Base, UidMixin):
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
'column',
|
||||
'fk_bookcase_uid',
|
||||
'row',
|
||||
),
|
||||
)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
row: Mapped[int] = mapped_column(SmallInteger)
|
||||
column: Mapped[int] = mapped_column(SmallInteger)
|
||||
|
||||
fk_bookcase_uid: Mapped[int] = mapped_column(Integer, ForeignKey('Bookcase.uid'))
|
||||
|
||||
bookcase: Mapped[Bookcase] = relationship(back_populates='locations')
|
||||
items: Mapped[set[BookcaseItem]] = relationship(back_populates='location')
|
||||
bookcase: Mapped[Bookcase] = relationship(back_populates='shelfs')
|
||||
items: Mapped[set[BookcaseItem]] = relationship(back_populates='shelf')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
row: int,
|
||||
column: int,
|
||||
bookcase: Bookcase,
|
||||
description: str | None = None,
|
||||
):
|
||||
self.name = name
|
||||
self.row = row
|
||||
self.column = column
|
||||
self.bookcase = bookcase
|
||||
self.description = description
|
|
@ -12,7 +12,7 @@ from .Base import Base
|
|||
from .mixins import UidMixin, UniqueNameMixin
|
||||
|
||||
class Language(Base, UidMixin, UniqueNameMixin):
|
||||
iso639_1_code: Mapped[str] = mapped_column(String(2))
|
||||
iso639_1_code: Mapped[str] = mapped_column(String(2), unique=True, index=True)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -2,7 +2,7 @@ from .Author import Author
|
|||
from .Base import Base
|
||||
from .Bookcase import Bookcase
|
||||
from .BookcaseItem import BookcaseItem
|
||||
from .BookcaseLocation import BookcaseLocation
|
||||
from .BookcaseShelf import BookcaseShelf
|
||||
from .Category import Category
|
||||
from .Language import Language
|
||||
from .MediaType import MediaType
|
|
@ -5,7 +5,7 @@ from flask_sqlalchemy import SQLAlchemy
|
|||
|
||||
from .models import (
|
||||
Bookcase,
|
||||
BookcaseLocation,
|
||||
BookcaseShelf,
|
||||
Language,
|
||||
MediaType,
|
||||
)
|
||||
|
@ -19,15 +19,100 @@ def seed_data(db: SQLAlchemy):
|
|||
]
|
||||
|
||||
bookcases = [
|
||||
Bookcase(name='A', description='The first bookcase'),
|
||||
Bookcase(name='B', description='The second bookcase'),
|
||||
Bookcase(name='Unnamed A', description='White case across dibbler'),
|
||||
Bookcase(name='Unnamed B', description='Math case in the working room'),
|
||||
Bookcase(name='Unnamed C', description='Large case in the working room'),
|
||||
Bookcase(name='Unnamed D', description='White comics case in the hallway'),
|
||||
Bookcase(name='Unnamed E', description='Wooden comics case in the hallway'),
|
||||
]
|
||||
|
||||
locations = [
|
||||
BookcaseLocation(name='1-1', description='The first location', bookcase=bookcases[0]),
|
||||
BookcaseLocation(name='1-2', description='The second location', bookcase=bookcases[0]),
|
||||
BookcaseLocation(name='2-1', description='The third location', bookcase=bookcases[1]),
|
||||
BookcaseLocation(name='2-2', description='The fourth location', bookcase=bookcases[1]),
|
||||
shelfs = [
|
||||
BookcaseShelf(row=0, column=0, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=1, column=0, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=2, column=0, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=3, column=0, bookcase=bookcases[0], description="Hacking"),
|
||||
BookcaseShelf(row=4, column=0, bookcase=bookcases[0], description="Hacking"),
|
||||
|
||||
BookcaseShelf(row=0, column=1, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=1, column=1, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=2, column=1, bookcase=bookcases[0], description="DOS"),
|
||||
BookcaseShelf(row=3, column=1, bookcase=bookcases[0], description="Food for thought"),
|
||||
BookcaseShelf(row=4, column=1, bookcase=bookcases[0], description="CPP"),
|
||||
|
||||
BookcaseShelf(row=0, column=2, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=1, column=2, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=2, column=2, bookcase=bookcases[0], description="E = mc2"),
|
||||
BookcaseShelf(row=3, column=2, bookcase=bookcases[0], description="OBJECTION!"),
|
||||
BookcaseShelf(row=4, column=2, bookcase=bookcases[0], description="/home"),
|
||||
|
||||
BookcaseShelf(row=0, column=3, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=1, column=3, bookcase=bookcases[0], description="Big indonisian island"),
|
||||
BookcaseShelf(row=2, column=3, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=3, column=3, bookcase=bookcases[0], description="Div science"),
|
||||
BookcaseShelf(row=4, column=3, bookcase=bookcases[0], description="/home"),
|
||||
|
||||
BookcaseShelf(row=0, column=4, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=1, column=4, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=2, column=4, bookcase=bookcases[0], description="(not) computer vision"),
|
||||
BookcaseShelf(row=3, column=4, bookcase=bookcases[0], description="Low voltage"),
|
||||
BookcaseShelf(row=4, column=4, bookcase=bookcases[0], description="/home"),
|
||||
|
||||
BookcaseShelf(row=0, column=5, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=1, column=5, bookcase=bookcases[0]),
|
||||
BookcaseShelf(row=2, column=5, bookcase=bookcases[0], description="/home"),
|
||||
BookcaseShelf(row=3, column=5, bookcase=bookcases[0], description="/home"),
|
||||
|
||||
BookcaseShelf(row=0, column=0, bookcase=bookcases[1]),
|
||||
BookcaseShelf(row=1, column=0, bookcase=bookcases[1], description="Kjellerarealer og komodovaraner"),
|
||||
BookcaseShelf(row=2, column=0, bookcase=bookcases[1]),
|
||||
BookcaseShelf(row=3, column=0, bookcase=bookcases[1], description="Quick mafs"),
|
||||
BookcaseShelf(row=4, column=0, bookcase=bookcases[1]),
|
||||
|
||||
BookcaseShelf(row=0, column=0, bookcase=bookcases[2]),
|
||||
BookcaseShelf(row=1, column=0, bookcase=bookcases[2]),
|
||||
BookcaseShelf(row=2, column=0, bookcase=bookcases[2], description="AI"),
|
||||
BookcaseShelf(row=3, column=0, bookcase=bookcases[2], description="X86"),
|
||||
BookcaseShelf(row=4, column=0, bookcase=bookcases[2], description="Humanoira"),
|
||||
BookcaseShelf(row=5, column=0, bookcase=bookcases[2], description="Hvem monterte rørforsterker?"),
|
||||
|
||||
BookcaseShelf(row=0, column=1, bookcase=bookcases[2]),
|
||||
BookcaseShelf(row=1, column=1, bookcase=bookcases[2], description="Div data"),
|
||||
BookcaseShelf(row=2, column=1, bookcase=bookcases[2], description="Chemistry"),
|
||||
BookcaseShelf(row=3, column=1, bookcase=bookcases[2], description="Soviet Phys. Techn. Phys"),
|
||||
BookcaseShelf(row=4, column=1, bookcase=bookcases[2], description="Digitalteknikk"),
|
||||
BookcaseShelf(row=5, column=1, bookcase=bookcases[2], description="Material"),
|
||||
|
||||
BookcaseShelf(row=0, column=2, bookcase=bookcases[2]),
|
||||
BookcaseShelf(row=1, column=2, bookcase=bookcases[2], description="Assembler / APL"),
|
||||
BookcaseShelf(row=2, column=2, bookcase=bookcases[2], description="Internet"),
|
||||
BookcaseShelf(row=3, column=2, bookcase=bookcases[2], description="Algorithms"),
|
||||
BookcaseShelf(row=4, column=2, bookcase=bookcases[2], description="Soviet Physics Jetp"),
|
||||
BookcaseShelf(row=5, column=2, bookcase=bookcases[2], description="Død og pine"),
|
||||
|
||||
BookcaseShelf(row=0, column=3, bookcase=bookcases[2]),
|
||||
BookcaseShelf(row=1, column=3, bookcase=bookcases[2], description="Web"),
|
||||
BookcaseShelf(row=2, column=3, bookcase=bookcases[2], description="Div languages"),
|
||||
BookcaseShelf(row=3, column=3, bookcase=bookcases[2], description="Python"),
|
||||
BookcaseShelf(row=4, column=3, bookcase=bookcases[2], description="D&D Minis"),
|
||||
BookcaseShelf(row=5, column=3, bookcase=bookcases[2], description="Perl"),
|
||||
|
||||
BookcaseShelf(row=0, column=4, bookcase=bookcases[2]),
|
||||
BookcaseShelf(row=1, column=4, bookcase=bookcases[2], description="Knuth on programming"),
|
||||
BookcaseShelf(row=2, column=4, bookcase=bookcases[2], description="Div languages"),
|
||||
BookcaseShelf(row=3, column=4, bookcase=bookcases[2], description="Typesetting"),
|
||||
BookcaseShelf(row=4, column=4, bookcase=bookcases[2]),
|
||||
|
||||
BookcaseShelf(row=0, column=0, bookcase=bookcases[3]),
|
||||
BookcaseShelf(row=0, column=1, bookcase=bookcases[3]),
|
||||
BookcaseShelf(row=0, column=2, bookcase=bookcases[3]),
|
||||
BookcaseShelf(row=0, column=3, bookcase=bookcases[3]),
|
||||
BookcaseShelf(row=0, column=4, bookcase=bookcases[3]),
|
||||
|
||||
BookcaseShelf(row=0, column=0, bookcase=bookcases[4]),
|
||||
BookcaseShelf(row=0, column=1, bookcase=bookcases[4]),
|
||||
BookcaseShelf(row=0, column=2, bookcase=bookcases[4]),
|
||||
BookcaseShelf(row=0, column=3, bookcase=bookcases[4]),
|
||||
BookcaseShelf(row=0, column=4, bookcase=bookcases[4], description="Religion"),
|
||||
]
|
||||
|
||||
with open(Path(__file__).parent.parent / 'data' / 'iso639_1.csv') as f:
|
||||
|
@ -36,7 +121,7 @@ def seed_data(db: SQLAlchemy):
|
|||
|
||||
db.session.add_all(media_types)
|
||||
db.session.add_all(bookcases)
|
||||
db.session.add_all(locations)
|
||||
db.session.add_all(shelfs)
|
||||
db.session.add_all(languages)
|
||||
db.session.commit()
|
||||
print("Added test media types, bookcases and locations.")
|
||||
print("Added test media types, bookcases and shelfs.")
|
|
@ -0,0 +1,45 @@
|
|||
import isbnlib
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import (
|
||||
Author,
|
||||
BookcaseItem,
|
||||
Language,
|
||||
)
|
||||
|
||||
def is_valid_pvv_isbn(isbn: str) -> bool:
|
||||
try:
|
||||
int(isbn)
|
||||
except ValueError:
|
||||
return False
|
||||
return len(isbn) == 8
|
||||
|
||||
def is_valid_isbn(isbn: str) -> bool:
|
||||
return any([
|
||||
isbnlib.is_isbn10(isbn),
|
||||
isbnlib.is_isbn13(isbn),
|
||||
])
|
||||
|
||||
def create_bookcase_item_from_isbn(isbn: str, sql_session: Session) -> BookcaseItem | None:
|
||||
metadata = isbnlib.meta(isbn, 'openl')
|
||||
if len(metadata.keys()) == 0:
|
||||
return None
|
||||
|
||||
bookcase_item = BookcaseItem(
|
||||
name = metadata.get('Title'),
|
||||
isbn = int(isbn.replace('-', '')),
|
||||
)
|
||||
|
||||
if len(authors := metadata.get('Authors')) > 0:
|
||||
for author in authors:
|
||||
bookcase_item.authors.add(Author(author))
|
||||
|
||||
if (language := metadata.get('Language')):
|
||||
bookcase_item.language = sql_session.scalars(
|
||||
select(Language)
|
||||
.where(Language.iso639_1_code == language)
|
||||
).one()
|
||||
|
||||
return bookcase_item
|
|
@ -1,31 +0,0 @@
|
|||
import isbnlib
|
||||
|
||||
from ..models import (
|
||||
Author,
|
||||
BookcaseItem,
|
||||
)
|
||||
|
||||
def is_valid_isbn(isbn: str) -> bool:
|
||||
return any([
|
||||
isbnlib.is_isbn10(isbn),
|
||||
isbnlib.is_isbn13(isbn),
|
||||
])
|
||||
|
||||
def create_bookcase_item_from_isbn(isbn: str) -> BookcaseItem | None:
|
||||
metadata = isbnlib.meta(isbn)
|
||||
if len(metadata.keys()) == 0:
|
||||
return None
|
||||
|
||||
bookcase_item = BookcaseItem(
|
||||
name = metadata.get('Title'),
|
||||
isbn = int(isbn.replace('-', '')),
|
||||
)
|
||||
|
||||
if len(authors := metadata.get('Authors')) > 0:
|
||||
for author in authors:
|
||||
bookcase_item.authors.add(Author(author))
|
||||
|
||||
if (language := metadata.get('language')):
|
||||
bookcase_item.language = language
|
||||
|
||||
return bookcase_item
|
|
@ -1,5 +1,6 @@
|
|||
from cmd import Cmd
|
||||
from typing import Any, Set
|
||||
from typing import Any
|
||||
from textwrap import dedent
|
||||
|
||||
from sqlalchemy import (
|
||||
create_engine,
|
||||
|
@ -9,7 +10,7 @@ from sqlalchemy.orm import (
|
|||
Session,
|
||||
)
|
||||
|
||||
from worblehat.services.item import (
|
||||
from worblehat.services.bookcase_item import (
|
||||
create_bookcase_item_from_isbn,
|
||||
is_valid_isbn,
|
||||
)
|
||||
|
@ -43,13 +44,11 @@ class _InteractiveItemSelector(Cmd):
|
|||
self,
|
||||
cls: type,
|
||||
sql_session: Session,
|
||||
items: Set[Any],
|
||||
default: Any | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.cls = cls
|
||||
self.sql_session = sql_session
|
||||
self.items = items
|
||||
self.default_item = default
|
||||
if default is not None:
|
||||
self.prompt = f'Select {cls.__name__} [{default.name}]> '
|
||||
|
@ -61,13 +60,10 @@ class _InteractiveItemSelector(Cmd):
|
|||
self.result = self.default_item
|
||||
return True
|
||||
|
||||
if self.sql_session is not None:
|
||||
result = self.sql_session.scalars(
|
||||
select(self.cls)
|
||||
.where(self.cls.name == arg),
|
||||
).all()
|
||||
else:
|
||||
result = [x for x in self.items if x.name == arg]
|
||||
|
||||
if len(result) != 1:
|
||||
print(f'No such {self.cls.__name__} found: {arg}')
|
||||
|
@ -77,13 +73,10 @@ class _InteractiveItemSelector(Cmd):
|
|||
return True
|
||||
|
||||
def completenames(self, text: str, *_) -> list[str]:
|
||||
if self.sql_session is not None:
|
||||
return self.sql_session.scalars(
|
||||
select(self.cls.name)
|
||||
.where(self.cls.name.like(f'{text}%'))
|
||||
.where(self.cls.name.like(f'{text}%')),
|
||||
).all()
|
||||
else:
|
||||
return [x for x in self.items if x.name.startswith(text)]
|
||||
|
||||
|
||||
class BookScanTool(Cmd):
|
||||
|
@ -94,13 +87,6 @@ Start by entering a command, or entering an ISBN of a new item.
|
|||
Type "help" to see list of commands
|
||||
"""
|
||||
|
||||
sql_session = None
|
||||
|
||||
bookcases: set[Bookcase] = set()
|
||||
bookcase_locations: set[BookcaseLocation] = set()
|
||||
bookcase_items: set[BookcaseItem] = set()
|
||||
media_types: set[MediaType] = set()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
@ -108,32 +94,39 @@ Type "help" to see list of commands
|
|||
engine = create_engine(Config.SQLALCHEMY_DATABASE_URI)
|
||||
self.sql_session = Session(engine)
|
||||
except Exception:
|
||||
print('Warning: could not connect to database. Saving to database has been disabled.')
|
||||
return
|
||||
print('Error: could not connect to database.')
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
self.bookcases = set(self.sql_session.scalars(select(Bookcase)).all())
|
||||
self.bookcase_locations = set(self.sql_session.scalars(select(BookcaseLocation)).all())
|
||||
self.bookcase_items = set(self.sql_session.scalars(select(BookcaseItem)).all())
|
||||
self.media_types = set(self.sql_session.scalars(select(MediaType)).all())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print('Warning: could not prefill data from sql database. Saving to database has been disabled.')
|
||||
self.sql_session.close()
|
||||
self.sql_session = None
|
||||
return
|
||||
|
||||
print('Note: Successfully connected to database')
|
||||
print(f"Connected to database at '{Config.SQLALCHEMY_DATABASE_URI}'")
|
||||
|
||||
|
||||
def do_list_bookcases(self, arg: str):
|
||||
self.columnize([repr(bc) for bc in self.bookcases])
|
||||
def do_list_bookcases(self, _: str):
|
||||
"""Usage: list_bookcases"""
|
||||
bookcase_shelfs = self.sql_session.scalars(
|
||||
select(BookcaseShelf)
|
||||
.join(Bookcase)
|
||||
.order_by(
|
||||
Bookcase.name,
|
||||
BookcaseShelf.column,
|
||||
BookcaseShelf.row,
|
||||
)
|
||||
).all()
|
||||
|
||||
bookcase_uid = None
|
||||
for shelf in bookcase_shelfs:
|
||||
if shelf.bookcase.uid != bookcase_uid:
|
||||
print(shelf.bookcase.name)
|
||||
bookcase_uid = shelf.bookcase.uid
|
||||
|
||||
name = f"r{shelf.row}-c{shelf.column}"
|
||||
if shelf.description is not None:
|
||||
name += f" [{shelf.description}]"
|
||||
|
||||
print(f' {name} - {sum(i.amount for i in shelf.items)} items')
|
||||
|
||||
|
||||
def do_add_bookcase(self, arg: str):
|
||||
"""
|
||||
Usage: add_bookcase <name> [description]
|
||||
"""
|
||||
"""Usage: add_bookcase <name> [description]"""
|
||||
arg = arg.split(' ')
|
||||
if len(arg) < 1:
|
||||
print('Usage: add_bookcase <name> [description]')
|
||||
|
@ -142,82 +135,105 @@ Type "help" to see list of commands
|
|||
name = arg[0]
|
||||
description = ' '.join(arg[1:])
|
||||
|
||||
if any([bc.name == name for bc in self.bookcases]):
|
||||
if self.sql_session.scalars(
|
||||
select(Bookcase)
|
||||
.where(Bookcase.name == name)
|
||||
).one_or_none() is not None:
|
||||
print(f'Error: a bookcase with name {name} already exists')
|
||||
return
|
||||
|
||||
bookcase = Bookcase(name, description)
|
||||
self.bookcases.add(bookcase)
|
||||
if self.sql_session is not None:
|
||||
self.sql_session.add(bookcase)
|
||||
|
||||
|
||||
def do_list_bookcase_locations(self, arg: str):
|
||||
self.columnize([repr(bl) for bl in self.bookcase_locations])
|
||||
|
||||
|
||||
def do_add_bookcase_location(self, arg: str):
|
||||
"""
|
||||
Usage: add_bookcase_location <bookcase_name> <name> [description]
|
||||
"""
|
||||
def do_add_bookcase_shelf(self, arg: str):
|
||||
"""Usage: add_bookcase_shelf <bookcase_name> <row>-<column> [description]"""
|
||||
arg = arg.split(' ')
|
||||
if len(arg) < 2:
|
||||
print('Usage: add_bookcase_location <bookcase_name> <name> [description]')
|
||||
print('Usage: add_bookcase_shelf <bookcase_name> <row>-<column> [description]')
|
||||
return
|
||||
|
||||
bookcase_name = arg[0]
|
||||
name = arg[1]
|
||||
row, column = [int(x) for x in arg[1].split('-')]
|
||||
description = ' '.join(arg[2:])
|
||||
|
||||
bookcases_with_name = [bc for bc in self.bookcases if bc.name == bookcase_name]
|
||||
if not len(bookcases_with_name) == 1:
|
||||
if (bookcase := self.sql_session.scalars(
|
||||
select(Bookcase)
|
||||
.where(Bookcase.name == bookcase_name)
|
||||
).one_or_none()) is None:
|
||||
print(f'Error: Could not find bookcase with name {bookcase_name}')
|
||||
return
|
||||
|
||||
if any([bc.name == name for bc in self.bookcase_locations]):
|
||||
print(f'Error: a bookcase with name {name} already exists')
|
||||
if self.sql_session.scalars(
|
||||
select(BookcaseShelf)
|
||||
.where(
|
||||
BookcaseShelf.bookcase == bookcase,
|
||||
BookcaseShelf.row == row,
|
||||
BookcaseShelf.column == column,
|
||||
)
|
||||
).one_or_none() is not None:
|
||||
print(f'Error: a bookshelf in bookcase {bookcase.name} with position {row}-{column} already exists')
|
||||
return
|
||||
|
||||
location = BookcaseLocation(
|
||||
name,
|
||||
bookcases_with_name[0],
|
||||
shelf = BookcaseShelf(
|
||||
row,
|
||||
column,
|
||||
bookcase,
|
||||
description,
|
||||
)
|
||||
self.bookcase_locations.add(location)
|
||||
if self.sql_session is not None:
|
||||
self.sql_session.add(location)
|
||||
self.sql_session.add(shelf)
|
||||
|
||||
|
||||
def do_list_bookcase_items(self, arg: str):
|
||||
def do_list_bookcase_items(self, _: str):
|
||||
"""Usage: list_bookcase_items"""
|
||||
self.columnize([repr(bi) for bi in self.bookcase_items])
|
||||
|
||||
|
||||
def default(self, arg: str):
|
||||
if not is_valid_isbn(arg):
|
||||
print(f'"{arg}" is not a valid isbn')
|
||||
def default(self, isbn: str):
|
||||
isbn = isbn.strip()
|
||||
if not is_valid_isbn(isbn):
|
||||
print(f'"{isbn}" is not a valid isbn')
|
||||
return
|
||||
|
||||
bookcase_item = create_bookcase_item_from_isbn(arg)
|
||||
if bookcase_item == None:
|
||||
print(f'Could not find data about item with isbn {arg} online.')
|
||||
print(f'If you think this is not due to a bug, please add the book to openlibrary.org before continuing.')
|
||||
if (existing_item := self.sql_session.scalars(
|
||||
select(BookcaseItem)
|
||||
.where(BookcaseItem.isbn == isbn)
|
||||
).one_or_none()) is not None:
|
||||
print('Found existing BookcaseItem:', existing_item)
|
||||
print(f'There are currently {existing_item.amount} of these in the system.')
|
||||
if _prompt_yes_no('Would you like to add another?', default=True):
|
||||
existing_item.amount += 1
|
||||
return
|
||||
|
||||
print('Please select the location where the item is placed:')
|
||||
bookcase_location_selector = _InteractiveItemSelector(
|
||||
cls = BookcaseLocation,
|
||||
bookcase_item = create_bookcase_item_from_isbn(isbn, self.sql_session)
|
||||
if bookcase_item is None:
|
||||
print(f'Could not find data about item with isbn {isbn} online.')
|
||||
print(f'If you think this is not due to a bug, please add the book to openlibrary.org before continuing.')
|
||||
return
|
||||
else:
|
||||
print(dedent(f"""
|
||||
Found item:
|
||||
title: {bookcase_item.name}
|
||||
authors: {', '.join(a.name for a in bookcase_item.authors)}
|
||||
language: {bookcase_item.language}
|
||||
"""))
|
||||
|
||||
print('Please select the shelf where the item is placed:')
|
||||
bookcase_shelf_selector = _InteractiveItemSelector(
|
||||
cls = BookcaseShelf,
|
||||
sql_session = self.sql_session,
|
||||
items = self.bookcase_locations,
|
||||
)
|
||||
|
||||
bookcase_location_selector.cmdloop()
|
||||
bookcase_item.location = bookcase_location_selector.result
|
||||
bookcase_shelf_selector.cmdloop()
|
||||
bookcase_item.shelf = bookcase_shelf_selector.result
|
||||
|
||||
print('Please select the items media type:')
|
||||
media_type_selector = _InteractiveItemSelector(
|
||||
cls = MediaType,
|
||||
sql_session = self.sql_session,
|
||||
items = self.media_types,
|
||||
default = next(mt for mt in self.media_types if mt.name.lower() == 'book'),
|
||||
default = self.sql_session.scalars(
|
||||
select(MediaType)
|
||||
.where(MediaType.name.ilike("book")),
|
||||
).one(),
|
||||
)
|
||||
|
||||
media_type_selector.cmdloop()
|
||||
|
@ -227,14 +243,16 @@ Type "help" to see list of commands
|
|||
if username != '':
|
||||
bookcase_item.owner = username
|
||||
|
||||
self.bookcase_items.add(bookcase_item)
|
||||
if self.sql_session is not None:
|
||||
self.sql_session.add(bookcase_item)
|
||||
|
||||
|
||||
def do_exit(self, arg: str):
|
||||
# TODO: take internally stored data, and save it, either in csv, json or database
|
||||
raise NotImplementedError()
|
||||
def do_exit(self, _: str):
|
||||
"""Usage: exit"""
|
||||
if _prompt_yes_no('Would you like to save your changes?'):
|
||||
self.sql_session.commit()
|
||||
else:
|
||||
self.sql_session.rollback()
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
Loading…
Reference in New Issue