from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List from datetime import date from uuid import uuid4 app = FastAPI() class Book(BaseModel): title: str author: str isbn: str copies: int class BookResponse(Book): available_copies: int class Customer(BaseModel): name: str email: str customer_id: str class CheckoutRequest(BaseModel): isbn: str customer_id: str due_date: str class CheckoutResponse(BaseModel): checkout_id: str isbn: str title: str customer_id: str checkout_date: str due_date: str class ReturnRequest(BaseModel): isbn: str customer_id: str books = {} customers = {} checkouts = [] def get_book(isbn: str): if isbn not in books: raise HTTPException(status_code=404, detail="Book not found") return books[isbn] def get_customer(customer_id: str): if customer_id not in customers: raise HTTPException(status_code=404, detail="Customer not found") return customers[customer_id] def enforce_business_rules(customer_id: str, isbn: str): customer_books = [c for c in checkouts if c["customer_id"] == customer_id] if len(customer_books) >= 5: raise HTTPException(status_code=400, detail="Customer has too many books checked out") if books[isbn]["available_copies"] <= 0: raise HTTPException(status_code=400, detail="No copies available") @app.post("/api/books", response_model=BookResponse, status_code=201) def add_book(book: Book): if book.isbn in books: raise HTTPException(status_code=400, detail="Book already exists") books[book.isbn] = book.dict() books[book.isbn]["available_copies"] = book.copies return books[book.isbn] @app.get("/api/books/{isbn}", response_model=BookResponse) def get_book_details(isbn: str): return get_book(isbn) @app.post("/api/customers", response_model=Customer, status_code=201) def create_customer(customer: Customer): if customer.customer_id in customers: raise HTTPException(status_code=400, detail="Customer already exists") customers[customer.customer_id] = customer.dict() return customer @app.get("/api/customers/{customer_id}", response_model=Customer) def get_customer_details(customer_id: str): return get_customer(customer_id) @app.post("/api/checkouts", response_model=CheckoutResponse, status_code=201) def checkout_book(request: CheckoutRequest): book = get_book(request.isbn) get_customer(request.customer_id) enforce_business_rules(request.customer_id, request.isbn) checkout_id = "CKO" + uuid4().hex[:6].upper() checkout_date = date.today().isoformat() books[request.isbn]["available_copies"] -= 1 checkout = { "checkout_id": checkout_id, "isbn": request.isbn, "title": book["title"], "customer_id": request.customer_id, "checkout_date": checkout_date, "due_date": request.due_date } checkouts.append(checkout) return checkout @app.post("/api/returns") def return_book(request: ReturnRequest): for i, c in enumerate(checkouts): if c["isbn"] == request.isbn and c["customer_id"] == request.customer_id: checkouts.pop(i) books[request.isbn]["available_copies"] += 1 return { "message": "Book returned successfully", "isbn": request.isbn, "customer_id": request.customer_id, "return_date": date.today().isoformat() } raise HTTPException(status_code=404, detail="Borrow record not found") @app.get("/api/customers/{customer_id}/books") def get_customer_books(customer_id: str): get_customer(customer_id) return [ { "isbn": c["isbn"], "title": c["title"], "author": books[c["isbn"]]["author"], "checkout_date": c["checkout_date"], "due_date": c["due_date"] } for c in checkouts if c["customer_id"] == customer_id ] @app.post("/api/reset") def reset_system(): books.clear() customers.clear() checkouts.clear() return {"message": "System reset successful"}