diff --git a/connection.py b/connection.py new file mode 100644 index 000000000..abf636761 --- /dev/null +++ b/connection.py @@ -0,0 +1,74 @@ +import csv +import os +import psycopg2 +import psycopg2.extras + + +def csv_to_dict(file_path): + with open(file_path, 'r', newline='') as f: + reader = csv.DictReader(f) + database = [dict(row) for row in reader] + return database + + +def dict_to_csv(file_path, data, is_answers=False): + with open(file_path, 'w', newline='') as f: + writer = csv.DictWriter(f, ANSWER_DATA_HEADER if is_answers else QUESTION_DATA_HEADER) + writer.writeheader() + writer.writerows(data) + + +def append_to_csv(file_path, data): + with open(file_path, 'a', newline='') as f: + writer = csv.writer(f) + writer.writerows([data.values()]) + + +# 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. + + +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/data/answer.csv b/data/answer.csv new file mode 100644 index 000000000..3ddfa1ffb --- /dev/null +++ b/data/answer.csv @@ -0,0 +1,3 @@ +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/data/question.csv b/data/question.csv new file mode 100644 index 000000000..e65825dc3 --- /dev/null +++ b/data/question.csv @@ -0,0 +1,14 @@ +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/data_handler.py b/data_handler.py new file mode 100644 index 000000000..3f13990ee --- /dev/null +++ b/data_handler.py @@ -0,0 +1,146 @@ +import os +from datetime import datetime + +from psycopg2 import sql + +import connection +from util import string_builder + +ANSWER_DATA_FILE_PATH = os.getcwd() + "/data/answer.csv" +QUESTION_DATA_FILE_PATH = os.getcwd() + "/data/question.csv" + + +def get_answers(): + database = connection.csv_to_dict(ANSWER_DATA_FILE_PATH) + return database + + +def get_questions(): + database = connection.csv_to_dict(QUESTION_DATA_FILE_PATH) + return database + + +def save_questions(data): + database = connection.dict_to_csv(QUESTION_DATA_FILE_PATH, data) + return database + + +def save_answers(data): + database = connection.dict_to_csv(ANSWER_DATA_FILE_PATH, data, True) + return database + + +def add_entry(entry, is_answer=False): + table = "answer" + if not is_answer: + table = "question" + + entry = escape_single_quotes(entry) + query = """INSERT INTO {table} + ({columns}) VALUES ({values}); + """.format(columns=string_builder(entry.keys()), + values=string_builder(entry.values(), False), + table=table) + print(query) + execute_query(query) + + +def get_question(question_id): + question_query = f"""SELECT * FROM question + WHERE id={int(question_id)};""" + question_data = execute_query(question_query) + return question_data + + +def get_answer(answer_id, answer_database): + for answer_data in answer_database: + if answer_data['id'] == answer_id: + return answer_data + + +def get_question_related_answers(question_id): + answers_query = f"""SELECT * FROM answer + WHERE question_id={int(question_id)} + ORDER BY submission_time DESC;""" + answers_of_question = execute_query(answers_query) + return answers_of_question + + +def update_record(record, is_answer=False): + table = "answer" + record = escape_single_quotes(record) + id_ = record['id'] + if not is_answer: + table = "question" + query = f"""UPDATE {table} + SET submission_time={"'" + record['submission_time'] + "'"}, + title={"'" + record['title'] + "'"}, + message={"'" + record['message'] + "'"}, + image={"'" + record['image'] + "'"} + WHERE id={id_}; + """ + else: + query = f"""UPDATE {table} + SET submission_time={"'" + record['submission_time'] + "'"}, + message={"'" + record['title'] + "'"}, + image={"'" + record['image'] + "'"} + WHERE id={id_}; + """ + + execute_query(query) + + +def delete_record(id, answer=False, delete=False): + if answer: + question_id_query = f"""SELECT question_id FROM answer + WHERE id={id};""" + delete_answer_query = f"""DELETE FROM answer + WHERE id={id};""" + delete_comment_query = f"""DELETE FROM comment + WHERE answer_id={id};""" + question_id = execute_query(question_id_query)[0]['question_id'] + + if delete: + execute_query(delete_comment_query) + execute_query(delete_answer_query) + + return question_id + + +@connection.connection_handler +def execute_query(cursor, query): + # print(query.startswith("INSERT")) + if query.startswith("SELECT"): + cursor.execute( + sql.SQL(query) + ) + result = cursor.fetchall() + + else: + result = cursor.execute(query) + return result + + +def handle_add_comment(req): + req.update(submission_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + query ="""INSERT INTO comment ({columns}) + VALUES ({value_list})""".format(columns=string_builder(req.keys(), True), + value_list=string_builder(req.values(), False) + ) + execute_query(query) + + +def escape_single_quotes(dictionary): + for key, value in dictionary.items(): + if type(value) == str and "'" in value: + dictionary[key] = value.replace("'", "''") + return dictionary + + +def get_comments(comment_tpe, _id): + comment_tpe += "_id" + query = """SELECT message, submission_time, edited_count FROM comment + WHERE {col} = {id} + """.format(col=comment_tpe, id=_id) + print(query) + return execute_query(query) \ No newline at end of file diff --git a/sample_data/askmatepart2-sample-data.sql b/sample_data/askmatepart2-sample-data.sql index a51c461b8..369355a65 100644 --- a/sample_data/askmatepart2-sample-data.sql +++ b/sample_data/askmatepart2-sample-data.sql @@ -17,6 +17,7 @@ ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS pk_tag_id CASCAD ALTER TABLE IF EXISTS ONLY public.question_tag DROP CONSTRAINT IF EXISTS fk_tag_id CASCADE; DROP TABLE IF EXISTS public.question; +DROP SEQUENCE IF EXISTS public.question_id_seq; CREATE TABLE question ( id serial NOT NULL, submission_time timestamp without time zone, @@ -28,6 +29,7 @@ CREATE TABLE question ( ); DROP TABLE IF EXISTS public.answer; +DROP SEQUENCE IF EXISTS public.answer_id_seq; CREATE TABLE answer ( id serial NOT NULL, submission_time timestamp without time zone, @@ -38,6 +40,7 @@ CREATE TABLE answer ( ); DROP TABLE IF EXISTS public.comment; +DROP SEQUENCE IF EXISTS public.comment_id_seq; CREATE TABLE comment ( id serial NOT NULL, question_id integer, @@ -55,6 +58,7 @@ CREATE TABLE question_tag ( ); DROP TABLE IF EXISTS public.tag; +DROP SEQUENCE IF EXISTS public.tag_id_seq; CREATE TABLE tag ( id serial NOT NULL, name text diff --git a/server.py b/server.py new file mode 100644 index 000000000..fc11bb8c4 --- /dev/null +++ b/server.py @@ -0,0 +1,194 @@ +import os +from datetime import datetime +from flask import Flask, render_template, request, redirect, url_for +import data_handler +import util +from util import handle_delete_question, handle_add_answer, handle_add_question, create_check_keywords_in_database_string + +from util import handle_add_answer, handle_add_question +from data_handler import handle_add_comment + +app = Flask(__name__) +app.debug = True + +@app.route('/') +@app.route('/list') +@app.route('/?order_by=&order_direction=', methods=['GET', 'POST']) +def list_questions(): + ''' + Assign values to the parameters of the sorted_questions function. It happens by getting them from the user. + Sort the questions according to sorted questions's parameters. + Convert order_direction from boolean to string. It is needed for user interface (html). + :param questions:list of dictionaries + :return: + ''' + order_direction = False if request.args.get('order_direction') == 'asc' else True + order_by = 'submission_time' if request.args.get('order_by') == None else request.args.get('order_by') + order_direction = 'ASC' if order_direction == False else 'DESC' + + q = """SELECT * FROM question ORDER BY {order_by} {order_direction} + """.format(order_by=order_by, order_direction=order_direction) + questions = data_handler.execute_query(q) + return render_template('list.html', + sorted_questions=questions, + order_by=order_by, + order_direction=order_direction) + + +@app.route('/add-question', methods=["GET", "POST"]) +def add_question(): + if request.method == 'POST': + req = request.form.to_dict() + handle_add_question(req) + return redirect(url_for("list_questions")) + + return render_template("edit-question.html", qid="") + + +@app.route("/question//new-answer", methods=["GET", "POST"]) +def add_answer(question_id): + if request.method == 'POST': + req = request.form.to_dict() + handle_add_answer(req) + return redirect("/question/" + question_id) + + return render_template("add-answer.html", qid=question_id) + + +@app.route('/question/') +def question_display(question_id): + question = data_handler.get_question(question_id) + related_answers = data_handler.get_question_related_answers(question_id) + question_comments = data_handler.get_comments("question", question_id) + + return render_template('display_question.html', + question=question.pop(), + question_comments=question_comments, + answers=related_answers, + get_comments=data_handler.get_comments) + + +@app.route("/question//vote-up") +def vote_up_question(question_id): + util.vote_question(question_id, 1) + + return redirect("/question/" + question_id) + + +@app.route("/question//vote-down") +def vote_down_question(question_id): + util.vote_question(question_id, -1) + + return redirect("/question/" + question_id) + + +@app.route("/vote-answer", methods=["POST"]) +def vote_answer(): + if request.method == 'POST': + req = request.form.to_dict() + util.vote_answer(req["id"], req["vote"]) + question_id = req['question_id'] + return redirect("/question/" + question_id) + +@app.route('/question//delete', methods=['GET', 'POST']) +def delete_question(question_id): + if request.method == 'POST': + if request.form.get('delete') == 'Yes': + + q = """DELETE FROM comment WHERE question_id = {question_id} OR answer_id = (SELECT id FROM answer WHERE id = {question_id}) + """.format(question_id=question_id) + data_handler.execute_query(q) + q = """DELETE FROM answer WHERE question_id = {question_id} + """.format(question_id=question_id) + data_handler.execute_query(q) + q = """DELETE FROM question_tag WHERE question_id = {question_id} + """.format(question_id=question_id) + data_handler.execute_query(q) + q = """DELETE FROM question WHERE id = {question_id} + """.format(question_id=question_id) + data_handler.execute_query(q) + + # handle_delete_question(question_id) + return redirect(url_for('list_questions')) + + else: + return redirect(url_for('question_display', question_id=question_id)) + + return render_template('asking_if_delete_entry.html', question_id=question_id) + + +@app.route('//edit', methods=['GET', 'POST']) +def edit_question(question_id): + + if request.method == 'POST': + edited_question_data = request.form.to_dict() + edited_question_data['id'] = int(edited_question_data['id']) + edited_question_data['submission_time'] = str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + util.handle_edit_question(edited_question_data) + question = data_handler.get_question(question_id)[0] + related_answers = data_handler.get_question_related_answers(question_id) + + return render_template('display_question.html', + question=question, + answers=related_answers, + get_comments=data_handler.get_comments) + + question = data_handler.get_question(question_id)[0] + + return render_template('edit-question.html', question=question) + + +@app.route('/answer//delete', methods=['GET', 'POST']) +def delete_answer(answer_id): + if request.method == 'POST': + delete = False + if request.form.get('delete') == 'Yes': + delete = True + question_id = data_handler.delete_record(answer_id, True, delete=delete) + return redirect('/question/' + str(question_id)) + else: + return render_template('asking_if_delete_answer.html', answer_id=answer_id) + + +@app.route('/search-for-questions', methods=['GET', 'POST']) +def search_for_questions(): + keywords = str(request.args.get('keywords')).replace(',', '').split(' ') + questions_containing_keywords_query = """SELECT DISTINCT question.* FROM question + JOIN answer ON question.id = answer.question_id + WHERE (question.title LIKE {string_1}) + OR (question.message LIKE {string_2}) + OR (answer.message LIKE {string_3}) + """.format(string_1=create_check_keywords_in_database_string(keywords, 'question', 'title'), + string_2=create_check_keywords_in_database_string(keywords, 'question', 'message'), + string_3=create_check_keywords_in_database_string(keywords, 'answer', 'message')) + questions_containing_keywords = data_handler.execute_query(questions_containing_keywords_query) + + return render_template('search_for_keywords_in_questions.html', + keywords=keywords, fieldnames=util.QUESTION_DATA_HEADER, questions=questions_containing_keywords) + + +@app.route("/upload", methods=["POST"]) +def upload_image(): + image = request.files["image"] + image.save(os.path.join(os.getcwd() + "/images/", image.filename)) + + return redirect("/") + + +@app.route("/question//new-comment", methods=["GET", "POST"]) +@app.route("/answer//new-comment", methods=["GET", "POST"]) +def comment_question(id): + comment_type = "question" + if "answer" in str(request.url_rule): + comment_type = "answer" + + if request.method == 'POST': + req = request.form.to_dict() + handle_add_comment(req) + return redirect(url_for("question_display", question_id=id)) + return render_template("add-comment.html", qid=id, type=comment_type) + + +if __name__ == '__main__': + app.run(debug=True) + diff --git a/static/images/.gitignore b/static/images/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/static/images/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/static/images/Screenshot from 2019-09-12 21-17-50.png b/static/images/Screenshot from 2019-09-12 21-17-50.png new file mode 100644 index 000000000..62125ec28 Binary files /dev/null and b/static/images/Screenshot from 2019-09-12 21-17-50.png differ diff --git a/static/images/image1.png b/static/images/image1.png new file mode 100644 index 000000000..b8b2b3116 Binary files /dev/null and b/static/images/image1.png differ diff --git a/static/images/image2.jpeg b/static/images/image2.jpeg new file mode 100644 index 000000000..1d8969788 Binary files /dev/null and b/static/images/image2.jpeg differ diff --git a/templates/add-answer.html b/templates/add-answer.html new file mode 100644 index 000000000..34a9a5bda --- /dev/null +++ b/templates/add-answer.html @@ -0,0 +1,23 @@ + + + + + Title + + +
+

Answer question

+
+
+ +
+
+ + +
+
+ +
+
+ + \ No newline at end of file diff --git a/templates/add-comment.html b/templates/add-comment.html new file mode 100644 index 000000000..b66e57ef3 --- /dev/null +++ b/templates/add-comment.html @@ -0,0 +1,21 @@ + + + + + Title + + +
+

Comment

+
+
+ +
+
+ +
+ +
+
+ + \ No newline at end of file diff --git a/templates/add-question.html b/templates/add-question.html new file mode 100644 index 000000000..e8830417e --- /dev/null +++ b/templates/add-question.html @@ -0,0 +1,24 @@ + + + + + Title + + +
+

Ask your question

+
+
+ + +
+
+ + +
+
+ +
+
+ + \ No newline at end of file diff --git a/templates/asking_if_delete_answer.html b/templates/asking_if_delete_answer.html new file mode 100644 index 000000000..35c1023cb --- /dev/null +++ b/templates/asking_if_delete_answer.html @@ -0,0 +1,16 @@ + + + + + Delete answer + + +
+

Are you sure you want to delete this answer?

+
+ + +
+
+ + \ No newline at end of file diff --git a/templates/asking_if_delete_entry.html b/templates/asking_if_delete_entry.html new file mode 100644 index 000000000..3734a0dac --- /dev/null +++ b/templates/asking_if_delete_entry.html @@ -0,0 +1,16 @@ + + + + + Delete question + + +
+

Are you sure you want to delete this question?

+
+ + +
+
+ + \ No newline at end of file diff --git a/templates/display_question.html b/templates/display_question.html new file mode 100644 index 000000000..2eb222537 --- /dev/null +++ b/templates/display_question.html @@ -0,0 +1,179 @@ + +{% set headers = ['Vote', 'Question details', 'Image', 'Submission', 'View'] %} +{% set keys = [('vote_number', '10%', 'left'), ('message', '55%', 'left'), ('image', '20%', 'center'), ('submission_time', '10%', 'center'), ('view_number', '5%', 'center')] %} + + + + Question + + +

{{ question.title }}

+
+ + + + {% for header in headers %} + {% if header == "Vote" %} + + {% else %} + + {% endif %} + {% endfor %} + + + + + + + {% for key, width, align in keys %} + {% if key == 'submission_time' %} + + {% elif key == 'image' %} + + {% else %} + + {% endif %} + {% endfor %} + + +
{{ header }}{{ header }}
+ + +
+
+ + +
+
{{ question.get(key) }} + {% if question.get(key) != "" %} + Picture + {% endif %} + {{ question.get(key) }}
+
+ + + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ + + {% if question_comments | length > 0 %} + {% for title in question_comments[0].keys() %} + + {% endfor %} + {% endif %} + + + {% for comment in question_comments %} + + {% for value in comment.values() %} + + {% endfor %} + + {% endfor %} + +
+ {{ title }} +
+ {{ value }} +
+
+ + + {% set answer_headers = ['Vote', 'Answer', 'Image', 'Submission'] %} + {% set answer_keys = [('vote_number', '10%', 'left'), ('message', '55%', 'left'), ('image', '20%', 'center'), ('submission_time', '10%', 'center')] %} + +

Answers to the question

+ + {% if answers | length != 0 %} +
+ + + + {% for header in answer_headers %} + {% if header =="Vote" %} + + {% else %} + + {% endif %} + {% endfor %} + + + + {% for answer in answers %} + + + {% for key, width, align in answer_keys %} + {% if key == 'submission_time' %} + + {% elif key == 'image' %} + + {% else %} + + {% endif %} + + + {% endfor %} + + + {% set comments = get_comments("answer",answer["id"]) %} + {% for comment in comments %} + + + + {% endfor %} + {% endfor %} + +
{{ header}}{{ header}}
+
+ + + + +
+
+ + + + +
+
{{ answer.get(key) }} + {% if answer.get(key) != "" %} + Picture + {% endif %} + {{ answer.get(key) }} +
+ +
+
+ {{ comment["message"] }} +
+
+ {% else %} +

{{ "There is no answer to this question" }}

+ {% endif %} +
+ + + + + diff --git a/templates/edit-question.html b/templates/edit-question.html new file mode 100644 index 000000000..300b6264d --- /dev/null +++ b/templates/edit-question.html @@ -0,0 +1,28 @@ + + + + + {{"Edit question" if question else "Ask your Question" }} + + +
+

{{"Edit question" if question else "Ask your Question" }}

+
+
+ +
+ + +
+
+ +
+ +
+
+
+ + +
+ + \ No newline at end of file diff --git a/templates/list.html b/templates/list.html new file mode 100644 index 000000000..989e7eb79 --- /dev/null +++ b/templates/list.html @@ -0,0 +1,76 @@ + + + + + Welcome! | Ask Mate + + +
+

Ask Mate

+
+ +
+
+
+ +
+ +
+
+

+

+ +
+ +
+

+
+ + {% set fieldnames = ['title', 'message', 'image', 'vote_number', 'view_number', 'submission_time'] %} +

+ +

+ + + +
+

+ + + + {% for header in fieldnames %} + + {% endfor %} + + {% for questions in sorted_questions %} + + {% for cell in fieldnames %} + {% if cell == 'submission_time' %} + + {% elif cell == 'title' %} + + {% elif cell == 'image' %} + + {% else %} + + {% endif %} + {% endfor %} + {% endfor %} + +
{{ header|capitalize|replace('_', ' ') }}
{{ questions[cell] }} + {{ questions[cell] }} + + {% if questions[cell] %} + + {% endif %} + {{ questions[cell] }}
+ + \ No newline at end of file diff --git a/templates/search_for_keywords_in_questions.html b/templates/search_for_keywords_in_questions.html new file mode 100644 index 000000000..7292d87e5 --- /dev/null +++ b/templates/search_for_keywords_in_questions.html @@ -0,0 +1,29 @@ + + + + + Title + + +

Your keywords were found in the following questions:

+ + {% set fieldnames = ['title', 'message', 'image', 'vote_number', 'view_number', 'submission_time'] %} + + {% for cell in fieldnames %} + + {% endfor %} + + {% for question in questions %} + + {% for cell in fieldnames %} + {% if cell == 'submission_time' %} + + {% else %} + + {% endif %} + {% endfor %} + {% endfor %} + +
{{ cell|capitalize|replace('_', ' ') }}
{{ question[cell] }}{{ question[cell] }}
+ + \ No newline at end of file diff --git a/templates/test.html b/templates/test.html new file mode 100644 index 000000000..3e67ca142 --- /dev/null +++ b/templates/test.html @@ -0,0 +1,12 @@ + + + + + Title + + +{% autoescape false %} +{{ d }} +{% endautoescape %} + + diff --git a/util.py b/util.py new file mode 100644 index 000000000..adfe76f3d --- /dev/null +++ b/util.py @@ -0,0 +1,176 @@ +import copy +import os +from datetime import datetime +from flask import request +import data_handler + + +QUESTION_DATA_HEADER = ['id', 'submission_time', 'view_number', 'vote_number', 'title', 'message', 'image'] +ANSWER_DATA_HEADER = ['id', 'submission_time', 'vote_number', 'question_id', 'message', 'image'] + + +def vote_question(_id, vote): + # questions = data_handler.get_questions() + # question = data_handler.get_question(_id, questions) + # questions.remove(question) + # question["vote_number"] = str(int(question["vote_number"]) + vote) + # questions.append(question) + # data_handler.save_questions(questions) + # data_handler.save_questions(questions) + query ="""UPDATE question SET vote_number = question.vote_number +{vote} + WHERE id = {id} + """.format(vote=vote,id=_id) + data_handler.execute_query(query) + + +def vote_answer(_id, vote): + delta = 1 if vote == "up" else -1 + # answers = data_handler.get_answers() + # answer = data_handler.get_answer(_id, answers) + # answers.remove(answer) + # answer["vote_number"] = str(int(answer["vote_number"]) + delta) + # answers.append(answer) + # data_handler.save_answers(answers) + query ="""UPDATE answer SET vote_number = vote_number +{vote} + WHERE id = {id} + """.format(vote=delta,id=_id) + data_handler.execute_query(query) + + +def handle_upload(req): + image = request.files["image"] + if image.filename != "": + req["image"] = "images/" + image.filename + image.save(os.path.join(os.getcwd() + "/static/images/", image.filename)) + else: + req["image"] = "" + + +def gen_question_id(): + answers = get_questions() + items = [x['id'] for x in answers] + return int(max(items)) + 1 + + +def gen_answer_id(): + # depricated - Marked for removal + answers = get_answers() + if len(answers) == 0: + return 0 + items = [x['id'] for x in answers] + return int(max(items)) + 1 + + +def generate_question_dict(data): + question_data = {} + + # question_data.update(id="") + question_data.update(submission_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + question_data.update(view_number=str(0)) + question_data.update(vote_number=str(0)) + question_data.update(title=data["title"]) + question_data.update(message=data["message"]) + question_data.update(image=data["image"]) + return question_data + + +def generate_answer_dict(data): + answer_data = {} + + # answer_data.update(id=str(gen_answer_id())) + # answer_data.update(submission_time=str(int(time.time()))) + answer_data.update(submission_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + answer_data.update(vote_number=str(0)) + answer_data.update(question_id=data["question_id"]) + answer_data.update(message=data["message"]) + answer_data.update(image=data["image"]) + return answer_data + + +def handle_delete_question(question_id): + question_database = data_handler.get_questions() + answer_database = data_handler.get_answers() + copied_answer_database = copy.deepcopy(answer_database) + for answer in copied_answer_database: + if answer['question_id'] == question_id: + answer_database.remove(answer) + for question in question_database: + if question['id'] == question_id: + question_database.remove(question) + data_handler.save_questions(question_database) + data_handler.save_answers(answer_database) + + +def handle_add_answer(reqv): + handle_upload(reqv) + answer = generate_answer_dict(reqv) + answers = data_handler.get_answers() + answers.append(answer) + data_handler.add_entry(answer, True) + + +def handle_add_question(req): + # questions = data_handler.get_questions() + handle_upload(req) + question = generate_question_dict(req) + # questions.append(question) + data_handler.add_entry(question) + + +def handle_edit_question(req): + handle_upload(req) + data_handler.update_record(req, is_answer=False) + + +def get_answer_related_question_ids(keywords, answer_database, attribute): + """ + Search keywords in database using attribute as a key. If it founds a keyword + in the attribute, then its related question id is stored. + :param keywords: list + :param answer_database: list of dictionaries + :param attribute: string + :return: list of item related question ids + """ + answer_related_question_ids = [] + for answer in answer_database: + if any(keyword in answer[attribute] for keyword in keywords): + answer_related_question_ids.append(answer['question_id']) + return answer_related_question_ids + + +def create_check_keywords_in_database_string(keywords, database, attribute): + string = f'\'%{keywords[0]}%\'' + for keyword in keywords[1:]: + string += f' OR {database}.{attribute} LIKE \'%{keyword}%\'' + return string + + +def search_keywords_in_attribute(keywords, id_s, database, attribute_1, attribute_2=None): + """ + Search keywords in table using attribute_1 and/or attribute_2 as key(s). + Search id in id_s in order to find and append other database related items. + :param keywords: list + :param id_s: list + :param database: list of dictionaries + :param attribute_1: string + :param attribute_2: string + :return: list of items containing keywords + """ + items_containing_keywords = [] + for item in database: + if any(keyword in item[attribute_1] for keyword in keywords) or \ + any(keyword in item[attribute_2] for keyword in keywords): + items_containing_keywords.append(item) + elif item['id'] in id_s: + items_containing_keywords.append(item) + return items_containing_keywords + + +def string_builder(lst, is_key=True): + result = "" + for element in lst: + if is_key: + result += "" + element + ", " + else: + result += "\'" + element + "\', " + return result[:-2] \ No newline at end of file