diff --git a/TODO b/TODO new file mode 100644 index 000000000..5b70bca79 --- /dev/null +++ b/TODO @@ -0,0 +1,10 @@ +#TODO redirect +#TODO login/logout +#TODO CSS + +USER PAGE: +#TODO querry find all questions from user +#TODO querry find all answers from user +#TODO querry find all comment from user +#TODO server function use this and send the datas to the html +#TODO user page html \ No newline at end of file diff --git a/answer_data_manager.py b/answer_data_manager.py new file mode 100644 index 000000000..b30d27455 --- /dev/null +++ b/answer_data_manager.py @@ -0,0 +1,134 @@ +import connection + +@connection.connection_handler +def delete_answer(cursor, q_id, a_id): + cursor.execute(""" + delete from comment + where question_id = %(q_id)s or answer_id = %(a_id)s; + delete from answer + where question_id = %(q_id)s and id = %(a_id)s + """, + {'q_id': q_id, 'a_id': a_id}) + + +@connection.connection_handler +def collect_answers(cursor, q_id): + cursor.execute(""" + SELECT * from answer + where question_id = %(q_id)s + """, + {'q_id': q_id}) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def find_answer(cursor, a_id): + cursor.execute(""" + SELECT * from answer + where id = %(a_id)s + """, + {'a_id': a_id}) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def update_answer(cursor, datas): + cursor.execute(""" + UPDATE answer + SET message=%s, image=%s + WHERE id=%s""", + (datas['message'], datas['image'], int(datas['id']))) + + +@connection.connection_handler +def collect_all_answer(cursor): + cursor.execute(""" + SELECT * FROM answer + """) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def add_answer(cursor, form_data): + cursor.execute(""" + INSERT INTO answer(submission_time, vote_number, question_id, message, image,user_id,accepted) + VALUES (%s, %s, %s, %s, %s,%s,%s)""", + (form_data['submission_time'], form_data['vote_number'], form_data['question_id'], + form_data['message'], form_data['image'], form_data['user_id'], form_data['accepted'])) + + +@connection.connection_handler +def accept_answer(cursor, a_id): + cursor.execute(""" + Update answer + set accepted = TRUE + where id = %(a_id)s + """, + {'a_id': a_id}) + + +@connection.connection_handler +def update_vote_number_answer(cursor, vote, a_id): + cursor.execute(""" + UPDATE answer + set vote_number = %(vote)s + WHERE id = %(a_id)s + """, + {'vote': vote, 'a_id': a_id}) + + +@connection.connection_handler +def update_view_number(cursor, q_id): + cursor.execute(""" + update question + set view_number = view_number+1 + where id = %(q_id)s + """, + {'q_id': q_id}) + + +@connection.connection_handler +def update_reputation(cursor,user_id,new_reputation): + cursor.execute(""" + UPDATE "user" + SET reputation = %(new_reputation)s + WHERE id = %(user_id)s + """, + {'user_id':user_id, 'new_reputation':new_reputation}) + + +@connection.connection_handler +def get_userid_used_answer(cursor,a_id): + cursor.execute(""" + SELECT user_id FROM answer + WHERE id = %(a_id)s + """, + {'a_id':a_id}) + + result = cursor.fetchall() + return result + + +@connection.connection_handler +def get_reputation(cursor,user_id): + cursor.execute(""" + SELECT reputation FROM "user" + WHERE id = %(user_id)s + """, + {'user_id':user_id}) + result = cursor.fetchone() + return result + + +@connection.connection_handler +def get_all_answes_form_user(cursor, u_id): + cursor.execute("""Select question_id, question.title as question_title, answer.message as answer, answer.vote_number as vote_number from answer + join question on question_id=question.id + where answer.user_id= %(u_id)s + """, + {'u_id':u_id}) + result=cursor.fetchall() + return result diff --git a/comment_data_manager.py b/comment_data_manager.py new file mode 100644 index 000000000..fa2bf8702 --- /dev/null +++ b/comment_data_manager.py @@ -0,0 +1,76 @@ +import connection + + +@connection.connection_handler +def update_view_number(cursor, q_id): + cursor.execute(""" + update question + set view_number = view_number+1 + where id = %(q_id)s + """, + {'q_id': q_id}) + + +@connection.connection_handler +def update_vote_number(cursor, vote, q_id): + cursor.execute(""" + UPDATE question + set vote_number = %(vote)s + WHERE id = %(q_id)s + """, + {'vote': vote, 'q_id': q_id}) + + +@connection.connection_handler +def add_comment_to_question(cursor, q_id, comment_message, u_id, s_time): + cursor.execute(""" + insert into comment(question_id, message, user_id, submission_time) + values (%(q_id)s,%(comment_message)s,%(u_id)s,%(s_time)s) + + """, + {'q_id': q_id, 'comment_message': comment_message, 'u_id': u_id, 's_time': s_time}) + + +@connection.connection_handler +def add_comment_to_answer(cursor, a_id, comment_message): + cursor.execute(""" + insert into comment(answer_id, message) + values (%(a_id)s,%(comment_message)s) + + """, + {'a_id': a_id, 'comment_message': comment_message}) + + +@connection.connection_handler +def collect_comment_to_question(cursor, q_id): + cursor.execute(""" + Select message from comment + where question_id = %(q_id)s + """, + {'q_id': q_id}) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def collect_comment_to_answer(cursor, a_id): + cursor.execute(""" + Select message, answer_id from comment + where answer_id = %(a_id)s + """, + {'a_id': a_id}) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def get_all_comments_from_user(cursor, u_id): + cursor.execute(""" + Select comment.message as comment, question.title as question, question.id as q_id, answer.question_id as question_id, answer.message as answer from comment + full join answer on comment.answer_id = answer.id + full join question on comment.question_id = question.id + where comment.user_id = %(u_id)s + """, + {'u_id': u_id}) + result = cursor.fetchall() + return result diff --git a/connection.py b/connection.py new file mode 100644 index 000000000..c9bda939a --- /dev/null +++ b/connection.py @@ -0,0 +1,51 @@ +# Creates a decorator to handle the database connection/cursor opening/closing. +# Creates the cursor with RealDictCursor, thus it returns real dictionaries, where the column names are the keys. +import os +import psycopg2 +import psycopg2.extras + + +def get_connection_string(): + # setup connection string + # to do this, please define these environment variables first + user_name = os.environ.get('PSQL_USER_NAME') + password = os.environ.get('PSQL_PASSWORD') + host = os.environ.get('PSQL_HOST') + database_name = os.environ.get('PSQL_DB_NAME') + + env_variables_defined = user_name and password and host and database_name + + if env_variables_defined: + # this string describes all info for psycopg2 to connect to the database + return 'postgresql://{user_name}:{password}@{host}/{database_name}'.format( + user_name=user_name, + password=password, + host=host, + database_name=database_name + ) + else: + raise KeyError('Some necessary environment variable(s) are not defined') + + +def open_database(): + try: + connection_string = get_connection_string() + connection = psycopg2.connect(connection_string) + connection.autocommit = True + except psycopg2.DatabaseError as exception: + print('Database connection problem') + raise exception + return connection + + +def connection_handler(function): + def wrapper(*args, **kwargs): + connection = open_database() + # we set the cursor_factory parameter to return with a RealDictCursor cursor (cursor which provide dictionaries) + dict_cur = connection.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + ret_value = function(dict_cur, *args, **kwargs) + dict_cur.close() + connection.close() + return ret_value + + return wrapper diff --git a/password_hash.py b/password_hash.py new file mode 100644 index 000000000..5bd5d6510 --- /dev/null +++ b/password_hash.py @@ -0,0 +1,12 @@ +import bcrypt + + +def hash_password(plain_text_password): + # By using bcrypt, the salt is saved into the hash itself + hashed_bytes = bcrypt.hashpw(plain_text_password.encode('utf-8'), bcrypt.gensalt()) + return hashed_bytes.decode('utf-8') + + +def verify_password(plain_text_password, hashed_password): + hashed_bytes_password = hashed_password.encode('utf-8') + return bcrypt.checkpw(plain_text_password.encode('utf-8'), hashed_bytes_password) diff --git a/question_data_manager.py b/question_data_manager.py new file mode 100644 index 000000000..8f2c5be05 --- /dev/null +++ b/question_data_manager.py @@ -0,0 +1,155 @@ +import connection + + +@connection.connection_handler +def sort_questions(cursor, option, how): + option = option + cursor.execute(""" + SELECT * FROM question + ORDER BY {option} {how} + """.format(option=option,how=how)) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def collect_questions(cursor): + cursor.execute(""" + SELECT * FROM question + """) + result = cursor.fetchall() + return result + + + +@connection.connection_handler +def delete_question(cursor, q_id): + cursor.execute(""" + delete from question_tag + where question_id = %(q_id)s; + delete from comment + where question_id = %(q_id)s; + delete from answer + where question_id = %(q_id)s; + delete from question + where id = %(q_id)s + """, + {'q_id': q_id}) + + +@connection.connection_handler +def find_searched_questions(cursor, searched_data): + searched_data = "%" + searched_data + "%" + cursor.execute(""" + select * from question + where title ilike %(searched_data)s + OR + message ilike %(searched_data)s + """, + {'searched_data': searched_data}) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def collect_latest_5_question(cursor): + cursor.execute(""" + SELECT * FROM question + ORDER BY submission_time DESC + LIMIT 5""") + result = cursor.fetchall() + return result + + +@connection.connection_handler +def find_question(cursor, q_id): + cursor.execute(""" + SELECT * from question + where id = %(q_id)s + """, + {'q_id': q_id}) + result = cursor.fetchall() + return result + + +@connection.connection_handler +def update_question(cursor, datas): + cursor.execute(""" + UPDATE question + SET title=%s, message=%s, image=%s + WHERE id=%s""", + (datas['title'], datas['message'], datas['image'], int(datas['id']))) + + +@connection.connection_handler +def add_question(cursor, from_data): + cursor.execute(""" + INSERT INTO question(submission_time, view_number, vote_number, title, message, image,user_id) + VALUES (%s,%s,%s, %s,%s,%s,%s)""", + ( + from_data['submission_time'], from_data['view_number'], from_data['vote_number'], + from_data['title'], + from_data['message'], from_data['image'], from_data['user_id'])) + +@connection.connection_handler +def update_view_number(cursor, q_id): + cursor.execute(""" + update question + set view_number = view_number+1 + where id = %(q_id)s + """, + {'q_id': q_id}) + + +@connection.connection_handler +def update_vote_number_question(cursor, vote, q_id): + cursor.execute(""" + UPDATE question + set vote_number = %(vote)s + WHERE id = %(q_id)s + """, + {'vote': vote, 'q_id': q_id}) + + +@connection.connection_handler +def update_reputation(cursor,user_id,new_reputation): + cursor.execute(""" + UPDATE "user" + SET reputation = %(new_reputation)s + WHERE id = %(user_id)s + """, + {'user_id':user_id, 'new_reputation':new_reputation}) + + +@connection.connection_handler +def get_userid_used_question(cursor,q_id): + cursor.execute(""" + SELECT user_id FROM question + WHERE id = %(q_id)s + """, + {'q_id':q_id}) + + result = cursor.fetchall() + return result + + +@connection.connection_handler +def get_reputation(cursor,user_id): + cursor.execute(""" + SELECT reputation FROM "user" + WHERE id = %(user_id)s + """, + {'user_id':user_id}) + result = cursor.fetchone() + return result + + +@connection.connection_handler +def get_all_quesrion_by_user(cursor,u_id): + cursor.execute(""" + select id, title, message, vote_number, view_number from question + where user_id = %(u_id)s + """, + {'u_id':u_id}) + result=cursor.fetchall() + return result \ No newline at end of file diff --git a/sample_data/answer.csv b/sample_data/answer.csv deleted file mode 100644 index 3ddfa1ffb..000000000 --- a/sample_data/answer.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,submission_time,vote_number,question_id,message,image -0,1493398154,4,0,"You need to use brackets: my_list = []", -1,1493088154,35,0,"Look it up in the Python docs", diff --git a/sample_data/askmatepart2-sample-data.sql b/sample_data/askmatepart2-sample-data.sql new file mode 100644 index 000000000..3feb03a0d --- /dev/null +++ b/sample_data/askmatepart2-sample-data.sql @@ -0,0 +1,157 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 9.5.6 +-- Dumped by pg_dump version 9.5.6 + +ALTER TABLE IF EXISTS ONLY public.question DROP CONSTRAINT IF EXISTS pk_question_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.answer DROP CONSTRAINT IF EXISTS pk_answer_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.answer DROP CONSTRAINT IF EXISTS fk_question_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.comment DROP CONSTRAINT IF EXISTS pk_comment_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.comment DROP CONSTRAINT IF EXISTS fk_question_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.comment DROP CONSTRAINT IF EXISTS fk_answer_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.question_tag DROP CONSTRAINT IF EXISTS pk_question_tag_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.question_tag DROP CONSTRAINT IF EXISTS fk_question_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS pk_tag_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.question_tag DROP CONSTRAINT IF EXISTS fk_tag_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.user DROP CONSTRAINT IF EXISTS pk_user_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.question DROP constraint IF EXISTS fk_user_id CASCADE ; +ALTER TABLE IF EXISTS ONLY public.answer DROP constraint IF EXISTS fk_user_id CASCADE ; + + +DROP TABLE IF EXISTS public.user; +CREATE TABLE "user"( + id serial NOT NULL, + submission_time timestamp without time zone, + user_name text, + hashed_password text, + reputation integer +); + + +DROP TABLE IF EXISTS public.question; +CREATE TABLE question ( + id serial NOT NULL, + submission_time timestamp without time zone, + view_number integer, + vote_number integer, + title text, + message text, + image text, + user_id integer +); + +DROP TABLE IF EXISTS public.answer; +CREATE TABLE answer ( + id serial NOT NULL, + submission_time timestamp without time zone, + vote_number integer, + question_id integer, + message text, + image text, + user_id integer, + accepted boolean +); + +DROP TABLE IF EXISTS public.comment; +CREATE TABLE comment ( + id serial NOT NULL, + question_id integer, + answer_id integer, + message text, + submission_time timestamp without time zone, + edited_count integer, + user_id integer +); + + +DROP TABLE IF EXISTS public.question_tag; +CREATE TABLE question_tag ( + question_id integer NOT NULL, + tag_id integer NOT NULL +); + +DROP TABLE IF EXISTS public.tag; +CREATE TABLE tag ( + id serial NOT NULL, + name text +); + +ALTER TABLE ONLY "user" + ADD CONSTRAINT pk_user_id PRIMARY KEY (id); + + +ALTER TABLE ONLY answer + ADD CONSTRAINT pk_answer_id PRIMARY KEY (id); + +ALTER TABLE ONLY comment + ADD CONSTRAINT pk_comment_id PRIMARY KEY (id); + +ALTER TABLE ONLY question + ADD CONSTRAINT pk_question_id PRIMARY KEY (id); + +ALTER TABLE ONLY question_tag + ADD CONSTRAINT pk_question_tag_id PRIMARY KEY (question_id, tag_id); + +ALTER TABLE ONLY tag + ADD CONSTRAINT pk_tag_id PRIMARY KEY (id); + +ALTER TABLE ONLY answer + ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES "user"(id) on delete cascade; + +ALTER TABLE ONLY question + ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES "user"(id) on delete cascade; + +ALTER TABLE ONLY comment + ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES "user"(id) on delete cascade; + +ALTER TABLE ONLY comment + ADD CONSTRAINT fk_answer_id FOREIGN KEY (answer_id) REFERENCES answer(id) on delete cascade; + +ALTER TABLE ONLY answer + ADD CONSTRAINT fk_question_id FOREIGN KEY (question_id) REFERENCES question(id) on delete cascade; + +ALTER TABLE ONLY question_tag + ADD CONSTRAINT fk_question_id FOREIGN KEY (question_id) REFERENCES question(id) on delete cascade; + +ALTER TABLE ONLY comment + ADD CONSTRAINT fk_question_id FOREIGN KEY (question_id) REFERENCES question(id) on delete cascade; + +ALTER TABLE ONLY question_tag + ADD CONSTRAINT fk_tag_id FOREIGN KEY (tag_id) REFERENCES tag(id) on delete cascade; + + +INSERT INTO question VALUES (0, '2017-04-28 08:29:00', 29, 7, 'How to make lists in Python?', 'I am totally new to this, any hints?', NULL); +INSERT INTO question VALUES (1, '2017-04-29 09:19:00', 15, 9, 'Wordpress loading multiple jQuery Versions', 'I developed a plugin that uses the jquery booklet plugin (http://builtbywill.com/booklet/#/) this plugin binds a function to $ so I cann call $(".myBook").booklet(); + +I could easy managing the loading order with wp_enqueue_script so first I load jquery then I load booklet so everything is fine. + +BUT in my theme i also using jquery via webpack so the loading order is now following: + +jquery +booklet +app.js (bundled file with webpack, including jquery)', 'images/image1.png'); +INSERT INTO question VALUES (2, '2017-05-01 10:41:00', 1364, 57, 'Drawing canvas with an image picked with Cordova Camera Plugin', 'I''m getting an image from device and drawing a canvas with filters using Pixi JS. It works all well using computer to get an image. But when I''m on IOS, it throws errors such as cross origin issue, or that I''m trying to use an unknown format. +', NULL); +SELECT pg_catalog.setval('question_id_seq', 2, true); + +INSERT INTO answer VALUES (1, '2017-04-28 16:49:00', 4, 1, 'You need to use brackets: my_list = []', NULL); +INSERT INTO answer VALUES (2, '2017-04-25 14:42:00', 35, 1, 'Look it up in the Python docs', 'images/image2.jpg'); +SELECT pg_catalog.setval('answer_id_seq', 2, true); + +INSERT INTO comment VALUES (1, 0, NULL, 'Please clarify the question as it is too vague!', '2017-05-01 05:49:00'); +INSERT INTO comment VALUES (2, NULL, 1, 'I think you could use my_list = list() as well.', '2017-05-02 16:55:00'); +SELECT pg_catalog.setval('comment_id_seq', 2, true); + +INSERT INTO "user" VALUES (1, '2017-05-01 05:49:00', 'levente', '$2b$12$R8SYQ7.S9FcrlGMCJsawjuP8AG/KpmMENIz8pSTGx2PN/W/8s2lq2'); +SELECT pg_catalog.setval('user_id_seq', 1, true); + +INSERT INTO tag VALUES (1, 'python'); +INSERT INTO tag VALUES (2, 'sql'); +INSERT INTO tag VALUES (3, 'css'); +SELECT pg_catalog.setval('tag_id_seq', 3, true); + +INSERT INTO question_tag VALUES (0, 1); +INSERT INTO question_tag VALUES (1, 3); +INSERT INTO question_tag VALUES (2, 3); diff --git a/sample_data/question.csv b/sample_data/question.csv deleted file mode 100644 index e65825dc3..000000000 --- a/sample_data/question.csv +++ /dev/null @@ -1,14 +0,0 @@ -id,submission_time,view_number,vote_number,title,message,image -0,1493368154,29,7,"How to make lists in Python?","I am totally new to this, any hints?", -1,1493068124,15,9,"Wordpress loading multiple jQuery Versions","I developed a plugin that uses the jquery booklet plugin (http://builtbywill.com/booklet/#/) this plugin binds a function to $ so I cann call $('.myBook').booklet(); - -I could easy managing the loading order with wp_enqueue_script so first I load jquery then I load booklet so everything is fine. - -BUT in my theme i also using jquery via webpack so the loading order is now following: - -jquery -booklet -app.js (bundled file with webpack, including jquery)","images/image1.png" -2,1493015432,1364,57,"Drawing canvas with an image picked with Cordova Camera Plugin","I'm getting an image from device and drawing a canvas with filters using Pixi JS. It works all well using computer to get an image. But when I'm on IOS, it throws errors such as cross origin issue, or that I'm trying to use an unknown format. - -This is the code I'm using to draw the image (that works on web/desktop but not cordova built ios app)", diff --git a/server.py b/server.py new file mode 100644 index 000000000..75cfbbe7e --- /dev/null +++ b/server.py @@ -0,0 +1,319 @@ +from flask import Flask, render_template, request, redirect, session, url_for, escape +import question_data_manager +import comment_data_manager +import answer_data_manager +import util +import password_hash +import user_data_manager + + +app = Flask(__name__) +app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' + + +@app.route('/') +def render_index(): + questions = question_data_manager.collect_latest_5_question() + if 'username' in session: + user_data = user_data_manager.get_user_id(username=session['username']) + user_id=user_data['id'] + else: + user_id='' + return render_template('index.html', questions=questions, user_id=user_id) + + +@app.route('/login', methods=['GET', 'POST']) +def login(): + questions = question_data_manager.collect_latest_5_question() + login_message = 'You are not logged in' + session['login_message'] = login_message + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + find_user = user_data_manager.find_user(username=username) + if find_user != None: + h_password = user_data_manager.get_hashed_password(user_name=username) + hashed_password = h_password['hashed_password'] + verify = password_hash.verify_password(password, hashed_password) + if verify == True: + session['verify'] = verify + session['username'] = username + login_message = 'Logged in as ' + username + session['login_message'] = login_message + return redirect(url_for('render_index', verify=verify, login_message=login_message, questions=questions)) + else: + session['verify'] = verify + login_message = 'Invalid username or password' + session['login_message'] = login_message + return redirect(url_for('render_index', login_message=login_message, questions=questions)) + else: + login_message = 'Invalid username or password' + session['login_message'] = login_message + return redirect(url_for('render_index', login_message=login_message, questions=questions)) + return redirect(url_for("render_index")) + + +@app.route('/logout') +def logout(): + # remove the username from the session if it's there + session.clear() + return redirect(url_for('render_index')) + + +@app.route('/all_question') +def show_all_question(): + questions = question_data_manager.collect_questions() + return render_template('all_question.html', questions=questions) + + +@app.route('/all_question/sort', methods=['GET', 'POST']) +def show_all_sorted_question(): + questions = sort_questions() + return render_template('all_question.html', questions=questions) + + +def sort_questions(): + sort_options = request.form.get('sort_options') + option = request.form.get('options') + result = question_data_manager.sort_questions(option=option, how=sort_options) + return result + + +@app.route('/question_page/vote_question',methods=['GET','POST']) +def vote_question(): + vote = int(request.form.get('vote_num')) + vote_up = request.form.get('vote_up') + vote_down = request.form.get('vote_down') + q_id = request.form.get('q_id') + id = question_data_manager.get_userid_used_question(q_id) + reputation = question_data_manager.get_reputation(id[0]['user_id']) + if request.method == 'POST': + if vote_up == 'up': + reputation = reputation['reputation'] + 5 + question_data_manager.update_reputation(id[0]['user_id'], reputation) + vote += 1 + elif vote_down == 'down': + reputation = reputation['reputation'] - 2 + question_data_manager.update_reputation(id[0]['user_id'], reputation) + vote -= 1 + question_data_manager.update_vote_number_question(vote=vote, q_id=q_id) + return redirect(url_for('show_question', question_id=q_id)) + + +@app.route('/question_page/vote_answer',methods=['GET','POST']) +def vote_answer(): + q_id= request.form.get('question_id') + vote = int(request.form.get('vote_num')) + vote_up = request.form.get('vote_up') + vote_down = request.form.get('vote_down') + a_id = request.form.get('a_id') + id = answer_data_manager.get_userid_used_answer(a_id) + reputation = answer_data_manager.get_reputation(id[0]['user_id']) + if request.method == 'POST': + if vote_up == 'up': + reputation = reputation['reputation'] + 10 + answer_data_manager.update_reputation(id[0]['user_id'], reputation) + vote += 1 + elif vote_down == 'down': + reputation = reputation['reputation'] -2 + answer_data_manager.update_reputation(id[0]['user_id'], reputation) + vote -= 1 + answer_data_manager.update_vote_number_answer(vote=vote, a_id=a_id) + return redirect(url_for('show_question',question_id=q_id)) + + +@app.route('/question_page//delete') +def delete_question(question_id): + question_data_manager.delete_question(q_id=question_id) + return redirect(url_for('show_all_question')) + + +@app.route('/question_page///answer-delete') +def delete_answer(question_id, answer_id): + answer_data_manager.delete_answer(q_id=question_id, a_id=answer_id) + return redirect(url_for('show_question',question_id=question_id)) + + +@app.route('/search', methods=['POST']) +def search(): + question_data = request.form.get('question_data') + result = question_data_manager.find_searched_questions(searched_data=question_data) + return render_template('all_question.html', questions=result) + + +@app.route('/question_page/') +def show_question(question_id): + question = question_data_manager.find_question(q_id=question_id) + answers = answer_data_manager.collect_answers(q_id=question_id) + comment_data_manager.update_view_number(q_id=question_id) + comment = comment_data_manager.collect_comment_to_question(q_id=question_id) + answer_comment = [] + for answer in answers: + temporary = comment_data_manager.collect_comment_to_answer(a_id=answer['id']) + if len(temporary) > 0: + answer_comment.append(temporary) + if len(answer_comment) <= 0: + answer_comment = [''] + return render_template('question_page.html', question=question, answers=answers, comment=comment, + answer_comment=answer_comment[0]) + + +@app.route('/question_page//edit') +def edit_question(question_id): + result = question_data_manager.find_question(q_id=question_id) + return render_template('add_question.html', result=result) + + +@app.route('/registration', methods=['GET', 'POST']) +def registration(): + if request.method == 'POST': + username, password = get_registration_data() + submission_time = util.submission_time_generator() + reputation=0 + user_data_manager.insert_new_user(submission_time=submission_time, username=username, h_password=password, user_reputation = reputation) + return redirect('/') + return render_template('registration.html') + + +def get_registration_data(): + user_name = request.form.get('user_name') + password = password_hash.hash_password(request.form.get('password')) + return user_name, password + + +@app.route('/rewrite_question', methods=['POST']) +def rewrite_question(): + updated_question = { + 'id': request.form.get('id'), + 'submission_time': request.form.get('submission_time'), + 'view_number': request.form.get('view_number'), + 'vote_number': request.form.get('vote_number'), + 'title': request.form.get('title'), + 'message': request.form.get('message'), + 'image': request.form.get('image') + } + question_data_manager.update_question(datas=updated_question) + return redirect(url_for('show_question', question_id=updated_question['id'])) + + +@app.route('/question_page//new-answer', methods=['GET', 'POST']) +def post_an_answer(question_id): + result = [] + message = "" + if request.method == 'POST': + new_answer = create_answer(question_id, request.form['message'], request.form['image']) + answer_data_manager.add_answer(form_data=new_answer) + return redirect(url_for('show_question', question_id=question_id)) + question = question_data_manager.find_question(question_id) + return render_template('new_answer.html', question=question, result=result, message=message) + + +@app.route('/accept-answer', methods=['POST', 'GET']) +def accept_answer(): + answer_id = request.form['a_id'] + question_id = request.form['q_id'] + answer_data_manager.accept_answer(a_id=answer_id) + return redirect(url_for('show_question', question_id=question_id)) + + +def create_answer(question_id, message, image): + user_id=user_data_manager.get_user_id(username=session['username']) + return { + 'submission_time': util.submission_time_generator(), + 'vote_number': 1, + 'question_id': question_id, + 'message': message, + 'image': image, + 'user_id': user_id['id'], + 'accepted': False + } + + +@app.route('/question_page//update') +def edit_answer(answer_id): + result = answer_data_manager.find_answer(a_id=answer_id) + return render_template('new_answer.html', result=result) + + +@app.route('/rewrite_answer', methods=['POST']) +def rewrite_answer(): + updated_answer = { + 'id': request.form.get('id'), + 'submission_time': request.form.get('submission_time'), + 'vote_number': request.form.get('vote_number'), + 'question_id': request.form.get('question_id'), + 'message': request.form.get('message'), + 'image': request.form.get('image'), + } + answer_data_manager.update_answer(datas=updated_answer) + return show_question(updated_answer['question_id']) + + +@app.route('/add-question', methods=['GET', 'POST']) +def add_question(): + result = [] + message = "" + if request.method == 'POST': + new_question = create_question(request.form['message'], request.form['image'], request.form['title']) + question_data_manager.add_question(from_data=new_question) + return redirect('/') + return render_template('add_question.html', result=result, message=message) + + +def create_question(message, image, title): + user_name = session['username'] + user_id = user_data_manager.get_user_id(username=user_name) + return { + 'submission_time': util.submission_time_generator(), + 'vote_number': 1, + 'message': message, + 'title': title, + 'image': image, + 'view_number': 1, + 'user_id': user_id['id'] + } + + +@app.route('/question_page//comment', methods=['GET', 'POST']) +def add_comment_to_question(question_id): + comment = 'question' + if request.method == 'POST': + user_name=session['username'] + user_id=user_data_manager.get_user_id(username=user_name) + message = request.form['message'] + time = util.submission_time_generator() + comment_data_manager.add_comment_to_question(q_id=question_id, comment_message=message, u_id=user_id['id'], s_time=time) + + return show_question(question_id) + return render_template('add_comment.html', question_id=question_id, comment=comment) + + +@app.route('/question_page///comment', methods=['GET', 'POST']) +def add_comment_to_answer(answer_id, question_id): + comment = 'answer' + if request.method == 'POST': + message = request.form['message'] + comment_data_manager.add_comment_to_answer(a_id=answer_id, comment_message=message) + return show_question(question_id) + return render_template('add_comment.html', answer_id=answer_id, question_id=question_id, comment=comment) + + +@app.route('/all-user') +def list_all_user(): + result = user_data_manager.select_all_user() + return render_template('all_users.html', users=result) + +@app.route('/user_page/') +def show_user_page(user_id): + questions_by_user=question_data_manager.get_all_quesrion_by_user(u_id=user_id) + answers_by_user=answer_data_manager.get_all_answes_form_user(u_id=user_id) + comments_by_user= comment_data_manager.get_all_comments_from_user(u_id=user_id) + user_data = user_data_manager.get_user_data(u_id=user_id) + return render_template('user_page.html', questions=questions_by_user, answers=answers_by_user, comments=comments_by_user, user_data=user_data) + + +if __name__ == "__main__": + app.run( + debug=True, + port=5000 + ) diff --git a/static/css/background.jpg b/static/css/background.jpg new file mode 100644 index 000000000..6bea2e763 Binary files /dev/null and b/static/css/background.jpg differ diff --git a/static/css/main.css b/static/css/main.css new file mode 100644 index 000000000..056e262a9 --- /dev/null +++ b/static/css/main.css @@ -0,0 +1,123 @@ +body { + background-image: url("background.jpg"); + color:black; + } + +#main_data { + background-color: rgb(232,232,232,0.2); + width: 400px; + position: center; +} + +#data{ + position: relative; + margin-right: fill; + float: right; + width: 400px; + backrgb(232,232,232)ground-color: lightgray; +} + +#answer{ + width: 400px; + position: center; + float: bottom; + background-color: rgb(232,232,232,0.2); + border-radius: 5px; +} + +#question_comment{ + width: 400px; + background-color: rgb(232,232,232,0.2) ; + color: black; + font-max-size: small; +} + +#answer_comment{ + width: 400px; + background-color: rgb(232,232,232,0.2); + color: black; + font-max-size: small; +} +.accepted_answer{ + background-color: #c38005; + width: 80px; +} + +.button { + background-color: #555555; + border: none; + color: white; + padding: 10px 24px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 20px; + border-radius: 4px; + +} + +.defaultbutton{ + background-color: #040404; + border: none; + color: rgba(87, 87, 87, 0.71); + padding: 10px 24px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 20px; + border-radius: 4px; +} + +h1 { + color: black; + text-align: center; + letter-spacing: 3px; + word-spacing: 8px; + text-shadow: 3px 2px gray; + } + +h2 { + color: black; + text-shadow: 3px 2px gray; +} + +thead { + color: black; + text-shadow: 3px 2px gray; +} + +#title_Color { + color: black; + text-shadow: 3px 2px gray; +} + +p.thicker { + font-weight: 900; +} + + +textarea{ + background-color: darkkhaki; + border-radius: 5px; +} + +.login{ + position: absolute; + right: 10px; +} + +.logout{ + position: absolute; + right: 10px; +} + +table, th, td{ + padding: 15px; + text-align: left; +} + +th, td{ + border-bottom: 1px solid #000000; +} + + diff --git a/templates/add_comment.html b/templates/add_comment.html new file mode 100644 index 000000000..a6752f55e --- /dev/null +++ b/templates/add_comment.html @@ -0,0 +1,31 @@ + + + + + ADD COMMENT + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +

Add Comment

+{% if comment == 'question' %} +
+ Comment:
+
+ + +{% elif comment == 'answer' %} + + Comment:
+
+ + + +{% endif %} +
+ + \ No newline at end of file diff --git a/templates/add_question.html b/templates/add_question.html new file mode 100644 index 000000000..1b8952294 --- /dev/null +++ b/templates/add_question.html @@ -0,0 +1,50 @@ + + + + + Add New Question + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +
{% include 'home_button.html' %}
+{% if result == [] %} +

Add New Question

+
+ Question Name:
+
+

+ Question: +
+
+

+ Image: +
+ +

+
+ +
+{% else %} +

Edit Question

+
+ Question Name:
+
+ Question:
+
+
+ + + + + + + + {% endif %} +
+ + \ No newline at end of file diff --git a/templates/all_question.html b/templates/all_question.html new file mode 100644 index 000000000..2d4d64858 --- /dev/null +++ b/templates/all_question.html @@ -0,0 +1,42 @@ + + + + + All Question + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +{% include 'home_button.html' %} +

ALL QUESTION

+
+ Sort by: + + + +
+

+

+ + + {% for question in questions%} + + + {% endfor %} + + +
+ {{question['title']}}
+ + \ No newline at end of file diff --git a/templates/all_users.html b/templates/all_users.html new file mode 100644 index 000000000..24e603703 --- /dev/null +++ b/templates/all_users.html @@ -0,0 +1,36 @@ + + + + + Users + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +
{% include 'home_button.html' %}
+

All User

+ + + + + + + + + {% for user in users %} + + + + +
+ + {% endfor %} + +
IDUser NameReputationSubmission Time
{{ user['id'] }}{{ user['user_name'] }}{{ user['reputation'] }}{{ user['submission_time']}}
+ + + \ No newline at end of file diff --git a/templates/home_button.html b/templates/home_button.html new file mode 100644 index 000000000..844d7218e --- /dev/null +++ b/templates/home_button.html @@ -0,0 +1,61 @@ + + + + + Home + + + + + + +
+ Home +
+

+
+ + +
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 000000000..c504b1990 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,50 @@ + + + + + Index + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +
{% include 'home_button.html' %}
+ +

ASK MATE

+{% if user_id != '' %} +User Page + {% endif %} +All User +{% if user_id != '' %} + Add new Question +{% else %} +Add new Question +{% endif %} +Show All Question +

LATEST 5 QUESTION:

+ + + + + + + + + {% for question in questions %} + + + + + + {% endfor %} + + + +
TitleSubmission TimeView NumberVote Number
+ {{ question['title'] }} {{ question['submission_time'] }}{{ question['view_number'] }}{{ question['vote_number'] }}
+
+ + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 000000000..0de23d461 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,20 @@ + + + + + Login + + +
+ +
+ +
+
+ + + +
+
+ + diff --git a/templates/logout.html b/templates/logout.html new file mode 100644 index 000000000..2d5accc5e --- /dev/null +++ b/templates/logout.html @@ -0,0 +1,14 @@ + + + + + Logout + + +
+ +
+ +
+ + \ No newline at end of file diff --git a/templates/navbar.html b/templates/navbar.html new file mode 100644 index 000000000..4dc17a733 --- /dev/null +++ b/templates/navbar.html @@ -0,0 +1,10 @@ + + + + + Navbar + + + + + \ No newline at end of file diff --git a/templates/new_answer.html b/templates/new_answer.html new file mode 100644 index 000000000..82e3700ad --- /dev/null +++ b/templates/new_answer.html @@ -0,0 +1,45 @@ + + + + + Post an Answer + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +{% include 'home_button.html' %} +{% if result == [] %} +

Post an Answer

+
+ +
+ Message +
+
+ Image +
+
+ +
+{% else %} +

Edit answer

+
+ Answer:
+
+
+ + + + + + + + {% endif %} +
+ + + diff --git a/templates/question_page.html b/templates/question_page.html new file mode 100644 index 000000000..12b6096bc --- /dev/null +++ b/templates/question_page.html @@ -0,0 +1,91 @@ + + + + + {{ question['title'] }} + + + +{% if session['verify'] == True %} +
{% include 'logout.html' %}
+{% else %} + +{% endif %} +
{% include 'home_button.html' %}
+
+ +

{{ question[0]['title'] }}

+
+ID :{{ question[0]['id'] }}
+Submission Time: {{ question[0]['submission_time'] }}
+View Number: {{ question[0]['view_number'] }}
+Vote Number: {{ question[0]['vote_number'] }}
+
+ +
+Message:

{{ question[0]['message'] }}


+Image:
+
+ +
+ +
+ +
+ +

Comments:

+{% for message in comment %} +
{{ message['message'] }}

+{% endfor %} + + + +
+ + + Vote Up Question
+ Vote Down Question
+ +
+ +

Answers:

+{% for answer in answers %} +
{{ answer['message'] }}

+ +

Vote Number: {{ answer['vote_number'] }}

+
+ + + + Vote Up Answer
+ Vote Down Answer
+ +
+ {% if answer['accepted'] == True %} +

accepted

+ {% else %} +
+ + + +
+ {% endif %} + comments:
+ {% for comment in answer_comment %} + {% if comment['answer_id'] == answer['id'] %} +
{{ comment['message'] }}

+ {% endif %} + {% endfor %} +
+ + +
+ +

+{% endfor %} +
+ +
+ + \ No newline at end of file diff --git a/templates/registration.html b/templates/registration.html new file mode 100644 index 000000000..74a8bf695 --- /dev/null +++ b/templates/registration.html @@ -0,0 +1,17 @@ + + + + + registration + + + +

registration

+
+ +
+ + +
+ + \ No newline at end of file diff --git a/templates/user_page.html b/templates/user_page.html new file mode 100644 index 000000000..001f73dd9 --- /dev/null +++ b/templates/user_page.html @@ -0,0 +1,83 @@ + + + + + User Page + + + +
{% include 'logout.html' %}
+
{% include 'home_button.html' %}
+

{{ user_data['user_name'] }}

+Reputation: {{ user_data['reputation'] }} +
+ID: {{ user_data['id'] }} +
+

QUESTIONS:

+ + + + + + + + + {% for question in questions %} + + + + + + + {% endfor %} + +
TitleMessageView NumberVote Number
{{ question['title'] }}{{ question['message'] }}{{ question['view_number'] }}{{ question['vote_number'] }}
+
+
+

ANSWERS:

+ + + + + + + + {% for answer in answers%} + + + + + + {% endfor %} + +
QuestionAnswerVote Number
{{ answer['question_title'] }} {{ answer['message'] }}{{ answer['vote_number'] }}
+
+
+

Comments:

+ + + + + + + + {% for comment in comments %} + + {% if comment['question'] != None %} + + {% else %} + + {% endif %} + {% if comment['answer'] != None %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
QuestionAnswerComment
{{comment['question']}}{{comment['answer']}}{{comment['comment']}}
+
+ + \ No newline at end of file diff --git a/user_data_manager.py b/user_data_manager.py new file mode 100644 index 000000000..96ee499d8 --- /dev/null +++ b/user_data_manager.py @@ -0,0 +1,64 @@ +import connection + + +@connection.connection_handler +def insert_new_user(cursor, submission_time, username, h_password, user_reputation): + cursor.execute(""" + insert into "user" (submission_time, user_name,hashed_password, reputation) + values (%(submission_time)s, %(username)s, %(h_password)s, %(user_reputation)s) + """, + {'submission_time':submission_time,'username': username, 'h_password': h_password, 'user_reputation':user_reputation}) + + +@connection.connection_handler +def find_user(cursor, username): + cursor.execute(""" + select user_name + from "user" + where user_name=%(username)s""", + {'username' : username}) + result = cursor.fetchone() + return result + + +@connection.connection_handler +def get_user_id(cursor, username): + cursor.execute(""" + select id from "user" + where user_name = %(username)s + """, + {'username': username}) + result = cursor.fetchone() + return result + +@connection.connection_handler +def get_hashed_password(cursor, user_name): + cursor.execute(""" + SELECT hashed_password + FROM "user" + WHERE user_name = %(user_name)s + """, + {'user_name' : user_name}) + result = cursor.fetchone() + return result + + +@connection.connection_handler +def select_all_user(cursor): + cursor.execute(""" + select * from "user" + ORDER BY id + """) + result=cursor.fetchall() + return result + + +@connection.connection_handler +def get_user_data(cursor, u_id): + cursor.execute(""" + select id, user_name, submission_time, reputation from "user" + where id = %(u_id)s + """, + {'u_id': u_id}) + result= cursor.fetchone() + return result diff --git a/util.py b/util.py new file mode 100644 index 000000000..e82686ed5 --- /dev/null +++ b/util.py @@ -0,0 +1,7 @@ +import datetime + + +def submission_time_generator(): + submission_time = datetime.datetime.now() + return submission_time +