From 74786e6d43bddb62701cbbddc3d8a020c726887b Mon Sep 17 00:00:00 2001 From: tamerat-ab Date: Fri, 9 Dec 2022 19:50:06 +0100 Subject: [PATCH] DATABASE_PROJECT --- ERD_screenshot.png | Bin 0 -> 61621 bytes create_table.py | 73 ++++++++ library app | 414 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 487 insertions(+) create mode 100644 ERD_screenshot.png create mode 100644 create_table.py create mode 100644 library app diff --git a/ERD_screenshot.png b/ERD_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..1f67bed22c3595f459182deb2405b64671a0a70e GIT binary patch literal 61621 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�_l~699B79 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 literal 0 HcmV?d00001 diff --git a/create_table.py b/create_table.py new file mode 100644 index 0000000..0dc249a --- /dev/null +++ b/create_table.py @@ -0,0 +1,73 @@ +import psycopg2 + +def create_tables(): + """ create tables in the PostgreSQL database""" + commands = ( + """ + + CREATE TABLE IF NOT EXISTS public.books +( + book_id serial NOT NULL , + title character varying(100) COLLATE pg_catalog."default" NOT NULL, + author character varying(100) COLLATE pg_catalog."default", + genre character varying(100) COLLATE pg_catalog."default", + pages integer, + added_date date DEFAULT CURRENT_DATE, + quantity integer NOT NULL, + CONSTRAINT books_pkey PRIMARY KEY (book_id), + CONSTRAINT uk_books UNIQUE (title, author) +) + """, + """ CREATE TABLE IF NOT EXISTS public.user_action +( + action_id serial NOT NULL, + user_name character varying(100) NOT NULL, + book_id integer NOT NULL, + borrow boolean DEFAULT false, + reading boolean DEFAULT false, + read boolean DEFAULT false, + fav boolean DEFAULT false, + will_read boolean DEFAULT false, + CONSTRAINT pk_action_id PRIMARY KEY (action_id) +) + """, + """CREATE TABLE IF NOT EXISTS public.users +( + user_name character varying(100) NOT NULL, + CONSTRAINT pk_user_name PRIMARY KEY (user_name), + CONSTRAINT uk_user_name UNIQUE (user_name) +) + """, + """ALTER TABLE IF EXISTS public.user_action + ADD CONSTRAINT fk_books_actions FOREIGN KEY (book_id) + REFERENCES public.books (book_id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION;""", + """ALTER TABLE IF EXISTS public.user_action + ADD FOREIGN KEY (user_name) + REFERENCES public.users (user_name) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID; + + """ + ) + conn = psycopg2.connect( + host="localhost", + database="library", + user="postgres", + password="postgres") + try: + cur = conn.cursor() + for command in commands: + cur.execute(command) + cur.close() + conn.commit() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + finally: + if conn is not None: + conn.close() + +if __name__ == '__main__': + create_tables() diff --git a/library app b/library app new file mode 100644 index 0000000..04c0f46 --- /dev/null +++ b/library app @@ -0,0 +1,414 @@ +import typer +from rich.console import Console +from rich.table import Table +from typing import Optional +from create_table import create_tables +import psycopg2 +import json + +console = Console() + +app = typer.Typer() + +conn = psycopg2.connect( + host="localhost", + database="library", + user="postgres", + password="postgres") + + +@app.command("start") +def start(): + typer.secho(f'''Welcome to Library CLI!\n\n + You can execute command '--help' to see the possible commands''', fg=typer.colors.GREEN) + # TODO: connect to database + create_tables() + with open('books.json', 'r') as f: + books = json.load(f) + for i in books: + cur = conn.cursor() + title = i['title'] + author = i ["authors"][0] + pages = i['pageCount'] + genre = i["categories"][0] + quantity= 1 + postgres_select_query = f"""select quantity from books where title = '{title}' and author = '{author}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is None: + postgres_insert_query = f""" INSERT INTO books (title,author,genre,pages,quantity) VALUES ('{title}','{author}','{genre}','{pages}','{quantity}')""" + cur.execute(postgres_insert_query) + cur.close() + conn.commit() + +# This is how you can get arguments, here username is a mandatory argument for this command. +@app.command("sign_up") +def sign_up(username: str): + try: + + cur = conn.cursor() + postgres_insert_query = f""" INSERT INTO users (user_name) VALUES ('{username}')""" + cur.execute(postgres_insert_query) + cur.close() + conn.commit() + typer.echo(f"Nice that you are signing up!") + except (Exception, psycopg2.DatabaseError): + print ('Check your username please') + finally: + if conn is not None: + conn.close() + +@app.command("add_book") +def add_book(): + typer.echo(f"Please provide book detail!") + cur = conn.cursor() + title = input ("Title: ") + author = input ("Author: ") + pages = input ("No. of pages: ") + genre = input ("Genre: ") + quantity= int(input ("Quantity: ")) + postgres_select_query = f"""select quantity from books where title = '{title}' and author = '{author}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + q2 = q1[0] + updated_quantity = q2+ quantity + postgres_update_query = f"""update books set quantity = '{updated_quantity}' where title = '{title}' and author = '{author}' """ + cur.execute(postgres_update_query) + typer.echo(f"Successfully updated book's quantity!") + else: + postgres_insert_query = f""" INSERT INTO books (title,author,genre,pages,quantity) VALUES ('{title}','{author}','{genre}','{pages}','{quantity}')""" + cur.execute(postgres_insert_query) + typer.echo(f"Successfully added book!") + + cur.close() + conn.commit() + +@app.command("search_by_name") +def search_by_name(name): + cur = conn.cursor() + postgres_select_query = f"""select ROW_NUMBER () OVER (ORDER BY book_id) as "#", + book_id as "Book ID", title as "Name", author as "Author", pages as "# Pages", + genre as "Genre", quantity > 0 as "Availability" from books where title LIKE '{name}%' """ + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("search_by_author") +def search_by_author(author): + cur = conn.cursor() + postgres_select_query = f"""select ROW_NUMBER () OVER (ORDER BY book_id) as "#", + book_id as "Book ID", title as "Name", author as "Author", pages as "# Pages", + genre as "Genre", quantity > 0 as "Availability" from books where author LIKE '{author}%' """ + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("recently_added") +def recently_added(genre: Optional[str]= typer.Argument(None)): + cur = conn.cursor() + if genre is None: + postgres_select_query = f""" +select ROW_NUMBER () OVER (order by added_date desc) as "#", book_id as "Book ID", title as "Name", author as "Author", pages as "# Pages", + genre as "Genre", case when quantity> 0 then 'True' + when quantity = 0 then 'False' end "Availability" + from books order by added_date desc limit 5;""" + cur.execute(postgres_select_query) + else: + postgres_select_query = f"""select ROW_NUMBER () OVER (order by added_date desc) as "#", book_id as "Book ID", title as "Name", author as "Author", pages as "# Pages", + genre as "Genre", case when quantity> 0 then 'True' + when quantity = 0 then 'False' end "Availability" + from books where genre = '{genre}' order by added_date desc limit 5 ;""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("most_read_books") +def most_read_books(genre: Optional[str]= typer.Argument(None)): + cur = conn.cursor() + if genre is None: + postgres_select_query = f"""select row_number() over(order by count(read) desc) as "#",ac.book_id as "Book ID", b.title as "Name", b.author as "Author" + , b.genre as "Genre", count(read) as "Count" + from user_action ac, books b where ac.book_id = b.book_id and read = 'true' group by ac.book_id , b.title, b.author, b.genre order by count(read) desc limit 10;""" + cur.execute(postgres_select_query) + else: + postgres_select_query = f"""select row_number() over(order by count(read) desc) as "#",ac.book_id as "Book ID", b.title as "Name" + , b.author as "Author", b.genre as "Genre", count(read) as "Count" from user_action ac, books b + where ac.book_id = b.book_id and read = 'true' and genre = '{genre}' + group by ac.book_id , b.title, b.author, b.genre order by count(read) desc limit 10;""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("most_favorite_books") +def most_favorite_books(genre: Optional[str]= typer.Argument(None)): + cur = conn.cursor() + if genre is None: + postgres_select_query = f"""select row_number() over(order by count(fav) desc) as "#",ac.book_id as "Book ID", b.title as "Name", b.author as "Author" + , b.genre as "Genre", count(fav) as "Count" + from user_action ac, books b where ac.book_id = b.book_id and fav = 'true' group by ac.book_id , b.title, b.author, b.genre order by count(fav) desc limit 10;""" + cur.execute(postgres_select_query) + else: + postgres_select_query = f"""select row_number() over(order by count(fav) desc) as "#",ac.book_id as "Book ID", b.title as "Name" + , b.author as "Author", b.genre as "Genre", count(fav) as "Count"from user_action ac, books b + where ac.book_id = b.book_id and fav = 'true' and genre = '{genre}' + group by ac.book_id , b.title, b.author, b.genre order by count(fav) desc limit 10;""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("most_read_genres") +def most_read_genres(): + cur = conn.cursor() + postgres_select_query = f""" SELECT ROW_NUMBER () OVER (ORDER BY COUNT(b.genre) DESC) as "#", b.genre AS "GENRE", COUNT(b.genre) AS "COUNT" FROM user_action u JOIN books b + ON b.book_id = u.book_id and u.read = True GROUP BY b.genre + ORDER BY COUNT(b.genre) DESC LIMIT 5 """ + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("most_read_authors") +def most_read_authors(): + cur = conn.cursor() + postgres_select_query = f"""select row_number() over(order by count(read) desc) as "#", b.author as "Author", count(read) as "Count" + from user_action ac, books b where ac.book_id = b.book_id and read = 'true' + group by b.author order by count(read) desc limit 3;""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("borrow_book") +def borrow_book(book_id, username): + cur = conn.cursor() + + postgres_select_query = f"""select u.user_name, b.book_id from users u , books b where b.book_id = '{book_id}' and u.user_name = '{username}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is None: + typer.echo(f"Sorry, user name or book id is incorrect!") + cur.close() + conn.commit() + return + + else: + postgres_select_query = f"""select quantity from books where book_id = '{book_id}' """ + cur.execute(postgres_select_query) + quantity = cur.fetchone() + + if quantity[0] > 0: + postgres_select_query = f"""select action_id from user_action WHERE user_name = '{username}' + and book_id = {book_id}""" + cur.execute(postgres_select_query) + actionid = cur.fetchone() + + if actionid == None: + postgres_insert_query = f""" INSERT INTO user_action (user_name,book_id,borrow) VALUES + ('{username}','{book_id}',true)""" + cur.execute(postgres_insert_query) + else: + postgres_update_query = f""" UPDATE user_action SET borrow = true + WHERE user_name = '{username}' and book_id = {book_id}""" + cur.execute(postgres_update_query) + + postgres_update_query = f""" UPDATE books SET quantity = quantity - 1 + WHERE book_id = {book_id}""" + cur.execute(postgres_update_query) + + typer.echo(f"{username} borrowed book {book_id}!") + + else: + typer.echo( + f"Sorry book {book_id} is not available! Try again later.") + + cur.close() + conn.commit() + + + +@app.command("return_book") +def return_book(book_id,user_name): + cur = conn.cursor() + postgres_select_query=f"""select b.quantity from books b join user_action u on b.book_id=u.book_id where u.user_name='{user_name}' and u.borrow=true and u.book_id='{book_id}'""" + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + q2=q1[0]+1 + postgres_update_query=f"""update books set quantity={q2} where book_id={book_id}""" + cur.execute(postgres_update_query) + postgres_update_query2 = f"""update user_action set borrow = null where book_id = '{book_id}' and user_name = '{user_name}' and borrow=true """ + cur.execute(postgres_update_query2) + typer.echo(f"You returned book {book_id}") + else: + typer.echo(f"Sorry, you didn't borrow book {book_id}") + cur.close() + conn.commit() + +@app.command("mark_read") +def mark_read(book_id: int, user_name: str): + cur = conn.cursor() + postgres_select_query = f"""select user_name from users where user_name = '{user_name}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + postgres_select_query = f"""select book_id from books where book_id = '{book_id}' """ + cur.execute(postgres_select_query) + q2 = cur.fetchone() + if q2 is not None: + postgres_insert_query = f"""INSERT INTO user_action (user_name,book_id,read) VALUES ('{user_name}','{book_id}',true) """ + cur.execute(postgres_insert_query) + typer.echo(f"You marked book {book_id} as read!") + else: + typer.echo(f"Sorry, book id is incorrect!") + else: + typer.echo(f"Sorry, user name is incorrect!") + cur.close() + conn.commit() + +@app.command("mark_reading") +def mark_reading(book_id: int, user_name: str): + cur = conn.cursor() + postgres_select_query = f"""select book_id , user_name from user_action where book_id = '{book_id}' and user_name = '{user_name}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + postgres_update_query = f"""update user_action set reading = 'true' where book_id = '{book_id}' and user_name = '{user_name}' """ + cur.execute(postgres_update_query) + typer.echo(f"You marked book {book_id} as reading!") + else: + postgres_select_query = f"""select u.user_name, b.book_id from users u , books b where b.book_id = '{book_id}' and u.user_name = '{user_name}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + postgres_insert_query = f""" INSERT INTO user_action (user_name,book_id,reading) VALUES ('{user_name}','{book_id}',true)""" + cur.execute(postgres_insert_query) + typer.echo(f"You marked book {book_id} as reading!") + else: + typer.echo(f"Sorry, user name or book id is incorrect!") + cur.close() + conn.commit() + +@app.command("mark_will_read") +def mark_will_read(book_id, username): + cur = conn.cursor() + postgres_select_query = f"""select action_id from user_action WHERE user_name = '{username}' + and book_id = {book_id}""" + cur.execute(postgres_select_query) + actionid = cur.fetchone() + if actionid == None: + postgres_insert_query = f""" INSERT INTO user_action (user_name,book_id,will_read) VALUES + ('{username}','{book_id}',true)""" + cur.execute(postgres_insert_query) + else: + postgres_update_query = f""" UPDATE user_action SET will_read = true, read = false, reading = false + WHERE user_name = '{username}' and book_id = {book_id}""" + cur.execute(postgres_update_query) + cur.close() + conn.commit() + typer.echo(f"{username} marked book {book_id} as will read.") + +@app.command("fav_book") +def fav_book(book_id: int, user_name: str): + cur = conn.cursor() + postgres_select_query = f"""select book_id , user_name from user_action where book_id = '{book_id}' and user_name = '{user_name}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + postgres_update_query = f"""update user_action set fav = 'true' where book_id = '{book_id}' and user_name = '{user_name}' """ + cur.execute(postgres_update_query) + typer.echo(f"You added book {book_id} to your favorites!'") + else: + postgres_select_query = f"""select u.user_name, b.book_id from users u , books b where b.book_id = '{book_id}' and u.user_name = '{user_name}' """ + cur.execute(postgres_select_query) + q1 = cur.fetchone() + if q1 is not None: + postgres_insert_query = f""" INSERT INTO user_action (user_name,book_id,fav) VALUES ('{user_name}','{book_id}',true)""" + cur.execute(postgres_insert_query) + typer.echo(f"You added book {book_id} to your favorites!'") + else: + typer.echo(f"Sorry, user name or book id is incorrect!") + cur.close() + conn.commit() + +@app.command("my_books") +def my_books(username): + typer.echo(f"BOOKS YOU READ") + cur = conn.cursor() + postgres_select_query = f""" select DISTINCT ON (b.book_id) ROW_NUMBER () OVER (ORDER BY b.book_id) as "#", + b.book_id as "Book ID", b.title as "Name", b.author as "Author", b.pages as "# Pages", + b.genre as "Genre", b.quantity > 0 as "Availability" from books b join + user_action u on b.book_id=u.book_id where u.user_name='{username}' and u.read=true ORDER BY b.book_id""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + typer.echo(f"BOOKS YOU ARE READING") + cur = conn.cursor() + postgres_select_query = f""" select DISTINCT ON (b.book_id) ROW_NUMBER () OVER (ORDER BY b.book_id) as "#", + b.book_id as "Book ID", b.title as "Name", b.author as "Author", b.pages as "# Pages", + b.genre as "Genre", b.quantity > 0 as "Availability" from books b join + user_action u on b.book_id=u.book_id where u.user_name='{username}' and u.reading=true ORDER BY b.book_id""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + typer.echo(f"BOOKS YOU WILL READ") + cur = conn.cursor() + postgres_select_query = f""" select DISTINCT ON (b.book_id) ROW_NUMBER () OVER (ORDER BY b.book_id) as "#", + b.book_id as "Book ID", b.title as "Name", b.author as "Author", b.pages as "# Pages", + b.genre as "Genre", b.quantity > 0 as "Availability" from books b join + user_action u on b.book_id=u.book_id where u.user_name='{username}' and u.will_read=true ORDER BY b.book_id""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + typer.echo(f"YOUR FAVORITE BOOKS") + cur = conn.cursor() + postgres_select_query = f""" select DISTINCT ON (b.book_id) ROW_NUMBER () OVER (ORDER BY b.book_id) as "#", + b.book_id as "Book ID", b.title as "Name", b.author as "Author", b.pages as "# Pages", + b.genre as "Genre", b.quantity > 0 as "Availability" from books b join + user_action u on b.book_id=u.book_id where u.user_name='{username}' and u.fav=true ORDER BY b.book_id""" + cur.execute(postgres_select_query) + display_table(cur) + cur.close() + conn.commit() + +@app.command("statistics") +def statistics(user_name: str): + cur = conn.cursor() + postgres_select_query = f"""select count(read), count(distinct (b.author)), count(distinct(b.genre)), sum(b.pages)from user_action ac, books b where ac.book_id = b.book_id and read = 'true' and ac.user_name = '{user_name}'; """ + cur.execute(postgres_select_query) + q=(cur.fetchall()) + table = Table(show_header=True, header_style="bold blue") + table.add_column("Statistic", style="dim", width=30) + table.add_column("Number", style="dim", min_width=10, justify=True) + table.add_row('Books you read', str(q[0][0])) + table.add_row('Authors you read', str(q[0][1])) + table.add_row('Genres you read', str(q[0][2])) + table.add_row('Total pages you read', str(q[0][3])) + console.print(table) + +# Example function for tables, you can add more columns/row. +@app.command("display_table") + +def display_table(cursor): + table = Table(show_header=True, header_style="bold blue") + column_names=[desc[0] for desc in cursor.description] + for c in column_names: + table.add_column(c, style="dim", min_width=10, justify=True) + for d in cursor.fetchall(): + ll=[] + for i in d: + ll.append(f"{i}") + table.add_row(*ll) + console.print(table) + +if __name__ == "__main__": + app() + \ No newline at end of file