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 (
|
from sqlalchemy.orm import (
|
||||||
Session,
|
Session,
|
||||||
)
|
)
|
||||||
from worblehat.cli.subclis.bookcase_item import BookcaseItemCli
|
|
||||||
|
|
||||||
from worblehat.services.bookcase_item import (
|
from worblehat.services.bookcase_item import (
|
||||||
create_bookcase_item_from_isbn,
|
create_bookcase_item_from_isbn,
|
||||||
|
@ -20,6 +19,9 @@ from .prompt_utils import *
|
||||||
from worblehat.config import Config
|
from worblehat.config import Config
|
||||||
from worblehat.models import *
|
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?
|
# 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
|
# However, is there anyone who are going to search by category rather than just look in
|
||||||
# the shelves?
|
# the shelves?
|
||||||
|
@ -66,14 +68,10 @@ class WorblehatCli(NumberedCmd):
|
||||||
bookcase_uid = None
|
bookcase_uid = None
|
||||||
for shelf in bookcase_shelfs:
|
for shelf in bookcase_shelfs:
|
||||||
if shelf.bookcase.uid != bookcase_uid:
|
if shelf.bookcase.uid != bookcase_uid:
|
||||||
print(shelf.bookcase.name)
|
print(shelf.bookcase.short_str())
|
||||||
bookcase_uid = shelf.bookcase.uid
|
bookcase_uid = shelf.bookcase.uid
|
||||||
|
|
||||||
name = f"r{shelf.row}-c{shelf.column}"
|
print(f' {shelf.short_str()} - {sum(i.amount for i in shelf.items)} items')
|
||||||
if shelf.description is not None:
|
|
||||||
name += f" [{shelf.description}]"
|
|
||||||
|
|
||||||
print(f' {name} - {sum(i.amount for i in shelf.items)} items')
|
|
||||||
|
|
||||||
|
|
||||||
def do_show_bookcase(self, arg: str):
|
def do_show_bookcase(self, arg: str):
|
||||||
|
@ -85,10 +83,7 @@ class WorblehatCli(NumberedCmd):
|
||||||
bookcase = bookcase_selector.result
|
bookcase = bookcase_selector.result
|
||||||
|
|
||||||
for shelf in bookcase.shelfs:
|
for shelf in bookcase.shelfs:
|
||||||
name = f"r{shelf.row}-c{shelf.column}"
|
print(shelf.short_str())
|
||||||
if shelf.description is not None:
|
|
||||||
name += f" [{shelf.description}]"
|
|
||||||
print(name)
|
|
||||||
for item in shelf.items:
|
for item in shelf.items:
|
||||||
print(f' {item.name} - {item.amount} copies')
|
print(f' {item.name} - {item.amount} copies')
|
||||||
|
|
||||||
|
@ -126,15 +121,6 @@ class WorblehatCli(NumberedCmd):
|
||||||
bookcase_selector.cmdloop()
|
bookcase_selector.cmdloop()
|
||||||
bookcase = bookcase_selector.result
|
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:
|
while True:
|
||||||
column = input('Column> ')
|
column = input('Column> ')
|
||||||
try:
|
try:
|
||||||
|
@ -144,15 +130,24 @@ class WorblehatCli(NumberedCmd):
|
||||||
continue
|
continue
|
||||||
break
|
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(
|
if self.sql_session.scalars(
|
||||||
select(BookcaseShelf)
|
select(BookcaseShelf)
|
||||||
.where(
|
.where(
|
||||||
BookcaseShelf.bookcase == bookcase,
|
BookcaseShelf.bookcase == bookcase,
|
||||||
BookcaseShelf.row == row,
|
|
||||||
BookcaseShelf.column == column,
|
BookcaseShelf.column == column,
|
||||||
|
BookcaseShelf.row == row,
|
||||||
)
|
)
|
||||||
).one_or_none() is not None:
|
).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
|
return
|
||||||
|
|
||||||
description = input('Description> ')
|
description = input('Description> ')
|
||||||
|
@ -172,7 +167,7 @@ class WorblehatCli(NumberedCmd):
|
||||||
def _create_bookcase_item(self, isbn: str):
|
def _create_bookcase_item(self, isbn: str):
|
||||||
bookcase_item = create_bookcase_item_from_isbn(isbn, self.sql_session)
|
bookcase_item = create_bookcase_item_from_isbn(isbn, self.sql_session)
|
||||||
if bookcase_item is None:
|
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.')
|
print(f'If you think this is not due to a bug, please add the book to openlibrary.org before continuing.')
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -191,37 +186,7 @@ class WorblehatCli(NumberedCmd):
|
||||||
bookcase_selector.cmdloop()
|
bookcase_selector.cmdloop()
|
||||||
bookcase = bookcase_selector.result
|
bookcase = bookcase_selector.result
|
||||||
|
|
||||||
def __complete_bookshelf_selection(session: Session, cls: type, arg: str):
|
bookcase_item.shelf = select_bookcase_shelf(bookcase, self.sql_session)
|
||||||
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
|
|
||||||
|
|
||||||
print('Please select the items media type:')
|
print('Please select the items media type:')
|
||||||
media_type_selector = InteractiveItemSelector(
|
media_type_selector = InteractiveItemSelector(
|
||||||
|
@ -254,14 +219,14 @@ class WorblehatCli(NumberedCmd):
|
||||||
select(BookcaseItem)
|
select(BookcaseItem)
|
||||||
.where(BookcaseItem.isbn == isbn)
|
.where(BookcaseItem.isbn == isbn)
|
||||||
).one_or_none()) is not None:
|
).one_or_none()) is not None:
|
||||||
print('Found existing BookcaseItem:', existing_item)
|
print(f'\nFound existing item for isbn "{isbn}"')
|
||||||
BookcaseItemCli(
|
BookcaseItemCli(
|
||||||
sql_session = self.sql_session,
|
sql_session = self.sql_session,
|
||||||
bookcase_item = existing_item,
|
bookcase_item = existing_item,
|
||||||
).cmdloop()
|
).cmdloop()
|
||||||
return
|
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)
|
self._create_bookcase_item(isbn)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from worblehat.cli.prompt_utils import (
|
||||||
prompt_yes_no,
|
prompt_yes_no,
|
||||||
)
|
)
|
||||||
from worblehat.models import (
|
from worblehat.models import (
|
||||||
|
Bookcase,
|
||||||
BookcaseItem,
|
BookcaseItem,
|
||||||
Language,
|
Language,
|
||||||
MediaType,
|
MediaType,
|
||||||
|
@ -18,21 +19,27 @@ from worblehat.services.bookcase_item import (
|
||||||
is_valid_isbn,
|
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):
|
class BookcaseItemCli(NumberedCmd):
|
||||||
def __init__(self, sql_session: Session, bookcase_item: BookcaseItem):
|
def __init__(self, sql_session: Session, bookcase_item: BookcaseItem):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.sql_session = sql_session
|
self.sql_session = sql_session
|
||||||
self.bookcase_item = bookcase_item
|
self.bookcase_item = bookcase_item
|
||||||
|
|
||||||
def do_show(self, _: str):
|
@property
|
||||||
print(dedent(f"""
|
def prompt_header(self) -> str:
|
||||||
Bookcase Item:
|
return _selected_bookcase_item_prompt(self.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}
|
|
||||||
"""))
|
|
||||||
|
|
||||||
def do_update_data(self, _: str):
|
def do_update_data(self, _: str):
|
||||||
item = create_bookcase_item_from_isbn(self.sql_session, self.bookcase_item.isbn)
|
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.authors = item.authors
|
||||||
self.bookcase_item.language = item.language
|
self.bookcase_item.language = item.language
|
||||||
|
|
||||||
|
|
||||||
def do_edit(self, arg: str):
|
def do_edit(self, arg: str):
|
||||||
EditBookcaseCli(self.sql_session, self.bookcase_item, self).cmdloop()
|
EditBookcaseCli(self.sql_session, self.bookcase_item, self).cmdloop()
|
||||||
|
|
||||||
|
|
||||||
def do_loan(self, arg: str):
|
def do_loan(self, arg: str):
|
||||||
print('TODO: implement loan')
|
print('TODO: implement loan')
|
||||||
|
|
||||||
def do_exit(self, _: str):
|
|
||||||
|
def do_done(self, _: str):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
funcs = {
|
funcs = {
|
||||||
1: {
|
1: {
|
||||||
'f': do_show,
|
'f': do_loan,
|
||||||
'doc': 'Show bookcase item',
|
'doc': 'Loan',
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
'f': do_update_data,
|
|
||||||
'doc': 'Pull updated data from online databases',
|
|
||||||
},
|
|
||||||
3: {
|
|
||||||
'f': do_edit,
|
'f': do_edit,
|
||||||
'doc': 'Edit',
|
'doc': 'Edit',
|
||||||
},
|
},
|
||||||
4: {
|
3: {
|
||||||
'f': do_loan,
|
'f': do_update_data,
|
||||||
'doc': 'Loan bookcase item',
|
'doc': 'Pull updated data from online databases',
|
||||||
},
|
},
|
||||||
5: {
|
4: {
|
||||||
'f': do_exit,
|
'f': do_done,
|
||||||
'doc': 'Exit',
|
'doc': 'Done',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +87,9 @@ class EditBookcaseCli(NumberedCmd):
|
||||||
self.bookcase_item = bookcase_item
|
self.bookcase_item = bookcase_item
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prompt_header(self) -> str:
|
||||||
|
return _selected_bookcase_item_prompt(self.bookcase_item)
|
||||||
|
|
||||||
def do_name(self, _: str):
|
def do_name(self, _: str):
|
||||||
while True:
|
while True:
|
||||||
|
@ -159,5 +169,51 @@ class EditBookcaseCli(NumberedCmd):
|
||||||
self.bookcase_item.amount = new_amount
|
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
|
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.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
|
def short_str(self) -> str:
|
||||||
|
result = self.name
|
||||||
|
if self.description is not None:
|
||||||
|
result += f' [{self.description}]'
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
|
@ -51,3 +51,9 @@ class BookcaseShelf(Base, UidMixin):
|
||||||
self.column = column
|
self.column = column
|
||||||
self.bookcase = bookcase
|
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