diff --git a/worblehat/cli/main.py b/worblehat/cli/main.py index 05cc32b..3095b0f 100644 --- a/worblehat/cli/main.py +++ b/worblehat/cli/main.py @@ -61,26 +61,6 @@ class WorblehatCli(NumberedCmd): exit(0) - def do_list_bookcases(self, _: str): - 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.short_str()) - bookcase_uid = shelf.bookcase.uid - - print(f' {shelf.short_str()} - {sum(i.amount for i in shelf.items)} items') - - def do_show_bookcase(self, arg: str): bookcase_selector = InteractiveItemSelector( cls = Bookcase, @@ -95,6 +75,35 @@ class WorblehatCli(NumberedCmd): print(f' {item.name} - {item.amount} copies') + def do_show_borrowed_queued(self, _: str): + borrowed_items = self.sql_session.scalars( + select(BookcaseItemBorrowing) + .where(BookcaseItemBorrowing.delivered.is_(None)) + .order_by(BookcaseItemBorrowing.end_time), + ).all() + + if len(borrowed_items) == 0: + print('No borrowed items found.') + else: + print('Borrowed items:') + for item in borrowed_items: + print(f'- {item.username} - {item.item.name} - to be delivered by {item.end_time.strftime("%Y-%m-%d")}') + + print() + + queued_items = self.sql_session.scalars( + select(BookcaseItemBorrowingQueue) + .order_by(BookcaseItemBorrowingQueue.entered_queue_time), + ).all() + + if len(queued_items) == 0: + print('No queued items found.') + else: + print('Users in queue:') + for item in queued_items: + print(f'- {item.username} - {item.item.name} - entered queue at {item.entered_queue_time.strftime("%Y-%m-%d")}') + + def _create_bookcase_item(self, isbn: str): bookcase_item = create_bookcase_item_from_isbn(isbn, self.sql_session) if bookcase_item is None: @@ -149,6 +158,8 @@ class WorblehatCli(NumberedCmd): if (existing_item := self.sql_session.scalars( select(BookcaseItem) .where(BookcaseItem.isbn == isbn) + .join(BookcaseItemBorrowing) + .join(BookcaseItemBorrowingQueue) ).one_or_none()) is not None: print(f'\nFound existing item for isbn "{isbn}"') BookcaseItemCli( @@ -185,11 +196,12 @@ class WorblehatCli(NumberedCmd): ).all() if len(slubberter) == 0: - print('No slubberts found. Yay!') + print('No slubberts found. Life is good.') return for slubbert in slubberter: - print(f'{slubbert.username} - {slubbert.item.name} - {slubbert.end_time.strftime("%Y-%m-%d")}') + print('Slubberter:') + print(f'- {slubbert.username} - {slubbert.item.name} - {slubbert.end_time.strftime("%Y-%m-%d")}') def do_advanced(self, _: str): @@ -225,20 +237,20 @@ class WorblehatCli(NumberedCmd): 'doc': 'Choose / Add item with its ISBN', }, 1: { - 'f': do_list_bookcases, - 'doc': 'List all bookcases', - }, - 2: { 'f': do_search, 'doc': 'Search', }, - 3: { + 2: { 'f': do_show_bookcase, 'doc': 'Show a bookcase, and its items', }, + 3: { + 'f': do_show_borrowed_queued, + 'doc': 'Show borrowed/queued items', + }, 4: { 'f': do_show_slabbedasker, - 'doc': 'Show a slabbedasker, and their wicked ways', + 'doc': 'Show slabbedasker', }, 5: { 'f': do_save, diff --git a/worblehat/cli/prompt_utils.py b/worblehat/cli/prompt_utils.py index d2fac67..9989cc8 100644 --- a/worblehat/cli/prompt_utils.py +++ b/worblehat/cli/prompt_utils.py @@ -58,6 +58,7 @@ class InteractiveItemSelector(Cmd): self.execute_selection = execute_selection self.complete_selection = complete_selection self.default_item = default + self.result = None if default is not None: self.prompt = f'Select {cls.__name__} [{default.name}]> ' @@ -197,6 +198,7 @@ class NumberedItemSelector(NumberedCmd): super().__init__() self.items = items self.stringify = stringify + self.result = None self.funcs = { i: { 'f': self._select_item, diff --git a/worblehat/cli/subclis/advanced_options.py b/worblehat/cli/subclis/advanced_options.py index 6cd7f1c..c727092 100644 --- a/worblehat/cli/subclis/advanced_options.py +++ b/worblehat/cli/subclis/advanced_options.py @@ -91,6 +91,26 @@ class AdvancedOptionsCli(NumberedCmd): self.sql_session.flush() + def do_list_bookcases(self, _: str): + 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.short_str()) + bookcase_uid = shelf.bookcase.uid + + print(f' {shelf.short_str()} - {sum(i.amount for i in shelf.items)} items') + + def do_done(self, _: str): return True @@ -104,6 +124,10 @@ class AdvancedOptionsCli(NumberedCmd): 'f': do_add_bookcase_shelf, 'doc': 'Add bookcase shelf', }, + 3: { + 'f': do_list_bookcases, + 'doc': 'List all bookcases', + }, 9: { 'f': do_done, 'doc': 'Done', diff --git a/worblehat/cli/subclis/bookcase_item.py b/worblehat/cli/subclis/bookcase_item.py index 2672a50..40e3a22 100644 --- a/worblehat/cli/subclis/bookcase_item.py +++ b/worblehat/cli/subclis/bookcase_item.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from textwrap import dedent from sqlalchemy import select @@ -7,6 +7,7 @@ from sqlalchemy.orm import Session from worblehat.cli.prompt_utils import ( InteractiveItemSelector, NumberedCmd, + NumberedItemSelector, format_date, prompt_yes_no, ) @@ -22,6 +23,7 @@ from worblehat.services.bookcase_item import ( create_bookcase_item_from_isbn, is_valid_isbn, ) +from worblehat.services.config import Config from .bookcase_shelf_selector import select_bookcase_shelf @@ -174,6 +176,50 @@ class BookcaseItemCli(NumberedCmd): print(f'Successfully delivered the item for {borrowing.username}') + def do_extend_borrowing(self, _: str): + borrowings = self.sql_session.scalars( + select(BookcaseItemBorrowing) + .join(BookcaseItem, BookcaseItem.uid == BookcaseItemBorrowing.fk_bookcase_item_uid) + .where(BookcaseItem.isbn == self.bookcase_item.isbn) + .order_by(BookcaseItemBorrowing.username) + ).all() + + if len(borrowings) == 0: + print('No one seems to have borrowed this item') + return + + borrowing_queue = self.sql_session.scalars( + select(BookcaseItemBorrowingQueue) + .where( + BookcaseItemBorrowingQueue.item == self.bookcase_item, + BookcaseItemBorrowingQueue.item_became_available_time == None, + ) + .order_by(BookcaseItemBorrowingQueue.entered_queue_time) + ).all() + + if len(borrowing_queue) != 0: + print('Sorry, you cannot extend the borrowing because there are people waiting in the queue') + print('Borrowing queue:') + for i, b in enumerate(borrowing_queue): + print(f' {i + 1}) {b.username}') + return + + print('Who are you?') + selector = NumberedItemSelector( + items = list(borrowings), + stringify = lambda b: f'{b.username} - Until {format_date(b.end_time)}', + ) + selector.cmdloop() + if selector.result is None: + return + borrowing = selector.result + + borrowing.end_time = datetime.now() + timedelta(days=int(Config['deadline_daemon.days_before_queue_position_expires'])) + self.sql_session.flush() + + print(f'Successfully extended the borrowing for {borrowing.username} until {format_date(borrowing.end_time)}') + + def do_done(self, _: str): return True @@ -188,10 +234,14 @@ class BookcaseItemCli(NumberedCmd): 'doc': 'Deliver', }, 3: { + 'f': do_extend_borrowing, + 'doc': 'Extend borrowing', + }, + 4: { 'f': do_edit, 'doc': 'Edit', }, - 4: { + 5: { 'f': do_update_data, 'doc': 'Pull updated data from online databases', }, diff --git a/worblehat/cli/subclis/search.py b/worblehat/cli/subclis/search.py index aff7499..8bb7d4a 100644 --- a/worblehat/cli/subclis/search.py +++ b/worblehat/cli/subclis/search.py @@ -13,6 +13,7 @@ class SearchCli(NumberedCmd): def __init__(self, sql_session: Session): super().__init__() self.sql_session = sql_session + self.result = None def do_search_all(self, _: str): @@ -57,11 +58,11 @@ class SearchCli(NumberedCmd): 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)") + print(f" {selected_author.name} ({sum(item.amount for item in selected_author.items)} items)") else: selector = NumberedItemSelector( items = author, - stringify = lambda author: f"{author.name} ({sum(item.amount for item in author.books)} items)", + stringify = lambda author: f"{author.name} ({sum(item.amount for item in author.items)} items)", ) selector.cmdloop() if selector.result is None: @@ -69,7 +70,7 @@ class SearchCli(NumberedCmd): selected_author = selector.result selector = NumberedItemSelector( - items = selected_author.books, + items = list(selected_author.items), stringify = lambda item: f"{item.name} ({item.isbn})", ) selector.cmdloop() diff --git a/worblehat/services/seed_test_data.py b/worblehat/services/seed_test_data.py index 04388c9..021816e 100644 --- a/worblehat/services/seed_test_data.py +++ b/worblehat/services/seed_test_data.py @@ -1,4 +1,5 @@ import csv +from datetime import datetime, timedelta from pathlib import Path from sqlalchemy.orm import Session @@ -6,7 +7,11 @@ from sqlalchemy.orm import Session from worblehat.flaskapp.database import db from ..models import ( + Author, Bookcase, + BookcaseItem, + BookcaseItemBorrowing, + BookcaseItemBorrowingQueue, BookcaseShelf, Language, MediaType, @@ -117,6 +122,100 @@ def seed_data(sql_session: Session = db.session): BookcaseShelf(row=0, column=4, bookcase=bookcases[4], description="Religion"), ] + authors = [ + Author(name="Donald E. Knuth"), + Author(name="J.K. Rowling"), + Author(name="J.R.R. Tolkien"), + Author(name="George R.R. Martin"), + Author(name="Stephen King"), + Author(name="Agatha Christie"), + ] + + book1 = BookcaseItem( + name = "The Art of Computer Programming", + isbn = "9780201896831", + ) + book1.authors.add(authors[0]) + book1.media_type = media_types[0] + book1.shelf = shelfs[59] + + book2 = BookcaseItem( + name = "Harry Potter and the Philosopher's Stone", + isbn = "9780747532743", + ) + book2.authors.add(authors[1]) + book2.media_type = media_types[0] + book2.shelf = shelfs[-1] + + book_owned_by_other_user = BookcaseItem( + name = "Book owned by other user", + isbn = "9780747532744", + ) + + book_owned_by_other_user.owner = "other_user" + book_owned_by_other_user.authors.add(authors[4]) + book_owned_by_other_user.media_type = media_types[0] + book_owned_by_other_user.shelf = shelfs[-2] + + borrowed_book_more_available = BookcaseItem( + name = "Borrowed book with more available", + isbn = "9780747532745", + ) + borrowed_book_more_available.authors.add(authors[5]) + borrowed_book_more_available.media_type = media_types[0] + borrowed_book_more_available.shelf = shelfs[-3] + borrowed_book_more_available.amount = 2 + + borrowed_book_no_more_available = BookcaseItem( + name = "Borrowed book with no more available", + isbn = "9780747532746", + ) + borrowed_book_no_more_available.authors.add(authors[5]) + borrowed_book_no_more_available.media_type = media_types[0] + borrowed_book_no_more_available.shelf = shelfs[-3] + + borrowed_book_people_in_queue = BookcaseItem( + name = "Borrowed book with people in queue", + isbn = "9780747532747", + ) + borrowed_book_people_in_queue.authors.add(authors[5]) + borrowed_book_people_in_queue.media_type = media_types[0] + borrowed_book_people_in_queue.shelf = shelfs[-3] + + borrowed_book_by_slabbedask = BookcaseItem( + name = "Borrowed book by slabbedask", + isbn = "9780747532748", + ) + borrowed_book_by_slabbedask.authors.add(authors[5]) + borrowed_book_by_slabbedask.media_type = media_types[0] + borrowed_book_by_slabbedask.shelf = shelfs[-3] + + books = [ + book1, + book2, + book_owned_by_other_user, + borrowed_book_more_available, + borrowed_book_no_more_available, + borrowed_book_people_in_queue, + ] + + slabbedask_borrowing = BookcaseItemBorrowing( + username="slabbedask", + item=borrowed_book_more_available, + ) + slabbedask_borrowing.end_time = datetime.now() - timedelta(days=1) + + borrowings = [ + BookcaseItemBorrowing(username="user", item=borrowed_book_more_available), + BookcaseItemBorrowing(username="user", item=borrowed_book_no_more_available), + BookcaseItemBorrowing(username="user", item=borrowed_book_people_in_queue), + slabbedask_borrowing, + ] + + queue = [ + BookcaseItemBorrowingQueue(username="user", item=borrowed_book_people_in_queue), + ] + with open(Path(__file__).parent.parent.parent / 'data' / 'iso639_1.csv') as f: reader = csv.reader(f) languages = [Language(name, code) for (code, name) in reader] @@ -125,5 +224,9 @@ def seed_data(sql_session: Session = db.session): sql_session.add_all(bookcases) sql_session.add_all(shelfs) sql_session.add_all(languages) + sql_session.add_all(authors) + sql_session.add_all(books) + sql_session.add_all(borrowings) + sql_session.add_all(queue) sql_session.commit() print("Added test media types, bookcases and shelfs.") \ No newline at end of file