from typing import List, Dict, Optional, Union, Any from datetime import datetime import re from sqlalchemy.orm import Session from pydantic import ValidationError def validate_isbn(isbn: str) -> bool: """ Validate ISBN format (supports 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_digit = 'X' if isbn[-1].upper() == 'X' else isbn[-1] if not (check_digit.isdigit() or check_digit == 'X'): return False return True elif len(isbn) == 13: # ISBN-13 validation if not isbn.isdigit(): return False return isbn.startswith('978') or isbn.startswith('979') return False def format_book_price(price: int) -> str: """ Format book price from cents to currency string. Args: price: Price in cents Returns: str: Formatted price string """ return f"${price/100:.2f}" def get_books_by_author(db: Session, author: str) -> List[Any]: """ Get all books by a specific author. Args: db: Database session author: Author name to search for Returns: List of Book objects by the author """ return db.query(Book).filter(Book.author == author).all() def search_books( db: Session, search_term: str, include_unavailable: bool = False ) -> List[Any]: """ Search books by title, author, or ISBN. Args: db: Database session search_term: Term to search for include_unavailable: Whether to include unavailable books Returns: List of matching Book objects """ query = db.query(Book).filter( (Book.title.ilike(f"%{search_term}%")) | (Book.author.ilike(f"%{search_term}%")) | (Book.isbn == search_term) ) if not include_unavailable: query = query.filter(Book.is_available == True) return query.all() def validate_publication_year(year: int) -> bool: """ Validate if publication year is within acceptable range. Args: year: Publication year to validate Returns: bool: True if year is valid, False otherwise """ current_year = datetime.now().year return 1800 <= year <= current_year + 1 def process_book_data(book_data: Dict[str, Any]) -> Dict[str, Any]: """ Process and validate book data before creation/update. Args: book_data: Dictionary containing book data Returns: Dict with processed and validated data """ processed_data = book_data.copy() # Validate ISBN if 'isbn' in processed_data and not validate_isbn(processed_data['isbn']): raise ValidationError("Invalid ISBN format") # Validate publication year if 'publication_year' in processed_data: if not validate_publication_year(processed_data['publication_year']): raise ValidationError("Invalid publication year") # Ensure title and author are stripped of whitespace if 'title' in processed_data: processed_data['title'] = processed_data['title'].strip() if 'author' in processed_data: processed_data['author'] = processed_data['author'].strip() return processed_data