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 0000000..9d7bb12 Binary files /dev/null and b/books_library.db differ 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()