Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions books_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
book_id,title,authors,genres,publisher,publication_year,isbn,page_count,language,description,cover_img
1984-orwell,1984,George Orwell,"Dystopian,Science Fiction,Political Fiction",Secker & Warburg,1949,978-0451524935,328,English,"A dystopian social science fiction novel and cautionary tale about the dangers of totalitarianism.",https://images.example.com/1984.jpg
pride-prejudice,Pride and Prejudice,Jane Austen,"Romance,Classic,Fiction",T. Egerton,1813,978-0141439518,432,English,"A romantic novel of manners that critiques the British landed gentry at the end of the 18th century.",https://images.example.com/pride.jpg
mockingbird,To Kill a Mockingbird,Harper Lee,"Southern Gothic,Coming-of-age,Legal Drama",J. B. Lippincott & Co.,1960,978-0061120084,324,English,"A novel about racial injustice and the destruction of innocence in the American South.",https://images.example.com/mockingbird.jpg
gatsby,The Great Gatsby,F. Scott Fitzgerald,"Tragedy,Fiction,Classic",Charles Scribner's Sons,1925,978-0743273565,180,English,"A novel about the American Dream and the Jazz Age.",https://images.example.com/gatsby.jpg
rings-fellowship,The Fellowship of the Ring,J.R.R. Tolkien,"Fantasy,Adventure,Epic",George Allen & Unwin,1954,978-0547928210,423,English,"The first volume of The Lord of the Rings trilogy.",https://images.example.com/fellowship.jpg
catcher-rye,The Catcher in the Rye,J.D. Salinger,"Coming-of-age,Fiction",Little Brown and Company,1951,978-0316769488,277,English,"A story about teenage rebellion and alienation.",https://images.example.com/catcher.jpg
hobbit,The Hobbit,J.R.R. Tolkien,"Fantasy,Adventure,Children's Literature",George Allen & Unwin,1937,978-0547928227,310,English,"A fantasy novel about the quest of home-loving Bilbo Baggins.",https://images.example.com/hobbit.jpg
hp-stone,Harry Potter and the Philosopher's Stone,J.K. Rowling,"Fantasy,Young Adult,Magic",Bloomsbury,1997,978-0439708180,223,English,"The first novel in the Harry Potter series.",https://images.example.com/hp1.jpg
hp-chamber,Harry Potter and the Chamber of Secrets,J.K. Rowling,"Fantasy,Young Adult,Magic",Bloomsbury,1998,978-0439064866,251,English,"The second novel in the Harry Potter series.",https://images.example.com/hp2.jpg
animal-farm,Animal Farm,George Orwell,"Political Satire,Allegory,Dystopian",Secker & Warburg,1945,978-0451526342,112,English,"An allegorical novella about Stalinism and the Russian Revolution.",https://images.example.com/animal.jpg
fahrenheit-451,Fahrenheit 451,Ray Bradbury,"Dystopian,Science Fiction",Ballantine Books,1953,978-1451673319,249,English,"A dystopian novel about a future American society where books are outlawed.",https://images.example.com/f451.jpg
brave-new-world,Brave New World,Aldous Huxley,"Dystopian,Science Fiction,Philosophy",Chatto & Windus,1932,978-0060850524,311,English,"A dystopian novel set in a futuristic World State of genetically modified citizens.",https://images.example.com/brave.jpg
handmaid,The Handmaid's Tale,Margaret Atwood,"Dystopian,Science Fiction,Feminist",McClelland & Stewart,1985,978-0385490818,311,English,"A dystopian novel set in a totalitarian society.",https://images.example.com/handmaid.jpg
kite-runner,The Kite Runner,Khaled Hosseini,"Historical Fiction,Drama",Riverhead Books,2003,978-1594631931,371,English,"A story of friendship and redemption set against the backdrop of Afghanistan.",https://images.example.com/kite.jpg
alchemist,The Alchemist,Paulo Coelho,"Quest,Drama,Fantasy",HarperTorch,1988,978-0062315007,197,English,"A philosophical book about following one's dreams.",https://images.example.com/alchemist.jpg
little-prince,The Little Prince,Antoine de Saint-Exupéry,"Children's Literature,Fable,Philosophy",Reynal & Hitchcock,1943,978-0156012195,96,English,"A poetic tale about a young prince who visits various planets.",https://images.example.com/prince.jpg
chronicles-narnia,The Lion the Witch and the Wardrobe,C.S. Lewis,"Fantasy,Children's Literature,Adventure",Geoffrey Bles,1950,978-0064404990,206,English,"A fantasy novel for children set in the fictional realm of Narnia.",https://images.example.com/narnia.jpg
hunger-games,The Hunger Games,Suzanne Collins,"Dystopian,Science Fiction,Young Adult",Scholastic Press,2008,978-0439023481,374,English,"A dystopian novel set in a post-apocalyptic nation.",https://images.example.com/hunger.jpg
da-vinci,The Da Vinci Code,Dan Brown,"Mystery,Thriller,Conspiracy",Doubleday,2003,978-0307474278,689,English,"A mystery thriller novel following symbologist Robert Langdon.",https://images.example.com/davinci.jpg
gone-girl,Gone Girl,Gillian Flynn,"Mystery,Thriller,Psychological",Crown Publishing Group,2012,978-0307588371,415,English,"A psychological thriller about a woman who goes missing.",https://images.example.com/gone.jpg
foundation,Foundation,Isaac Asimov,"Science Fiction,Space Opera",Gnome Press,1951,978-0553293357,255,English,"A science fiction novel about the fall and rise of civilizations.",https://images.example.com/foundation.jpg
dune,Dune,Frank Herbert,"Science Fiction,Adventure",Chilton Books,1965,978-0441172719,688,English,"A science fiction novel set in the distant future amidst a huge interstellar empire.",https://images.example.com/dune.jpg
neuromancer,Neuromancer,William Gibson,"Cyberpunk,Science Fiction",Ace Books,1984,978-0441569595,271,English,"A cyberpunk novel that helped define the genre.",https://images.example.com/neuro.jpg
enders-game,Ender's Game,Orson Scott Card,"Science Fiction,Military,Young Adult",Tor Books,1985,978-0812550702,324,English,"A military science fiction novel about a young boy trained in military arts.",https://images.example.com/ender.jpg
left-hand,The Left Hand of Darkness,Ursula K. Le Guin,"Science Fiction,Anthropological",Ace Books,1969,978-0441478125,304,English,"A groundbreaking work of science fiction exploring gender and society.",https://images.example.com/lefthand.jpg
Binary file added books_library.db
Binary file not shown.
343 changes: 343 additions & 0 deletions create_books_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
import sqlite3
import csv
from pathlib import Path
from datetime import datetime

# Configuration
CSV_PATH = Path("books_data.csv")
DB_PATH = Path("books_library.db")


def create_schema(conn: sqlite3.Connection) -> None:
"""
Create a comprehensive books/library database schema.

Tables:
- authors: Author information
- publishers: Publisher information
- genres: Book genres
- books: Main book information
- book_authors: Many-to-many relationship (books can have multiple authors)
- book_genres: Many-to-many relationship (books can have multiple genres)
- users: User accounts for reading tracking
- user_profiles: Extended user profile information
- user_reading: Track which books users have read
- user_reviews: User reviews and ratings
"""
cur = conn.cursor()

# Enable foreign keys
cur.execute("PRAGMA foreign_keys = ON;")

# Authors table
cur.execute("""
CREATE TABLE IF NOT EXISTS authors (
author_id INTEGER PRIMARY KEY AUTOINCREMENT,
author_name TEXT NOT NULL,
birth_year INTEGER,
country TEXT,
biography TEXT,
author_img TEXT
);
""")

# Publishers table
cur.execute("""
CREATE TABLE IF NOT EXISTS publishers (
publisher_id INTEGER PRIMARY KEY AUTOINCREMENT,
publisher_name TEXT NOT NULL UNIQUE,
country TEXT,
founded_year INTEGER
);
""")

# Genres table
cur.execute("""
CREATE TABLE IF NOT EXISTS genres (
genre_id INTEGER PRIMARY KEY AUTOINCREMENT,
genre_name TEXT NOT NULL UNIQUE
);
""")

# Books table
cur.execute("""
CREATE TABLE IF NOT EXISTS books (
book_id TEXT PRIMARY KEY,
title TEXT NOT NULL,
publisher_id INTEGER,
publication_year INTEGER,
isbn TEXT UNIQUE,
page_count INTEGER,
language TEXT,
description TEXT,
cover_img TEXT,
FOREIGN KEY (publisher_id) REFERENCES publishers(publisher_id)
);
""")

# Book-Authors junction table
cur.execute("""
CREATE TABLE IF NOT EXISTS book_authors (
book_id TEXT NOT NULL,
author_id INTEGER NOT NULL,
author_order INTEGER DEFAULT 1,
PRIMARY KEY (book_id, author_id),
FOREIGN KEY (book_id) REFERENCES books(book_id) ON DELETE CASCADE,
FOREIGN KEY (author_id) REFERENCES authors(author_id) ON DELETE CASCADE
);
""")

# Book-Genres junction table
cur.execute("""
CREATE TABLE IF NOT EXISTS book_genres (
book_id TEXT NOT NULL,
genre_id INTEGER NOT NULL,
PRIMARY KEY (book_id, genre_id),
FOREIGN KEY (book_id) REFERENCES books(book_id) ON DELETE CASCADE,
FOREIGN KEY (genre_id) REFERENCES genres(genre_id) ON DELETE CASCADE
);
""")

# Users table
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
first_name TEXT,
last_name TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login DATETIME,
is_active BOOLEAN DEFAULT 1
);
""")

# User profiles table
cur.execute("""
CREATE TABLE IF NOT EXISTS user_profiles (
profile_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER UNIQUE NOT NULL,
bio TEXT,
favorite_genres TEXT,
books_read_count INTEGER DEFAULT 0,
profile_image_url TEXT,
city TEXT,
country TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
);
""")

# User reading tracking table
cur.execute("""
CREATE TABLE IF NOT EXISTS user_reading (
reading_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
book_id TEXT NOT NULL,
status TEXT CHECK(status IN ('want_to_read', 'reading', 'completed', 'abandoned')),
date_started DATETIME,
date_completed DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
FOREIGN KEY (book_id) REFERENCES books(book_id) ON DELETE CASCADE,
UNIQUE(user_id, book_id)
);
""")

# User reviews table
cur.execute("""
CREATE TABLE IF NOT EXISTS user_reviews (
review_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
book_id TEXT NOT NULL,
rating INTEGER CHECK(rating >= 1 AND rating <= 5),
review_text TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
FOREIGN KEY (book_id) REFERENCES books(book_id) ON DELETE CASCADE,
UNIQUE(user_id, book_id)
);
""")

# Triggers for timestamp updates
cur.execute("""
CREATE TRIGGER IF NOT EXISTS update_users_timestamp
AFTER UPDATE ON users
BEGIN
UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE user_id = NEW.user_id;
END;
""")

cur.execute("""
CREATE TRIGGER IF NOT EXISTS update_profiles_timestamp
AFTER UPDATE ON user_profiles
BEGIN
UPDATE user_profiles SET updated_at = CURRENT_TIMESTAMP WHERE profile_id = NEW.profile_id;
END;
""")

cur.execute("""
CREATE TRIGGER IF NOT EXISTS update_reviews_timestamp
AFTER UPDATE ON user_reviews
BEGIN
UPDATE user_reviews SET updated_at = CURRENT_TIMESTAMP WHERE review_id = NEW.review_id;
END;
""")

conn.commit()


def load_csv_into_db(conn: sqlite3.Connection, csv_path: Path) -> None:
"""
Read the books CSV file and populate the database.
Expected CSV columns: book_id, title, authors, genres, publisher,
publication_year, isbn, page_count, language, description, cover_img
"""
cur = conn.cursor()

if not csv_path.exists():
print(f"⚠️ CSV file not found: {csv_path}")
print("Creating empty database with schema only.")
return

with csv_path.open("r", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)

for row in reader:
# Extract and clean data
book_id = (row.get("book_id") or "").strip()
title = (row.get("title") or "").strip()
authors_str = (row.get("authors") or "").strip()
genres_str = (row.get("genres") or "").strip()
publisher_name = (row.get("publisher") or "").strip()
pub_year = (row.get("publication_year") or "").strip()
isbn = (row.get("isbn") or "").strip()
page_count = (row.get("page_count") or "").strip()
language = (row.get("language") or "").strip()
description = (row.get("description") or "").strip()
cover_img = (row.get("cover_img") or "").strip()

# Skip if missing critical fields
if not book_id or not title:
continue

# Handle publisher
publisher_id = None
if publisher_name:
cur.execute(
"INSERT OR IGNORE INTO publishers (publisher_name) VALUES (?);",
(publisher_name,)
)
cur.execute(
"SELECT publisher_id FROM publishers WHERE publisher_name = ?;",
(publisher_name,)
)
publisher_id = cur.fetchone()[0]

# Insert book
cur.execute(
"""
INSERT OR IGNORE INTO books
(book_id, title, publisher_id, publication_year, isbn, page_count, language, description, cover_img)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
""",
(
book_id,
title,
publisher_id,
int(pub_year) if pub_year.isdigit() else None,
isbn if isbn else None,
int(page_count) if page_count.isdigit() else None,
language if language else None,
description if description else None,
cover_img if cover_img else None
)
)

# Handle authors
if authors_str:
for author_name in authors_str.split(";"):
author_name = author_name.strip()
if not author_name:
continue

# Insert or get author
cur.execute(
"INSERT OR IGNORE INTO authors (author_name) VALUES (?);",
(author_name,)
)
cur.execute(
"SELECT author_id FROM authors WHERE author_name = ?;",
(author_name,)
)
author_id = cur.fetchone()[0]

# Link book to author
cur.execute(
"INSERT OR IGNORE INTO book_authors (book_id, author_id) VALUES (?, ?);",
(book_id, author_id)
)

# Handle genres
if genres_str:
for genre_name in genres_str.split(","):
genre_name = genre_name.strip()
if not genre_name:
continue

# Insert or get genre
cur.execute(
"INSERT OR IGNORE INTO genres (genre_name) VALUES (?);",
(genre_name,)
)
cur.execute(
"SELECT genre_id FROM genres WHERE genre_name = ?;",
(genre_name,)
)
genre_id = cur.fetchone()[0]

# Link book to genre
cur.execute(
"INSERT OR IGNORE INTO book_genres (book_id, genre_id) VALUES (?, ?);",
(book_id, genre_id)
)

conn.commit()


def main() -> None:
# Connect to database (creates file if it doesn't exist)
conn = sqlite3.connect(DB_PATH)

try:
print(f"📚 Creating books library database: {DB_PATH}")
create_schema(conn)
print("✅ Schema created successfully")

load_csv_into_db(conn, CSV_PATH)

# Print some stats
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM books;")
book_count = cur.fetchone()[0]
cur.execute("SELECT COUNT(*) FROM authors;")
author_count = cur.fetchone()[0]
cur.execute("SELECT COUNT(*) FROM genres;")
genre_count = cur.fetchone()[0]

print(f"✅ Database created with:")
print(f" - {book_count} books")
print(f" - {author_count} authors")
print(f" - {genre_count} genres")

finally:
conn.close()

print(f"✅ Done! Books library database saved to '{DB_PATH}'")


if __name__ == "__main__":
main()