Misc improvements:
- Extract the bookshelf selector into its own file, since it was a little bit complicated with completion and everything. - Print out bookcase item information on every prompt in the bookcase item specific subclis - Add funcs to EditBookcaseCli - Add shelf editing to EditBookcaseCli - Ensure shorthand column-row pairs are always written in that order
This commit is contained in:
parent
cd666377f8
commit
9b96875346
|
@ -8,7 +8,6 @@ from sqlalchemy import (
|
|||
from sqlalchemy.orm import (
|
||||
Session,
|
||||
)
|
||||
from worblehat.cli.subclis.bookcase_item import BookcaseItemCli
|
||||
|
||||
from worblehat.services.bookcase_item import (
|
||||
create_bookcase_item_from_isbn,
|
||||
|
@ -20,6 +19,9 @@ from .prompt_utils import *
|
|||
from worblehat.config import Config
|
||||
from worblehat.models import *
|
||||
|
||||
from .subclis.bookcase_item import BookcaseItemCli
|
||||
from .subclis.bookcase_shelf_selector import select_bookcase_shelf
|
||||
|
||||
# TODO: Category seems to have been forgotten. Maybe relevant interactivity should be added?
|
||||
# However, is there anyone who are going to search by category rather than just look in
|
||||
# the shelves?
|
||||
|
@ -66,14 +68,10 @@ class WorblehatCli(NumberedCmd):
|
|||
bookcase_uid = None
|
||||
for shelf in bookcase_shelfs:
|
||||
if shelf.bookcase.uid != bookcase_uid:
|
||||
print(shelf.bookcase.name)
|
||||
print(shelf.bookcase.short_str())
|
||||
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')
|
||||
print(f' {shelf.short_str()} - {sum(i.amount for i in shelf.items)} items')
|
||||
|
||||
|
||||
def do_show_bookcase(self, arg: str):
|
||||
|
@ -85,10 +83,7 @@ class WorblehatCli(NumberedCmd):
|
|||
bookcase = bookcase_selector.result
|
||||
|
||||
for shelf in bookcase.shelfs:
|
||||
name = f"r{shelf.row}-c{shelf.column}"
|
||||
if shelf.description is not None:
|
||||
name += f" [{shelf.description}]"
|
||||
print(name)
|
||||
print(shelf.short_str())
|
||||
for item in shelf.items:
|
||||
print(f' {item.name} - {item.amount} copies')
|
||||
|
||||
|
@ -126,15 +121,6 @@ class WorblehatCli(NumberedCmd):
|
|||
bookcase_selector.cmdloop()
|
||||
bookcase = bookcase_selector.result
|
||||
|
||||
while True:
|
||||
row = input('Row> ')
|
||||
try:
|
||||
row = int(row)
|
||||
except ValueError:
|
||||
print('Error: row must be a number')
|
||||
continue
|
||||
break
|
||||
|
||||
while True:
|
||||
column = input('Column> ')
|
||||
try:
|
||||
|
@ -144,15 +130,24 @@ class WorblehatCli(NumberedCmd):
|
|||
continue
|
||||
break
|
||||
|
||||
while True:
|
||||
row = input('Row> ')
|
||||
try:
|
||||
row = int(row)
|
||||
except ValueError:
|
||||
print('Error: row must be a number')
|
||||
continue
|
||||
break
|
||||
|
||||
if self.sql_session.scalars(
|
||||
select(BookcaseShelf)
|
||||
.where(
|
||||
BookcaseShelf.bookcase == bookcase,
|
||||
BookcaseShelf.row == row,
|
||||
BookcaseShelf.column == column,
|
||||
BookcaseShelf.row == row,
|
||||
)
|
||||
).one_or_none() is not None:
|
||||
print(f'Error: a bookshelf in bookcase {bookcase.name} with position {row}-{column} already exists')
|
||||
print(f'Error: a bookshelf in bookcase {bookcase.name} with position c{column}-r{row} already exists')
|
||||
return
|
||||
|
||||
description = input('Description> ')
|
||||
|
@ -172,7 +167,7 @@ class WorblehatCli(NumberedCmd):
|
|||
def _create_bookcase_item(self, isbn: str):
|
||||
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'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:
|
||||
|
@ -191,37 +186,7 @@ class WorblehatCli(NumberedCmd):
|
|||
bookcase_selector.cmdloop()
|
||||
bookcase = bookcase_selector.result
|
||||
|
||||
def __complete_bookshelf_selection(session: Session, cls: type, arg: str):
|
||||
args = arg.split('-')
|
||||
query = select(cls.row, cls.column).where(cls.bookcase == bookcase)
|
||||
try:
|
||||
if arg != '' and len(args) > 0:
|
||||
query = query.where(cls.row == int(args[0]))
|
||||
if len(args) > 1:
|
||||
query = query.where(cls.column == int(args[1]))
|
||||
except ValueError:
|
||||
return []
|
||||
|
||||
result = session.execute(query).all()
|
||||
return [f"{r}-{c}" for r,c in result]
|
||||
|
||||
print('Please select the shelf where the item is placed:')
|
||||
bookcase_shelf_selector = InteractiveItemSelector(
|
||||
cls = BookcaseShelf,
|
||||
sql_session = self.sql_session,
|
||||
execute_selection = lambda session, cls, arg: session.scalars(
|
||||
select(cls)
|
||||
.where(
|
||||
cls.bookcase == bookcase,
|
||||
cls.column == int(arg.split('-')[1]),
|
||||
cls.row == int(arg.split('-')[0]),
|
||||
)
|
||||
).all(),
|
||||
complete_selection = __complete_bookshelf_selection,
|
||||
)
|
||||
|
||||
bookcase_shelf_selector.cmdloop()
|
||||
bookcase_item.shelf = bookcase_shelf_selector.result
|
||||
bookcase_item.shelf = select_bookcase_shelf(bookcase, self.sql_session)
|
||||
|
||||
print('Please select the items media type:')
|
||||
media_type_selector = InteractiveItemSelector(
|
||||
|
@ -254,14 +219,14 @@ class WorblehatCli(NumberedCmd):
|
|||
select(BookcaseItem)
|
||||
.where(BookcaseItem.isbn == isbn)
|
||||
).one_or_none()) is not None:
|
||||
print('Found existing BookcaseItem:', existing_item)
|
||||
print(f'\nFound existing item for isbn "{isbn}"')
|
||||
BookcaseItemCli(
|
||||
sql_session = self.sql_session,
|
||||
bookcase_item = existing_item,
|
||||
).cmdloop()
|
||||
return
|
||||
|
||||
if prompt_yes_no(f"Could not find item with isbn '{isbn}'.\nWould you like to create it?", default=True):
|
||||
if prompt_yes_no(f"Could not find item with ISBN '{isbn}'.\nWould you like to create it?", default=True):
|
||||
self._create_bookcase_item(isbn)
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from worblehat.cli.prompt_utils import (
|
|||
prompt_yes_no,
|
||||
)
|
||||
from worblehat.models import (
|
||||
Bookcase,
|
||||
BookcaseItem,
|
||||
Language,
|
||||
MediaType,
|
||||
|
@ -18,21 +19,27 @@ from worblehat.services.bookcase_item import (
|
|||
is_valid_isbn,
|
||||
)
|
||||
|
||||
from .bookcase_shelf_selector import select_bookcase_shelf
|
||||
|
||||
def _selected_bookcase_item_prompt(bookcase_item: BookcaseItem) -> str:
|
||||
return dedent(f'''
|
||||
Item: {bookcase_item.name}
|
||||
ISBN: {bookcase_item.isbn}
|
||||
Amount: {bookcase_item.amount}
|
||||
Authors: {', '.join(a.name for a in bookcase_item.authors)}
|
||||
Bookcase: {bookcase_item.shelf.bookcase.short_str()}
|
||||
Shelf: {bookcase_item.shelf.short_str()}
|
||||
''')
|
||||
|
||||
class BookcaseItemCli(NumberedCmd):
|
||||
def __init__(self, sql_session: Session, bookcase_item: BookcaseItem):
|
||||
super().__init__()
|
||||
self.sql_session = sql_session
|
||||
self.bookcase_item = bookcase_item
|
||||
|
||||
def do_show(self, _: str):
|
||||
print(dedent(f"""
|
||||
Bookcase Item:
|
||||
Name: {self.bookcase_item.name}
|
||||
ISBN: {self.bookcase_item.isbn}
|
||||
Amount: {self.bookcase_item.amount}
|
||||
Shelf: {self.bookcase_item.shelf.column}-{self.bookcase_item.shelf.row}
|
||||
Description: {self.bookcase_item.shelf.description}
|
||||
"""))
|
||||
@property
|
||||
def prompt_header(self) -> str:
|
||||
return _selected_bookcase_item_prompt(self.bookcase_item)
|
||||
|
||||
def do_update_data(self, _: str):
|
||||
item = create_bookcase_item_from_isbn(self.sql_session, self.bookcase_item.isbn)
|
||||
|
@ -41,35 +48,35 @@ class BookcaseItemCli(NumberedCmd):
|
|||
self.bookcase_item.authors = item.authors
|
||||
self.bookcase_item.language = item.language
|
||||
|
||||
|
||||
def do_edit(self, arg: str):
|
||||
EditBookcaseCli(self.sql_session, self.bookcase_item, self).cmdloop()
|
||||
|
||||
|
||||
def do_loan(self, arg: str):
|
||||
print('TODO: implement loan')
|
||||
|
||||
def do_exit(self, _: str):
|
||||
|
||||
def do_done(self, _: str):
|
||||
return True
|
||||
|
||||
|
||||
funcs = {
|
||||
1: {
|
||||
'f': do_show,
|
||||
'doc': 'Show bookcase item',
|
||||
'f': do_loan,
|
||||
'doc': 'Loan',
|
||||
},
|
||||
2: {
|
||||
'f': do_update_data,
|
||||
'doc': 'Pull updated data from online databases',
|
||||
},
|
||||
3: {
|
||||
'f': do_edit,
|
||||
'doc': 'Edit',
|
||||
},
|
||||
4: {
|
||||
'f': do_loan,
|
||||
'doc': 'Loan bookcase item',
|
||||
3: {
|
||||
'f': do_update_data,
|
||||
'doc': 'Pull updated data from online databases',
|
||||
},
|
||||
5: {
|
||||
'f': do_exit,
|
||||
'doc': 'Exit',
|
||||
4: {
|
||||
'f': do_done,
|
||||
'doc': 'Done',
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -80,6 +87,9 @@ class EditBookcaseCli(NumberedCmd):
|
|||
self.bookcase_item = bookcase_item
|
||||
self.parent = parent
|
||||
|
||||
@property
|
||||
def prompt_header(self) -> str:
|
||||
return _selected_bookcase_item_prompt(self.bookcase_item)
|
||||
|
||||
def do_name(self, _: str):
|
||||
while True:
|
||||
|
@ -159,5 +169,51 @@ class EditBookcaseCli(NumberedCmd):
|
|||
self.bookcase_item.amount = new_amount
|
||||
|
||||
|
||||
def do_exit():
|
||||
def do_shelf(self, _: str):
|
||||
bookcase_selector = InteractiveItemSelector(
|
||||
Bookcase,
|
||||
self.sql_session,
|
||||
)
|
||||
bookcase_selector.cmdloop()
|
||||
bookcase = bookcase_selector.result
|
||||
|
||||
shelf = select_bookcase_shelf(bookcase, self.sql_session)
|
||||
|
||||
self.bookcase_item.shelf = shelf
|
||||
|
||||
|
||||
def do_done(self, _: str):
|
||||
return True
|
||||
|
||||
|
||||
funcs = {
|
||||
1: {
|
||||
'f': do_name,
|
||||
'doc': 'Change name',
|
||||
},
|
||||
2: {
|
||||
'f': do_isbn,
|
||||
'doc': 'Change ISBN',
|
||||
},
|
||||
3: {
|
||||
'f': do_language,
|
||||
'doc': 'Change language',
|
||||
},
|
||||
4: {
|
||||
'f': do_media_type,
|
||||
'doc': 'Change media type',
|
||||
},
|
||||
5: {
|
||||
'f': do_amount,
|
||||
'doc': 'Change amount',
|
||||
},
|
||||
6: {
|
||||
'f': do_shelf,
|
||||
'doc': 'Change shelf',
|
||||
},
|
||||
7: {
|
||||
'f': do_done,
|
||||
'doc': 'Done',
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from worblehat.cli.prompt_utils import InteractiveItemSelector
|
||||
from worblehat.models import (
|
||||
Bookcase,
|
||||
BookcaseShelf,
|
||||
)
|
||||
|
||||
def select_bookcase_shelf(
|
||||
bookcase: Bookcase,
|
||||
sql_session: Session,
|
||||
prompt: str = "Please select the shelf where the item is placed (col-row):"
|
||||
) -> BookcaseShelf:
|
||||
def __complete_bookshelf_selection(session: Session, cls: type, arg: str):
|
||||
args = arg.split('-')
|
||||
query = select(cls.row, cls.column).where(cls.bookcase == bookcase)
|
||||
try:
|
||||
if arg != '' and len(args) > 0:
|
||||
query = query.where(cls.column == int(args[0]))
|
||||
if len(args) > 1:
|
||||
query = query.where(cls.row == int(args[1]))
|
||||
except ValueError:
|
||||
return []
|
||||
|
||||
result = session.execute(query).all()
|
||||
return [f"{c}-{r}" for r,c in result]
|
||||
|
||||
print(prompt)
|
||||
bookcase_shelf_selector = InteractiveItemSelector(
|
||||
cls = BookcaseShelf,
|
||||
sql_session = sql_session,
|
||||
execute_selection = lambda session, cls, arg: session.scalars(
|
||||
select(cls)
|
||||
.where(
|
||||
cls.bookcase == bookcase,
|
||||
cls.column == int(arg.split('-')[0]),
|
||||
cls.row == int(arg.split('-')[1]),
|
||||
)
|
||||
).all(),
|
||||
complete_selection = __complete_bookshelf_selection,
|
||||
)
|
||||
|
||||
bookcase_shelf_selector.cmdloop()
|
||||
return bookcase_shelf_selector.result
|
|
@ -29,3 +29,9 @@ class Bookcase(Base, UidMixin, UniqueNameMixin):
|
|||
self.name = name
|
||||
self.description = description
|
||||
|
||||
def short_str(self) -> str:
|
||||
result = self.name
|
||||
if self.description is not None:
|
||||
result += f' [{self.description}]'
|
||||
return result
|
||||
|
||||
|
|
|
@ -50,4 +50,10 @@ class BookcaseShelf(Base, UidMixin):
|
|||
self.row = row
|
||||
self.column = column
|
||||
self.bookcase = bookcase
|
||||
self.description = description
|
||||
self.description = description
|
||||
|
||||
def short_str(self) -> str:
|
||||
result = f'{self.column}-{self.row}'
|
||||
if self.description is not None:
|
||||
result += f' [{self.description}]'
|
||||
return result
|
Loading…
Reference in New Issue