From d0beabc78c9cb1f23b270e4d025190ea40b28df6 Mon Sep 17 00:00:00 2001 From: Backend IM Bot Date: Thu, 27 Mar 2025 10:30:03 +0000 Subject: [PATCH] Add helper functions for Book --- helpers/book_helpers.py | 156 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 helpers/book_helpers.py diff --git a/helpers/book_helpers.py b/helpers/book_helpers.py new file mode 100644 index 0000000..e985def --- /dev/null +++ b/helpers/book_helpers.py @@ -0,0 +1,156 @@ +from typing import List, Dict, Optional, Union, Any +from datetime import datetime +import re +from sqlalchemy.orm import Session +from models.book import Book +from schemas.book import BookCreate, BookUpdate + +def validate_isbn(isbn: str) -> bool: + """ + Validate ISBN format (both ISBN-10 and ISBN-13). + + Args: + isbn: ISBN string to validate + + Returns: + bool: True if ISBN format is valid, False otherwise + """ + # Remove hyphens and spaces + isbn = isbn.replace('-', '').replace(' ', '') + + if len(isbn) == 10: + # ISBN-10 validation + if not isbn[:-1].isdigit(): + return False + check = sum((10 - i) * int(x) for i, x in enumerate(isbn[:9])) + check = (11 - (check % 11)) % 11 + return str(check) == isbn[-1] or (check == 10 and isbn[-1].upper() == 'X') + + elif len(isbn) == 13: + # ISBN-13 validation + if not isbn.isdigit(): + return False + check = sum((3 if i % 2 else 1) * int(x) for i, x in enumerate(isbn[:12])) + check = (10 - (check % 10)) % 10 + return int(isbn[-1]) == check + + return False + +def get_book_by_isbn(db: Session, isbn: str) -> Optional[Book]: + """ + Retrieve a book by its ISBN. + + Args: + db: Database session + isbn: ISBN to search for + + Returns: + Book object if found, None otherwise + """ + return db.query(Book).filter(Book.isbn == isbn).first() + +def search_books( + db: Session, + title: Optional[str] = None, + author: Optional[str] = None, + publisher: Optional[str] = None, + year_from: Optional[int] = None, + year_to: Optional[int] = None, + available_only: bool = False +) -> List[Book]: + """ + Search books with multiple optional filters. + + Args: + db: Database session + title: Optional title search string + author: Optional author search string + publisher: Optional publisher search string + year_from: Optional start year for publication + year_to: Optional end year for publication + available_only: If True, return only available books + + Returns: + List of Book objects matching the search criteria + """ + query = db.query(Book) + + if title: + query = query.filter(Book.title.ilike(f"%{title}%")) + if author: + query = query.filter(Book.author.ilike(f"%{author}%")) + if publisher: + query = query.filter(Book.publisher.ilike(f"%{publisher}%")) + if year_from: + query = query.filter(Book.publication_year >= year_from) + if year_to: + query = query.filter(Book.publication_year <= year_to) + if available_only: + query = query.filter(Book.is_available == True) + + return query.all() + +def create_book_safely(db: Session, book_data: BookCreate) -> Union[Book, Dict[str, str]]: + """ + Create a new book with validation and error handling. + + Args: + db: Database session + book_data: Book data for creation + + Returns: + Book object if created successfully, error dict otherwise + """ + # Validate ISBN + if not validate_isbn(book_data.isbn): + return {"error": "Invalid ISBN format"} + + # Check if book already exists + existing_book = get_book_by_isbn(db, book_data.isbn) + if existing_book: + return {"error": "ISBN already exists"} + + # Validate publication year + current_year = datetime.now().year + if book_data.publication_year > current_year: + return {"error": "Publication year cannot be in the future"} + + # Create the book + db_book = Book( + title=book_data.title, + author=book_data.author, + isbn=book_data.isbn, + description=book_data.description, + publication_year=book_data.publication_year, + pages=book_data.pages, + publisher=book_data.publisher, + is_available=book_data.is_available + ) + + db.add(db_book) + db.commit() + db.refresh(db_book) + + return db_book + +def update_book_availability(db: Session, isbn: str, is_available: bool) -> Union[Book, Dict[str, str]]: + """ + Update the availability status of a book. + + Args: + db: Database session + isbn: ISBN of the book to update + is_available: New availability status + + Returns: + Updated Book object if successful, error dict otherwise + """ + book = get_book_by_isbn(db, isbn) + if not book: + return {"error": "Book not found"} + + book.is_available = is_available + db.commit() + db.refresh(book) + + return book \ No newline at end of file