From fbec7c7f0ace451fcd19480fc4b0f486303d8159 Mon Sep 17 00:00:00 2001 From: rcorrie874 Date: Tue, 2 Dec 2025 16:56:51 -0500 Subject: [PATCH] Add books library database with comprehensive schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created a new books/library database system with the following features: - Complete BCNF-normalized schema for books, authors, publishers, and genres - Many-to-many relationships for book-author and book-genre associations - User management system with profiles and reading tracking - User reviews and ratings functionality - Sample dataset with 25 classic and popular books across various genres - Python script to generate database from CSV data Database includes: - 25 books from classic literature to modern sci-fi - 25 authors including Orwell, Tolkien, Rowling, Atwood, and more - 32 distinct genres - Full metadata including ISBNs, publication years, page counts This complements the existing music artists database and demonstrates similar database design patterns for a different domain. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- books_data.csv | 26 +++ books_library.db | Bin 0 -> 106496 bytes create_books_database.py | 343 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 books_data.csv create mode 100644 books_library.db create mode 100644 create_books_database.py diff --git a/books_data.csv b/books_data.csv new file mode 100644 index 0000000..a56146a --- /dev/null +++ b/books_data.csv @@ -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 diff --git a/books_library.db b/books_library.db new file mode 100644 index 0000000000000000000000000000000000000000..9d7bb12bd8473e9ca7663902dcfbccd977b7f264 GIT binary patch literal 106496 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 AUH||9 literal 0 HcmV?d00001 diff --git a/create_books_database.py b/create_books_database.py new file mode 100644 index 0000000..5c5beef --- /dev/null +++ b/create_books_database.py @@ -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()