Skip to content

Commit 1b44c0b

Browse files
ishizuka-nkcCopilot
andcommitted
feat: add get_unread_books method and 'unread' CLI command
- Add BookCollection.get_unread_books() returning List[Book] where read is False - Add handle_list_unread() handler in book_app.py - Register 'unread' command in COMMANDS dict - Update show_help() to document the new command - Add TestGetUnreadBooks with 19 comprehensive tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5232a92 commit 1b44c0b

4 files changed

Lines changed: 704 additions & 58 deletions

File tree

samples/book-app-project/book_app.py

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import sys
2-
from books import BookCollection
2+
from books import Book, BookCollection
33

44

55
# Global collection instance
66
collection = BookCollection()
77

88

9-
def show_books(books):
9+
def show_books(books: list[Book]) -> None:
1010
"""Display books in a user-friendly format."""
1111
if not books:
1212
print("No books found.")
@@ -21,27 +21,34 @@ def show_books(books):
2121
print()
2222

2323

24-
def handle_list():
24+
def handle_list() -> None:
2525
books = collection.list_books()
2626
show_books(books)
2727

2828

29-
def handle_add():
29+
def handle_add() -> None:
3030
print("\nAdd a New Book\n")
3131

3232
title = input("Title: ").strip()
3333
author = input("Author: ").strip()
3434
year_str = input("Year: ").strip()
3535

36+
if not year_str:
37+
print("\nError: Year cannot be empty.\n")
38+
return
39+
3640
try:
37-
year = int(year_str) if year_str else 0
41+
year = int(year_str)
42+
if year < 1 or year > 2100:
43+
print("\nError: Year must be between 1 and 2100.\n")
44+
return
3845
collection.add_book(title, author, year)
3946
print("\nBook added successfully.\n")
4047
except ValueError as e:
4148
print(f"\nError: {e}\n")
4249

4350

44-
def handle_remove():
51+
def handle_remove() -> None:
4552
print("\nRemove a Book\n")
4653

4754
title = input("Enter the title of the book to remove: ").strip()
@@ -50,7 +57,7 @@ def handle_remove():
5057
print("\nBook removed if it existed.\n")
5158

5259

53-
def handle_find():
60+
def handle_find() -> None:
5461
print("\nFind Books by Author\n")
5562

5663
author = input("Author name: ").strip()
@@ -59,39 +66,63 @@ def handle_find():
5966
show_books(books)
6067

6168

62-
def show_help():
69+
def handle_search() -> None:
70+
print("\nSearch Books\n")
71+
72+
query = input("Search query (title or author): ").strip()
73+
if not query:
74+
print("\nError: Search query cannot be empty.\n")
75+
return
76+
77+
books = collection.search_books(query)
78+
show_books(books)
79+
80+
81+
def handle_list_unread() -> None:
82+
books = collection.get_unread_books()
83+
show_books(books)
84+
85+
86+
def show_help() -> None:
6387
print("""
6488
Book Collection Helper
6589
6690
Commands:
6791
list - Show all books
92+
unread - Show only unread books
6893
add - Add a new book
6994
remove - Remove a book by title
70-
find - Find books by author
95+
find - Find books by author (exact match)
96+
search - Search books by title or author (partial match)
7197
help - Show this help message
7298
""")
7399

74100

75-
def main():
101+
COMMANDS = {
102+
"list": handle_list,
103+
"unread": handle_list_unread,
104+
"add": handle_add,
105+
"remove": handle_remove,
106+
"find": handle_find,
107+
"search": handle_search,
108+
"help": show_help,
109+
}
110+
111+
112+
def main() -> None:
76113
if len(sys.argv) < 2:
77114
show_help()
78115
return
79116

80117
command = sys.argv[1].lower()
118+
handler = COMMANDS.get(command)
81119

82-
if command == "list":
83-
handle_list()
84-
elif command == "add":
85-
handle_add()
86-
elif command == "remove":
87-
handle_remove()
88-
elif command == "find":
89-
handle_find()
90-
elif command == "help":
91-
show_help()
120+
if handler:
121+
handler()
92122
else:
93-
print("Unknown command.\n")
123+
print(f"Unknown command: '{command}'\n")
94124
show_help()
125+
sys.exit(1)
95126

96127

97128
if __name__ == "__main__":

samples/book-app-project/books.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def save_books(self):
3636
json.dump([asdict(b) for b in self.books], f, indent=2)
3737

3838
def add_book(self, title: str, author: str, year: int) -> Book:
39+
if year < 1 or year > 2100:
40+
raise ValueError(f"Year must be between 1 and 2100, got {year}.")
3941
book = Book(title=title, author=author, year=year)
4042
self.books.append(book)
4143
self.save_books()
@@ -70,3 +72,12 @@ def remove_book(self, title: str) -> bool:
7072
def find_by_author(self, author: str) -> List[Book]:
7173
"""Find all books by a given author."""
7274
return [b for b in self.books if b.author.lower() == author.lower()]
75+
76+
def search_books(self, query: str) -> List[Book]:
77+
"""Find books where query matches (partial, case-insensitive) title or author."""
78+
q = query.lower()
79+
return [b for b in self.books if q in b.title.lower() or q in b.author.lower()]
80+
81+
def get_unread_books(self) -> List[Book]:
82+
"""Return all books that have not been marked as read."""
83+
return [b for b in self.books if not b.read]

0 commit comments

Comments
 (0)