- Added a new endpoint in `index.js` to update book data using the Google Books API, allowing for real-time updates based on ISBN. - Introduced a `rebuild_book_entries` function in `libraryManager.py` to facilitate user-driven updates of book entries from the Google Books API. - Enhanced error handling and user prompts for better interaction during book updates. - Updated `public/index.html` and `public/script.js` to improve the user interface and support new functionalities. - Modified styles in `public/styles.css` to enhance the layout and usability of the checkout button and location prompt.
245 lines
11 KiB
Python
245 lines
11 KiB
Python
import requests
|
|
from rich.console import Console
|
|
from rich.prompt import Prompt
|
|
from rich.table import Table
|
|
from fuzzywuzzy import process
|
|
from requests.auth import HTTPBasicAuth
|
|
|
|
API_BASE_URL = "https://localhost:3000" # Replace with your actual API base URL
|
|
console = Console()
|
|
|
|
# Use environment variables or a secure method to store credentials
|
|
USERNAME = 'admin'
|
|
PASSWORD = 'library@123' # Replace with your actual password
|
|
|
|
def list_books():
|
|
response = requests.get(f"{API_BASE_URL}/api/books-with-images", verify=False)
|
|
if response.status_code == 200:
|
|
books = response.json()
|
|
table = Table(title="Books")
|
|
table.add_column("ISBN", justify="right", style="cyan", no_wrap=True)
|
|
table.add_column("Title", style="magenta")
|
|
table.add_column("Authors", style="green")
|
|
for book in books:
|
|
table.add_row(book['isbn'], book['title'], book['authors'])
|
|
console.print(table)
|
|
else:
|
|
console.print("Failed to fetch books.", style="bold red")
|
|
|
|
def add_book():
|
|
isbn = Prompt.ask("Enter ISBN")
|
|
title = Prompt.ask("Enter Title")
|
|
authors = Prompt.ask("Enter Authors")
|
|
data = {
|
|
"isbn": isbn,
|
|
"title": title,
|
|
"authors": authors
|
|
}
|
|
response = requests.post(
|
|
f"{API_BASE_URL}/store-book",
|
|
json=data,
|
|
verify=False,
|
|
auth=HTTPBasicAuth(USERNAME, PASSWORD)
|
|
)
|
|
if response.status_code == 200:
|
|
console.print("Book added successfully.", style="bold green")
|
|
else:
|
|
console.print("Failed to add book.", style="bold red")
|
|
|
|
def remove_book():
|
|
isbn = Prompt.ask("Enter ISBN of the book to remove")
|
|
response = requests.delete(
|
|
f"{API_BASE_URL}/book/{isbn}",
|
|
verify=False,
|
|
auth=HTTPBasicAuth(USERNAME, PASSWORD)
|
|
)
|
|
if response.status_code == 200:
|
|
console.print("Book removed successfully.", style="bold green")
|
|
else:
|
|
console.print("Failed to remove book.", style="bold red")
|
|
|
|
def change_book_status():
|
|
isbn = Prompt.ask("Enter ISBN of the book to change status")
|
|
status = Prompt.ask("Enter new status (e.g., Available, Checked Out)")
|
|
data = {"status": status}
|
|
response = requests.put(
|
|
f"{API_BASE_URL}/book/{isbn}",
|
|
json=data,
|
|
verify=False,
|
|
auth=HTTPBasicAuth(USERNAME, PASSWORD)
|
|
)
|
|
if response.status_code == 200:
|
|
console.print("Book status updated successfully.", style="bold green")
|
|
else:
|
|
console.print("Failed to update book status.", style="bold red")
|
|
|
|
def search_books():
|
|
query = Prompt.ask("Enter search query")
|
|
response = requests.get(f"{API_BASE_URL}/api/books-with-images", verify=False)
|
|
if response.status_code == 200:
|
|
books = response.json()
|
|
book_titles = [book['title'] for book in books]
|
|
matches = process.extract(query, book_titles, limit=5)
|
|
|
|
if matches:
|
|
table = Table(title="Search Results")
|
|
table.add_column("ISBN", justify="right", style="cyan", no_wrap=True)
|
|
table.add_column("Title", style="magenta")
|
|
table.add_column("Authors", style="green")
|
|
table.add_column("Score", justify="right", style="yellow")
|
|
|
|
for match in matches:
|
|
title, score = match
|
|
book = next(book for book in books if book['title'] == title)
|
|
table.add_row(book['isbn'], book['title'], book['authors'], str(score))
|
|
|
|
console.print(table)
|
|
else:
|
|
console.print("No matches found.", style="bold red")
|
|
else:
|
|
console.print("Failed to fetch books.", style="bold red")
|
|
|
|
def list_books_on_loan():
|
|
response = requests.get(f"{API_BASE_URL}/api/books-on-loan", verify=False)
|
|
if response.status_code == 200:
|
|
loans = response.json()
|
|
table = Table(title="Books on Loan")
|
|
table.add_column("ISBN", justify="right", style="cyan", no_wrap=True)
|
|
table.add_column("Title", style="magenta")
|
|
table.add_column("User Name", style="blue")
|
|
table.add_column("Checkout Date", style="green")
|
|
table.add_column("Return Date", style="yellow")
|
|
|
|
for loan in loans:
|
|
table.add_row(loan['isbn'], loan['title'], loan['name'], loan['checkout_date'], loan['return_date'])
|
|
|
|
console.print(table)
|
|
else:
|
|
console.print("Failed to fetch books on loan.", style="bold red")
|
|
|
|
def rebuild_book_entries():
|
|
isbn = Prompt.ask("Enter ISBN of the book to rebuild")
|
|
response = requests.get(f"{API_BASE_URL}/book/confirm/{isbn}", verify=False)
|
|
|
|
if response.status_code == 200:
|
|
book_data = response.json()
|
|
console.print("Current Book Data from Local Database:", style="bold blue")
|
|
console.print(f"Title: {book_data['data']['title']}")
|
|
console.print(f"Authors: {book_data['data']['authors']}")
|
|
console.print(f"Published Date: {book_data['data'].get('publishedDate', 'N/A')}")
|
|
console.print(f"Description: {book_data['data'].get('description', 'N/A')}")
|
|
|
|
# Allow the user to select fields to search with
|
|
console.print("\nSelect fields to search the Google Books API with:")
|
|
use_title = Prompt.ask("Use Title? (yes/no)", choices=["yes", "no"], default="yes") == "yes"
|
|
use_authors = Prompt.ask("Use Authors? (yes/no)", choices=["yes", "no"], default="yes") == "yes"
|
|
use_isbn = Prompt.ask("Use ISBN? (yes/no)", choices=["yes", "no"], default="yes") == "yes"
|
|
|
|
# Build search query based on selected fields
|
|
query_components = []
|
|
if use_title:
|
|
query_components.append(f'intitle:{book_data["data"]["title"]}')
|
|
if use_authors:
|
|
query_components.append(f'inauthor:{book_data["data"]["authors"]}')
|
|
if use_isbn:
|
|
query_components.append(f'isbn:{book_data["data"]["isbn"]}')
|
|
query = '+'.join(query_components)
|
|
|
|
# Query the Google Books API directly
|
|
google_response = requests.get(f"https://www.googleapis.com/books/v1/volumes?q={query}")
|
|
if google_response.status_code == 200:
|
|
google_data = google_response.json()
|
|
if google_data["totalItems"] > 0:
|
|
# Take the first result
|
|
volumes = google_data["items"]
|
|
console.print("\nMultiple volumes found. Please select one:")
|
|
for i, item in enumerate(volumes):
|
|
volume_info = item["volumeInfo"]
|
|
title = volume_info.get('title', 'N/A')
|
|
authors = ', '.join(volume_info.get('authors', []))
|
|
published_date = volume_info.get('publishedDate', 'N/A')
|
|
|
|
# Limit the length of the title and authors to 32 characters
|
|
if len(title) > 32:
|
|
title = title[:29] + '...'
|
|
if len(authors) > 32:
|
|
authors = authors[:29] + '...'
|
|
|
|
console.print(f"{i + 1}. Title: {title}, Authors: {authors}, Published Date: {published_date}")
|
|
|
|
selected_index = int(Prompt.ask("Enter the number of the volume you want to select")) - 1
|
|
volume_info = volumes[selected_index]["volumeInfo"]
|
|
console.print("\nBook Data Found from Google Books API:", style="bold green")
|
|
console.print(f"Title: {volume_info.get('title', 'N/A')}")
|
|
console.print(f"Authors: {', '.join(volume_info.get('authors', []))}")
|
|
console.print(f"Published Date: {volume_info.get('publishedDate', 'N/A')}")
|
|
console.print(f"Description: {volume_info.get('description', 'N/A')}")
|
|
# isbn (use 13, then 10 if 13 is not present) ISBN: [{'type': 'ISBN_10', 'identifier': '0521274559'}, {'type': 'ISBN_13', 'identifier': '9780521274555'}]
|
|
isbn = volume_info.get('industryIdentifiers', [])
|
|
isbn = next((identifier['identifier'] for identifier in isbn if identifier['type'] == 'ISBN_13'), None)
|
|
if not isbn:
|
|
isbn = next((identifier['identifier'] for identifier in isbn if identifier['type'] == 'ISBN_10'), None)
|
|
console.print(f"ISBN: {isbn}")
|
|
|
|
# Ask the user if they want to update the entry
|
|
update = Prompt.ask("Do you want to update this book entry? (yes/no)", choices=["yes", "no"], default="no")
|
|
if update == "yes":
|
|
data = {
|
|
"title": volume_info.get('title'),
|
|
"authors": ', '.join(volume_info.get('authors', [])),
|
|
"publishedDate": volume_info.get('publishedDate'),
|
|
"description": volume_info.get('description'),
|
|
"isbn": isbn
|
|
}
|
|
update_response = requests.put(
|
|
f"{API_BASE_URL}/book/{book_data['data']['isbn']}",
|
|
json=data,
|
|
verify=False,
|
|
auth=HTTPBasicAuth(USERNAME, PASSWORD)
|
|
)
|
|
if update_response.status_code == 200:
|
|
console.print("Book entry updated successfully.", style="bold green")
|
|
else:
|
|
console.print("Failed to update book entry.", style="bold red")
|
|
print(update_response.json())
|
|
print(update_response.status_code)
|
|
else:
|
|
console.print("No data found on Google Books API.", style="bold red")
|
|
else:
|
|
console.print("Failed to fetch data from Google Books API.", style="bold red")
|
|
else:
|
|
console.print("Book not found in local database.", style="bold red")
|
|
|
|
def main():
|
|
while True:
|
|
console.print("\n[bold]Admin Console[/bold]")
|
|
console.print("1. List Books")
|
|
console.print("2. Add Book")
|
|
console.print("3. Remove Book")
|
|
console.print("4. Change Book Status")
|
|
console.print("5. Search Books")
|
|
console.print("6. List Books on Loan")
|
|
console.print("7. Rebuild Book Entries")
|
|
console.print("8. Exit")
|
|
choice = Prompt.ask("Choose an option", choices=["1", "2", "3", "4", "5", "6", "7", "8"], default="8")
|
|
|
|
if choice == "1":
|
|
list_books()
|
|
elif choice == "2":
|
|
add_book()
|
|
elif choice == "3":
|
|
remove_book()
|
|
elif choice == "4":
|
|
change_book_status()
|
|
elif choice == "5":
|
|
search_books()
|
|
elif choice == "6":
|
|
list_books_on_loan()
|
|
elif choice == "7":
|
|
rebuild_book_entries()
|
|
elif choice == "8":
|
|
console.print("Exiting...", style="bold yellow")
|
|
break
|
|
|
|
if __name__ == "__main__":
|
|
main() |