from typing import Optional, Dict, Union, List 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 (ISBN-10 or ISBN-13). Args: isbn: ISBN string to validate Returns: bool: True if valid ISBN format, False otherwise """ isbn = isbn.replace("-", "").replace(" ", "") if len(isbn) == 10: # ISBN-10 validation if not isbn[:-1].isdigit() and isbn[-1].lower() != 'x': return False total = 0 for i in range(9): total += int(isbn[i]) * (10 - i) check = 11 - (total % 11) return str(check) == isbn[-1] or (check == 10 and isbn[-1].lower() == 'x') elif len(isbn) == 13: # ISBN-13 validation if not isbn.isdigit(): return False total = 0 for i in range(12): if i % 2 == 0: total += int(isbn[i]) else: total += int(isbn[i]) * 3 check = 10 - (total % 10) if check == 10: check = 0 return str(check) == isbn[-1] 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_by_title(db: Session, title: str, limit: int = 10) -> List[Book]: """ Search for books by title using partial matching. Args: db: Database session title: Title to search for limit: Maximum number of results to return Returns: List of matching Book objects """ return db.query(Book).filter(Book.title.ilike(f"%{title}%")).limit(limit).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 required fields if not book_data.title or not book_data.title.strip(): return {"error": "Title is required"} if not book_data.author or not book_data.author.strip(): return {"error": "Author is required"} # Validate ISBN if not validate_isbn(book_data.isbn): return {"error": "Invalid ISBN format"} # Check for duplicate ISBN existing_book = get_book_by_isbn(db, book_data.isbn) if existing_book: return {"error": "Book with this ISBN already exists"} # Create the book db_book = Book( title=book_data.title.strip(), author=book_data.author.strip(), description=book_data.description.strip() if book_data.description else None, isbn=book_data.isbn.replace("-", "").replace(" ", "") ) db.add(db_book) db.commit() db.refresh(db_book) return db_book def format_book_response(book: Book) -> Dict[str, str]: """ Format a book object into a standardized response format. Args: book: Book object to format Returns: Dictionary with formatted book data """ return { "title": book.title, "author": book.author, "description": book.description or "", "isbn": f"{book.isbn[:3]}-{book.isbn[3:4]}-{book.isbn[4:6]}-{book.isbn[6:12]}-{book.isbn[12:]}" if len(book.isbn) == 13 else f"{book.isbn[:1]}-{book.isbn[1:3]}-{book.isbn[3:9]}-{book.isbn[9:]}" }