164 lines
5.1 KiB
Python
164 lines
5.1 KiB
Python
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 (supports both ISBN-10 and 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 search_books(
|
|
db: Session,
|
|
title: Optional[str] = None,
|
|
author: Optional[str] = None,
|
|
genre: Optional[str] = None,
|
|
publisher: Optional[str] = None,
|
|
available_only: bool = False
|
|
) -> List[Book]:
|
|
"""
|
|
Search books with multiple optional filters.
|
|
|
|
Args:
|
|
db: Database session
|
|
title: Optional title to search for
|
|
author: Optional author to search for
|
|
genre: Optional genre to filter by
|
|
publisher: Optional publisher to filter by
|
|
available_only: If True, return only available books
|
|
|
|
Returns:
|
|
List of books 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 genre:
|
|
query = query.filter(Book.genre == genre)
|
|
if publisher:
|
|
query = query.filter(Book.publisher == publisher)
|
|
if available_only:
|
|
query = query.filter(Book.is_available == True)
|
|
|
|
return query.all()
|
|
|
|
def get_book_stats(db: Session) -> Dict[str, Any]:
|
|
"""
|
|
Get statistical information about books in the database.
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
Dictionary containing various statistics about books
|
|
"""
|
|
total_books = db.query(Book).count()
|
|
available_books = db.query(Book).filter(Book.is_available == True).count()
|
|
genres = db.query(Book.genre).distinct().all()
|
|
languages = db.query(Book.language).distinct().all()
|
|
avg_pages = db.query(func.avg(Book.page_count)).scalar()
|
|
|
|
return {
|
|
"total_books": total_books,
|
|
"available_books": available_books,
|
|
"total_genres": len(genres),
|
|
"genres": [g[0] for g in genres],
|
|
"languages": [l[0] for l in languages],
|
|
"average_page_count": round(avg_pages if avg_pages else 0, 2)
|
|
}
|
|
|
|
def validate_book_data(book_data: Union[BookCreate, BookUpdate]) -> Dict[str, str]:
|
|
"""
|
|
Validate book data before creation or update.
|
|
|
|
Args:
|
|
book_data: Book data to validate
|
|
|
|
Returns:
|
|
Dictionary of validation errors, empty if validation passes
|
|
"""
|
|
errors = {}
|
|
|
|
if hasattr(book_data, 'isbn') and book_data.isbn:
|
|
if not validate_isbn(book_data.isbn):
|
|
errors['isbn'] = "Invalid ISBN format"
|
|
|
|
if hasattr(book_data, 'publication_year'):
|
|
current_year = datetime.now().year
|
|
if book_data.publication_year > current_year:
|
|
errors['publication_year'] = "Publication year cannot be in the future"
|
|
|
|
if hasattr(book_data, 'page_count'):
|
|
if book_data.page_count <= 0:
|
|
errors['page_count'] = "Page count must be positive"
|
|
|
|
return errors
|
|
|
|
def get_similar_books(db: Session, book_id: int, limit: int = 5) -> List[Book]:
|
|
"""
|
|
Find similar books based on genre and author.
|
|
|
|
Args:
|
|
db: Database session
|
|
book_id: ID of the reference book
|
|
limit: Maximum number of similar books to return
|
|
|
|
Returns:
|
|
List of similar books
|
|
"""
|
|
reference_book = db.query(Book).filter(Book.id == book_id).first()
|
|
if not reference_book:
|
|
return []
|
|
|
|
similar_books = db.query(Book).filter(
|
|
Book.id != book_id,
|
|
Book.genre == reference_book.genre,
|
|
Book.author == reference_book.author
|
|
).limit(limit).all()
|
|
|
|
if len(similar_books) < limit:
|
|
# If not enough books by same author, add books from same genre
|
|
additional_books = db.query(Book).filter(
|
|
Book.id != book_id,
|
|
Book.genre == reference_book.genre,
|
|
Book.author != reference_book.author
|
|
).limit(limit - len(similar_books)).all()
|
|
similar_books.extend(additional_books)
|
|
|
|
return similar_books |