Refactoring and adding API connection

parent 7209211d
from sqlalchemy.orm import Session
from fastapi import HTTPException
from .models import BookDB
from .schemas import BookCreate, Book
def create_book(db: Session, book: BookCreate):
db_book = BookDB(**book.dict())
try:
db.add(db_book)
db.commit()
db.refresh(db_book)
return db_book
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def get_books(db: Session, skip: int = 0, limit: int = 100):
return db.query(BookDB).offset(skip).limit(limit).all()
def get_book(db: Session, book_id: int):
db_book = db.query(BookDB).filter(BookDB.id == book_id).first()
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
return db_book
def update_book(db: Session, book_id: int, book: BookCreate):
db_book = db.query(BookDB).filter(BookDB.id == book_id).first()
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
for key, value in book.dict().items():
setattr(db_book, key, value)
try:
db.commit()
db.refresh(db_book)
return db_book
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def delete_book(db: Session, book_id: int):
db_book = db.query(BookDB).filter(BookDB.id == book_id).first()
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
try:
db.delete(db_book)
db.commit()
return {"message": "Book deleted successfully"}
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
\ No newline at end of file
from sqlalchemy import Column, Integer, String
from database import Base
class BookDB(Base):
__tablename__ = "books"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
author = Column(String, index=True)
owner = Column(Integer)
\ No newline at end of file
from pydantic import BaseModel
from typing import Optional
class BookBase(BaseModel):
title: str
author: Optional[str] = None
owner: int
class BookCreate(BookBase):
pass
class Book(BookBase):
id: int
class Config:
from_attributes = True
\ No newline at end of file
from sqlalchemy.orm import Session
from fastapi import HTTPException
from datetime import date
from .models import LoanDB
from .schemas import LoanCreate, Loan
from book.models import BookDB
def create_loan(db: Session, loan: LoanCreate):
db_loan = LoanDB(**loan.dict())
try:
db.add(db_loan)
db.commit()
db.refresh(db_loan)
return db_loan
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def get_loans(db: Session, skip: int = 0, limit: int = 100):
return db.query(LoanDB).offset(skip).limit(limit).all()
def get_loan(db: Session, user_id: int, book_id: int, start_date: date):
db_loan = db.query(LoanDB).filter(
LoanDB.id_user == user_id,
LoanDB.id_book == book_id,
LoanDB.start == start_date
).first()
if db_loan is None:
raise HTTPException(status_code=404, detail="Loan not found")
return db_loan
def update_loan(db: Session, user_id: int, book_id: int, start_date: date, loan: LoanCreate):
db_loan = db.query(LoanDB).filter(
LoanDB.id_user == user_id,
LoanDB.id_book == book_id,
LoanDB.start == start_date
).first()
if db_loan is None:
raise HTTPException(status_code=404, detail="Loan not found")
for key, value in loan.dict().items():
setattr(db_loan, key, value)
try:
db.commit()
db.refresh(db_loan)
return db_loan
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def delete_loan(db: Session, user_id: int, book_id: int, start_date: date):
db_loan = db.query(LoanDB).filter(
LoanDB.id_user == user_id,
LoanDB.id_book == book_id,
LoanDB.start == start_date
).first()
if db_loan is None:
raise HTTPException(status_code=404, detail="Loan not found")
try:
db.delete(db_loan)
db.commit()
return {"message": "Loan deleted successfully"}
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def get_available_books(db: Session):
subquery = db.query(LoanDB.id_book).filter(LoanDB.status != 'ended').subquery()
available_books = db.query(BookDB).filter(BookDB.id.notin_(subquery)).all()
return available_books
\ No newline at end of file
from sqlalchemy import Column, Integer, Date, Enum
from database import Base
from .schemas import LoanStatus
class LoanDB(Base):
__tablename__ = "loans"
id_user = Column(Integer, primary_key=True, index=True)
id_book = Column(Integer, primary_key=True, index=True)
status = Column(Enum(LoanStatus), default=LoanStatus.reserved)
start = Column(Date, nullable=True)
end = Column(Date, nullable=True)
\ No newline at end of file
from pydantic import BaseModel
from datetime import date
from typing import Optional
from enum import Enum
class LoanStatus(str, Enum):
reserved = "reserved"
borrowed = "borrowed"
returned = "returned"
class LoanBase(BaseModel):
id_user: int
id_book: int
status: LoanStatus = LoanStatus.reserved
start: Optional[date] = None
end: Optional[date] = None
class LoanCreate(LoanBase):
pass
class Loan(LoanBase):
class Config:
from_attributes = True
\ No newline at end of file
from typing import List from fastapi import FastAPI, Depends
from datetime import date from datetime import date
from fastapi import FastAPI, HTTPException, Depends from typing import List
from passlib.context import CryptContext
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from typing import Optional from database import get_db
from pydantic import BaseModel, EmailStr from user import crud as user_crud, schemas as user_schemas
from datetime import date from book import crud as book_crud, schemas as book_schemas
from database import * from loan import crud as loan_crud, schemas as loan_schemas
from book.schemas import Book # Import Book schema
# Pydantic Models
class UserBase(BaseModel):
email: EmailStr
role: int = 0
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
from_attributes = True
class BookBase(BaseModel):
title: str
author: Optional[str] = None
owner: int
class BookCreate(BookBase):
pass
class Book(BookBase):
id: int
class Config:
from_attributes = True
class LoanBase(BaseModel):
id_user: int
id_book: int
status: LoanStatus = LoanStatus.reserved
start: Optional[date] = None
end: Optional[date] = None
class LoanCreate(LoanBase):
pass
class Loan(LoanBase):
class Config:
from_attributes = True
# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
app = FastAPI() app = FastAPI()
# User CRUD Operations # User CRUD Operations
@app.post("/users/", response_model=User) @app.post("/users/", response_model=user_schemas.User)
def create_user(user: UserCreate, db: Session = Depends(get_db)): def create_user(user: user_schemas.UserCreate, db: Session = Depends(get_db)):
db_user = db.query(UserDB).filter(UserDB.email == user.email).first() return user_crud.create_user(db, user)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
hashed_password = pwd_context.hash(user.password)
db_user = UserDB(
email=user.email,
password=hashed_password,
role=user.role
)
try:
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
@app.get("/users/", response_model=List[User]) @app.get("/users/", response_model=List[user_schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = db.query(UserDB).offset(skip).limit(limit).all() return user_crud.get_users(db, skip, limit)
return users
@app.get("/users/{user_id}", response_model=User) @app.get("/users/{user_id}", response_model=user_schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)): def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = db.query(UserDB).filter(UserDB.id == user_id).first() return user_crud.get_user(db, user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.put("/users/{user_id}", response_model=User) @app.put("/users/{user_id}", response_model=user_schemas.User)
def update_user(user_id: int, user: UserCreate, db: Session = Depends(get_db)): def update_user(user_id: int, user: user_schemas.UserCreate, db: Session = Depends(get_db)):
db_user = db.query(UserDB).filter(UserDB.id == user_id).first() return user_crud.update_user(db, user_id, user)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
# Update email if it's changed and not already taken
if user.email != db_user.email:
existing_user = db.query(UserDB).filter(UserDB.email == user.email).first()
if existing_user:
raise HTTPException(status_code=400, detail="Email already registered")
db_user.email = user.email
# Update password
db_user.password = pwd_context.hash(user.password)
db_user.role = user.role
try:
db.commit()
db.refresh(db_user)
return db_user
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/users/{user_id}") @app.delete("/users/{user_id}")
def delete_user(user_id: int, db: Session = Depends(get_db)): def delete_user(user_id: int, db: Session = Depends(get_db)):
db_user = db.query(UserDB).filter(UserDB.id == user_id).first() return user_crud.delete_user(db, user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
try:
db.delete(db_user)
db.commit()
return {"message": "User deleted successfully"}
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
# Book CRUD Operations # Book CRUD Operations
@app.post("/books/", response_model=Book) @app.post("/books/", response_model=book_schemas.Book)
def create_book(book: BookCreate, db: Session = Depends(get_db)): def create_book(book: book_schemas.BookCreate, db: Session = Depends(get_db)):
db_book = BookDB(**book.dict()) return book_crud.create_book(db, book)
try:
db.add(db_book)
db.commit()
db.refresh(db_book)
return db_book
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
@app.get("/books/", response_model=List[Book]) @app.get("/books/", response_model=List[book_schemas.Book])
def read_books(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): def read_books(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
books = db.query(BookDB).offset(skip).limit(limit).all() return book_crud.get_books(db, skip, limit)
return books
@app.get("/books/{book_id}", response_model=Book) @app.get("/books/{book_id}", response_model=book_schemas.Book)
def read_book(book_id: int, db: Session = Depends(get_db)): def read_book(book_id: int, db: Session = Depends(get_db)):
db_book = db.query(BookDB).filter(BookDB.id == book_id).first() return book_crud.get_book(db, book_id)
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
return db_book
@app.put("/books/{book_id}", response_model=Book) @app.put("/books/{book_id}", response_model=book_schemas.Book)
def update_book(book_id: int, book: BookCreate, db: Session = Depends(get_db)): def update_book(book_id: int, book: book_schemas.BookCreate, db: Session = Depends(get_db)):
db_book = db.query(BookDB).filter(BookDB.id == book_id).first() return book_crud.update_book(db, book_id, book)
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
for key, value in book.dict().items():
setattr(db_book, key, value)
try:
db.commit()
db.refresh(db_book)
return db_book
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/books/{book_id}") @app.delete("/books/{book_id}")
def delete_book(book_id: int, db: Session = Depends(get_db)): def delete_book(book_id: int, db: Session = Depends(get_db)):
db_book = db.query(BookDB).filter(BookDB.id == book_id).first() return book_crud.delete_book(db, book_id)
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
try:
db.delete(db_book)
db.commit()
return {"message": "Book deleted successfully"}
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
# Loan CRUD Operations # Loan CRUD Operations
@app.post("/loans/", response_model=Loan) @app.post("/loans/", response_model=loan_schemas.Loan)
def create_loan(loan: LoanCreate, db: Session = Depends(get_db)): def create_loan(loan: loan_schemas.LoanCreate, db: Session = Depends(get_db)):
db_loan = LoanDB(**loan.dict()) return loan_crud.create_loan(db, loan)
try:
db.add(db_loan)
db.commit()
db.refresh(db_loan)
return db_loan
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
@app.get("/loans/", response_model=List[Loan]) @app.get("/loans/", response_model=List[loan_schemas.Loan])
def read_loans(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): def read_loans(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
loans = db.query(LoanDB).offset(skip).limit(limit).all() return loan_crud.get_loans(db, skip, limit)
return loans
@app.get("/loans/{user_id}/{book_id}/{start_date}", response_model=Loan) @app.get("/loans/{user_id}/{book_id}/{start_date}", response_model=loan_schemas.Loan)
def read_loan(user_id: int, book_id: int, start_date: date, db: Session = Depends(get_db)): def read_loan(user_id: int, book_id: int, start_date: date, db: Session = Depends(get_db)):
db_loan = db.query(LoanDB).filter( return loan_crud.get_loan(db, user_id, book_id, start_date)
LoanDB.id_user == user_id,
LoanDB.id_book == book_id,
LoanDB.start == start_date
).first()
if db_loan is None:
raise HTTPException(status_code=404, detail="Loan not found")
return db_loan
@app.put("/loans/{user_id}/{book_id}/{start_date}", response_model=Loan) @app.put("/loans/{user_id}/{book_id}/{start_date}", response_model=loan_schemas.Loan)
def update_loan( def update_loan(user_id: int, book_id: int, start_date: date, loan: loan_schemas.LoanCreate, db: Session = Depends(get_db)):
user_id: int, return loan_crud.update_loan(db, user_id, book_id, start_date, loan)
book_id: int,
start_date: date,
loan: LoanCreate,
db: Session = Depends(get_db)
):
db_loan = db.query(LoanDB).filter(
LoanDB.id_user == user_id,
LoanDB.id_book == book_id,
LoanDB.start == start_date
).first()
if db_loan is None:
raise HTTPException(status_code=404, detail="Loan not found")
for key, value in loan.dict().items():
setattr(db_loan, key, value)
try:
db.commit()
db.refresh(db_loan)
return db_loan
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/loans/{user_id}/{book_id}/{start_date}") @app.delete("/loans/{user_id}/{book_id}/{start_date}")
def delete_loan(user_id: int, book_id: int, start_date: date, db: Session = Depends(get_db)): def delete_loan(user_id: int, book_id: int, start_date: date, db: Session = Depends(get_db)):
db_loan = db.query(LoanDB).filter( return loan_crud.delete_loan(db, user_id, book_id, start_date)
LoanDB.id_user == user_id,
LoanDB.id_book == book_id, @app.get("/books/available", response_model=List[Book])
LoanDB.start == start_date def get_available_books(db: Session = Depends(get_db)):
).first() return loan_crud.get_available_books(db)
if db_loan is None: \ No newline at end of file
raise HTTPException(status_code=404, detail="Loan not found")
try:
db.delete(db_loan)
db.commit()
return {"message": "Loan deleted successfully"}
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
\ No newline at end of file
from sqlalchemy.orm import Session
from fastapi import HTTPException
from passlib.context import CryptContext
from .models import UserDB
from .schemas import UserCreate, User
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def create_user(db: Session, user: UserCreate):
db_user = db.query(UserDB).filter(UserDB.email == user.email).first()
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
hashed_password = pwd_context.hash(user.password)
db_user = UserDB(
email=user.email,
password=hashed_password,
role=user.role
)
try:
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def get_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(UserDB).offset(skip).limit(limit).all()
def get_user(db: Session, user_id: int):
db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
def update_user(db: Session, user_id: int, user: UserCreate):
db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
if user.email != db_user.email:
existing_user = db.query(UserDB).filter(UserDB.email == user.email).first()
if existing_user:
raise HTTPException(status_code=400, detail="Email already registered")
db_user.email = user.email
db_user.password = pwd_context.hash(user.password)
db_user.role = user.role
try:
db.commit()
db.refresh(db_user)
return db_user
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
def delete_user(db: Session, user_id: int):
db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
try:
db.delete(db_user)
db.commit()
return {"message": "User deleted successfully"}
except Exception as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e))
\ No newline at end of file
from sqlalchemy import Column, Integer, String
from database import Base
class UserDB(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
password = Column(String)
role = Column(Integer, default=0)
\ No newline at end of file
from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
email: EmailStr
role: int = 0
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
from_attributes = True
\ No newline at end of file
import BookList from "@/app/components/booklist"; import { useState, useEffect } from 'react';
import BookList from '../components/booklist';
export default function Home() { export default function Page() {
return ( const [books, setBooks] = useState([]);
<BookList/> const [loading, setLoading] = useState(true);
); const [error, setError] = useState(null);
useEffect(() => {
const fetchBooks = async () => {
try {
setLoading(true);
// Replace with your actual API endpoint
const response = await fetch('https://localhost:8000/books');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setBooks(data);
} catch (err) {
setError(err.message);
console.error('Error fetching books:', err);
} finally {
setLoading(false);
}
};
fetchBooks();
}, []);
if (loading) {
return (
<div className="flex justify-center items-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
</div>
);
}
if (error) {
return (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mx-auto max-w-md mt-10">
<p>Error loading books: {error}</p>
<p>Please try again later</p>
</div>
);
}
return <BookList books={books} />;
} }
\ No newline at end of file
import Navbar from './components/navbar'; import Navbar from '../components/navbar';
import './globals.css'; import './globals.css';
export const metadata = { export const metadata = {
......
// Grid layout component that fetches and displays books // Grid layout component that fetches and displays books
"use client"; "use client";
import { useState, useEffect } from 'react'; import PropTypes from 'prop-types';
import Book from "./book"; import Book from "./book";
export default function BookList () { export default function BookList ({ books }) {
const [books, setBooks] = useState([]); if (!books) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchBooks = async () => {
try {
setLoading(true);
/*
// Replace with your actual API endpoint
const response = await fetch('https://api.example.com/books');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();*/
const data = [
{id: 1, title: "Lord of the rings", author: "JRR Tolkien"},
{id: 2, title: "Artemisa", author: "Andy Weir"},
{id: 3, title: "Dune", author: "Orson Scott Card"}
];
setBooks(data);
} catch (err) {
setError(err.message);
console.error('Error fetching books:', err);
} finally {
setLoading(false);
}
};
fetchBooks();
}, []);
if (loading) {
return ( return (
<div className="flex justify-center items-center min-h-screen"> <div className="flex justify-center items-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div> <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
...@@ -46,11 +12,10 @@ export default function BookList () { ...@@ -46,11 +12,10 @@ export default function BookList () {
); );
} }
if (error) { if (books.length === 0) {
return ( return (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mx-auto max-w-md mt-10"> <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mx-auto max-w-md mt-10">
<p>Error loading books: {error}</p> <p>No books found.</p>
<p>Please try again later</p>
</div> </div>
); );
} }
...@@ -59,19 +24,19 @@ export default function BookList () { ...@@ -59,19 +24,19 @@ export default function BookList () {
<div className="container mx-auto px-4 py-8"> <div className="container mx-auto px-4 py-8">
<h1 className="text-2xl font-bold mb-6">Book Collection</h1> <h1 className="text-2xl font-bold mb-6">Book Collection</h1>
{books.length === 0 ? ( <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<p className="text-gray-600">No books found.</p> {books.map((book) => (
) : ( <Book
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> key={book.id}
{books.map((book) => ( title={book.title}
<Book author={book.author}
key={book.id} />
title={book.title} ))}
author={book.author} </div>
/>
))}
</div>
)}
</div> </div>
); );
};
BookList.propTypes = {
books: PropTypes.array.isRequired,
}; };
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment