cli: add some search functionality
This commit is contained in:
parent
e154989a16
commit
d13a3a0932
|
@ -58,13 +58,12 @@ Run `poetry run worblehat --help` for more info
|
|||
- [X] Ability to create and update bookcases
|
||||
- [X] Ability to create and update bookcase shelfs
|
||||
- [X] Ability to create and update bookcase items
|
||||
- [X] Ability to borrow an item
|
||||
- [X] Ability to borrow and deliver items
|
||||
- [ ] Ability to borrow and deliver multiple items at a time
|
||||
- [X] Ability to enter the queue for borrowing an item
|
||||
- [ ] Ability to extend a borrowing, only if no one is behind you in the queue
|
||||
- [ ] Ability to borrow multiple items at a time
|
||||
- [ ] Ability to deliver multiple items at a time
|
||||
- [ ] Ability to list borrowed items which are overdue
|
||||
- [ ] Ability to search for items
|
||||
- [~] Ability to search for items
|
||||
- [ ] Ability to print PVV-specific labels for items missing a label, or which for any other reason needs a custom one
|
||||
- [ ] Ascii art of monkey with fingers in eyes
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from .subclis import (
|
|||
AdvancedOptionsCli,
|
||||
BookcaseItemCli,
|
||||
select_bookcase_shelf,
|
||||
SearchCli,
|
||||
)
|
||||
|
||||
# TODO: Category seems to have been forgotten. Maybe relevant interactivity should be added?
|
||||
|
@ -161,7 +162,13 @@ class WorblehatCli(NumberedCmd):
|
|||
|
||||
|
||||
def do_search(self, _: str):
|
||||
print('TODO: implement search')
|
||||
search_cli = SearchCli(self.sql_session)
|
||||
search_cli.cmdloop()
|
||||
if search_cli.result is not None:
|
||||
BookcaseItemCli(
|
||||
sql_session = self.sql_session,
|
||||
bookcase_item = search_cli.result,
|
||||
).cmdloop()
|
||||
|
||||
|
||||
def do_advanced(self, _: str):
|
||||
|
|
|
@ -129,16 +129,16 @@ class NumberedCmd(Cmd):
|
|||
|
||||
|
||||
prompt_header: str | None = None
|
||||
funcs: dict[int, dict[str, str | Callable[[Any, str], bool | None]]]
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
||||
@classmethod
|
||||
def _generate_usage_list(cls) -> str:
|
||||
def _generate_usage_list(self) -> str:
|
||||
result = ''
|
||||
for i, func in cls.funcs.items():
|
||||
for i, func in self.funcs.items():
|
||||
if i == 0:
|
||||
i = '*'
|
||||
result += f'{i}) {func["doc"]}\n'
|
||||
|
@ -185,4 +185,27 @@ class NumberedCmd(Cmd):
|
|||
else:
|
||||
result += f'[{self.lastcmd}]> '
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
|
||||
class NumberedItemSelector(NumberedCmd):
|
||||
def __init__(
|
||||
self,
|
||||
items: list[Any],
|
||||
stringify: Callable[[Any], str] = lambda x: str(x),
|
||||
):
|
||||
super().__init__()
|
||||
self.items = items
|
||||
self.stringify = stringify
|
||||
self.funcs = {
|
||||
i: {
|
||||
'f': self._select_item,
|
||||
'doc': self.stringify(item),
|
||||
}
|
||||
for i, item in enumerate(items, start=1)
|
||||
}
|
||||
|
||||
|
||||
def _select_item(self, *a):
|
||||
self.result = self.items[int(self.lastcmd)-1]
|
||||
return True
|
|
@ -1,3 +1,4 @@
|
|||
from .advanced_options import AdvancedOptionsCli
|
||||
from .bookcase_item import BookcaseItemCli
|
||||
from .bookcase_shelf_selector import select_bookcase_shelf
|
||||
from .bookcase_shelf_selector import select_bookcase_shelf
|
||||
from .search import SearchCli
|
|
@ -0,0 +1,145 @@
|
|||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
from worblehat.cli.prompt_utils import (
|
||||
NumberedCmd,
|
||||
NumberedItemSelector,
|
||||
)
|
||||
from worblehat.models import Author, BookcaseItem
|
||||
|
||||
|
||||
class SearchCli(NumberedCmd):
|
||||
def __init__(self, sql_session: Session):
|
||||
super().__init__()
|
||||
self.sql_session = sql_session
|
||||
|
||||
|
||||
def do_search_all(self, _: str):
|
||||
print('TODO: Implement search all')
|
||||
|
||||
|
||||
def do_search_title(self, _: str):
|
||||
while (input_text := input('Enter title: ')) == '':
|
||||
pass
|
||||
|
||||
items = self.sql_session.scalars(
|
||||
select(BookcaseItem)
|
||||
.where(BookcaseItem.name.ilike(f'%{input_text}%')),
|
||||
).all()
|
||||
|
||||
if len(items) == 0:
|
||||
print('No items found.')
|
||||
return
|
||||
|
||||
selector = NumberedItemSelector(
|
||||
items = items,
|
||||
stringify = lambda item: f"{item.name} ({item.isbn})",
|
||||
)
|
||||
selector.cmdloop()
|
||||
if selector.result is not None:
|
||||
self.result = selector.result
|
||||
return True
|
||||
|
||||
|
||||
def do_search_author(self, _: str):
|
||||
while (input_text := input('Enter author name: ')) == '':
|
||||
pass
|
||||
|
||||
author = self.sql_session.scalars(
|
||||
select(Author)
|
||||
.where(Author.name.ilike(f'%{input_text}%')),
|
||||
).all()
|
||||
|
||||
if len(author) == 0:
|
||||
print('No authors found.')
|
||||
return
|
||||
elif len(author) == 1:
|
||||
selected_author = author[0]
|
||||
print('Found author:')
|
||||
print(f" {selected_author.name} ({sum(item.amount for item in selected_author.books)} items)")
|
||||
else:
|
||||
selector = NumberedItemSelector(
|
||||
items = author,
|
||||
stringify = lambda author: f"{author.name} ({sum(item.amount for item in author.books)} items)",
|
||||
)
|
||||
selector.cmdloop()
|
||||
if selector.result is None:
|
||||
return
|
||||
selected_author = selector.result
|
||||
|
||||
selector = NumberedItemSelector(
|
||||
items = selected_author.books,
|
||||
stringify = lambda item: f"{item.name} ({item.isbn})",
|
||||
)
|
||||
selector.cmdloop()
|
||||
if selector.result is not None:
|
||||
self.result = selector.result
|
||||
return True
|
||||
|
||||
|
||||
def do_search_owner(self, _: str):
|
||||
while (input_text := input('Enter username: ')) == '':
|
||||
pass
|
||||
|
||||
users = self.sql_session.scalars(
|
||||
select(BookcaseItem.owner)
|
||||
.where(BookcaseItem.owner.ilike(f'%{input_text}%'))
|
||||
.distinct(),
|
||||
).all()
|
||||
|
||||
if len(users) == 0:
|
||||
print('No users found.')
|
||||
return
|
||||
elif len(users) == 1:
|
||||
selected_user = users[0]
|
||||
print('Found user:')
|
||||
print(f" {selected_user}")
|
||||
else:
|
||||
selector = NumberedItemSelector(items = users)
|
||||
selector.cmdloop()
|
||||
if selector.result is None:
|
||||
return
|
||||
selected_user = selector.result
|
||||
|
||||
items = self.sql_session.scalars(
|
||||
select(BookcaseItem)
|
||||
.where(BookcaseItem.owner == selected_user),
|
||||
).all()
|
||||
|
||||
selector = NumberedItemSelector(
|
||||
items = items,
|
||||
stringify = lambda item: f"{item.name} ({item.isbn})",
|
||||
)
|
||||
selector.cmdloop()
|
||||
if selector.result is not None:
|
||||
self.result = selector.result
|
||||
return True
|
||||
|
||||
|
||||
def do_done(self, _: str):
|
||||
return True
|
||||
|
||||
|
||||
funcs = {
|
||||
1: {
|
||||
'f': do_search_all,
|
||||
'doc': 'Search everything',
|
||||
},
|
||||
2: {
|
||||
'f': do_search_title,
|
||||
'doc': 'Search by title',
|
||||
},
|
||||
3: {
|
||||
'f': do_search_author,
|
||||
'doc': 'Search by author',
|
||||
},
|
||||
4: {
|
||||
'f': do_search_owner,
|
||||
'doc': 'Search by owner',
|
||||
},
|
||||
9: {
|
||||
'f': do_done,
|
||||
'doc': 'Done',
|
||||
},
|
||||
}
|
Loading…
Reference in New Issue