diff --git a/.gitignore b/.gitignore index dbed2ff1f..35c7602c7 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,15 @@ target/ # pyenv .python-version +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + # celery beat schedule file celerybeat-schedule @@ -101,4 +110,4 @@ ENV/ .mypy_cache/ # pycharm files -.idea/ \ No newline at end of file +.idea/ diff --git a/data_handler.py b/data_handler.py new file mode 100644 index 000000000..ec28922d9 --- /dev/null +++ b/data_handler.py @@ -0,0 +1,101 @@ +import os +import database_manager + +ANSWER_FILE_PATH = 'sample_data/answer.csv' +QUESTION_FILE_PATH = 'sample_data/question.csv' +IMAGE_UPLOAD_PATH = "static/images" +ALLOWED_IMAGE_TYPE = ["PNG", "JPG"] +ROOT_PATH = 'static/images' + + +def sort_data(list_of_dicts, order_by, order_direction): + converted_list = convert_numbers_in_list_to_int(list_of_dicts) + sorted_list_of_dicts = sorted(converted_list, key=lambda item: item[order_by], + reverse=True if order_direction == 'desc' else False) + return sorted_list_of_dicts + + +def convert_numbers_in_list_to_int(all_data): + for i in range(len(all_data)): + all_data[i]['vote_number'] = int(all_data[i]['vote_number']) + try: + all_data[i]['view_number'] = int(all_data[i]['view_number']) + except KeyError: + continue + return all_data + + +def allowed_image(filename): + if not "." in filename: + return False + ext = filename.rsplit(".", 1)[1] + if ext.upper() in ALLOWED_IMAGE_TYPE: + return True + else: + return False + + +def get_image_path_for_question_by_id(question_id): + delete_file = [] + question = database_manager.get_question_by_id(question_id) + answers_by_question = database_manager.get_all_answer_by_question_id_sorted(question_id) + delete_file.append(question[0]['image']) + for answer in answers_by_question: + delete_file.append(answer['image']) + return delete_file + + +def get_image_path_for_answer_by_id(answer_id): + answer = database_manager.get_answer_by_id(answer_id) + return answer[0]['image'] + + +def delete_image_by_id(id, answer=False): + try: + if answer: + path = get_image_path_for_answer_by_id(id) + os.remove(path) + else: + path = get_image_path_for_question_by_id(id) + for file in path: + os.remove(file) + except: + pass + + +def apostroph_change(text): + change = ('title', 'message', 'name') + new_text = text + for column in change: + try: + new_text[column] = text[column].replace('\'', '\"') + except: + continue + return new_text + +def search_highlight(text, search_word): + change = ('title', 'message', 'name') + new_text = text + for element_index in range(len(text)): + for column in change: + try: + new_text[element_index][column] = text[element_index][column].replace(f'{search_word}', f'{search_word}') + except: + continue + return new_text + +def remove_from_list(questions): + remove = [] + for index in range(len(questions)): + if questions[index]['id'] is None: + remove.append(questions[index]) + for element in remove: + questions.remove(element) + return questions + +def tag_duplicate_check(tag): + all_tag = database_manager.all_tag_name() + for element in all_tag: + if element['name'] == tag: + return True + return False \ No newline at end of file diff --git a/database/ask_mate_database.sql b/database/ask_mate_database.sql new file mode 100644 index 000000000..92f9e18db --- /dev/null +++ b/database/ask_mate_database.sql @@ -0,0 +1,42 @@ +DROP TABLE IF EXISTS question; +DROP TABLE IF EXISTS answer; +DROP TABLE IF EXISTS comment; +DROP TABLE IF EXISTS question_tag; +DROP TABLE IF EXISTS tag_; + +CREATE TABLE question ( + id SERIAL PRIMARY KEY, + submission_time integer NOT NULL, + view_number integer, + vote_number integer, + title character varying(2550), + message character varying(2550) NOT NULL, + image character varying(2550) +); + +CREATE TABLE answer ( + id SERIAL PRIMARY KEY, + submission_time character varying(255) NOT NULL, + vote_number integer, + question_id integer, + message character varying(255) NOT NULL, + image character varying(255) +); + +CREATE TABLE comment ( + id SERIAL PRIMARY KEY, + question_id integer, + answer_id integer, + message character varying(255) NOT NULL, + submission_time character varying(255) NOT NULL, + edited_number integer +); + +INSERT INTO question VALUES + (0, 1493368154, 29, 7, 'How to make lists in Python?', 'I am totally new to this, any hints?', NULL), + (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)', NULL); + +INSERT INTO answer VALUES + (0, 1493398154, 5, 0, 'You need to use brackets: my_list = []'), + (1, 1493088154, 35, 0, 'Look it up in the Python docs'); \ No newline at end of file diff --git a/database_common.py b/database_common.py new file mode 100644 index 000000000..57921ae24 --- /dev/null +++ b/database_common.py @@ -0,0 +1,46 @@ +import os +import psycopg2 +import psycopg2.extras +from psycopg2 import sql + + +def get_connection_string(): + 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: + 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() + 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/database_manager.py b/database_manager.py new file mode 100644 index 000000000..8bf51fbd0 --- /dev/null +++ b/database_manager.py @@ -0,0 +1,306 @@ +import database_common +from psycopg2.extensions import AsIs + + +@database_common.connection_handler +def get_all_questions_sorted(cursor, order_by='submission_time', order_direction='asc'): + cursor.execute(f""" + SELECT * FROM question + ORDER BY {order_by} {order_direction}; + """) + all_questions_sorted = cursor.fetchall() + return all_questions_sorted + + +@database_common.connection_handler +def get_five_latest_questions_sorted(cursor, order_by='submission_time', order_direction='DESC'): + cursor.execute(f""" + SELECT * FROM + ( + SELECT * FROM question + ORDER BY submission_time DESC + LIMIT 5 + ) AS T1 ORDER BY {order_by} {order_direction}; + """) + five_latest_questions_sorted = cursor.fetchall() + return five_latest_questions_sorted + + +@database_common.connection_handler +def add_question(cursor, new_question): + cursor.execute(""" + INSERT INTO question (submission_time, view_number, vote_number, title, message, image) + VALUES (%s, %s, %s, %s, %s, %s); + """, + (new_question['submission_time'], + new_question['view_number'], + new_question['vote_number'], + new_question['title'], + new_question['message'], + new_question['image'])) + + +@database_common.connection_handler +def add_answer(cursor, new_answer): + cursor.execute(f""" + INSERT INTO answer (submission_time, vote_number, question_id, message, image) + VALUES ('{new_answer['submission_time']}', {new_answer['vote_number']}, {new_answer['question_id']}, '{new_answer['message']}', '{new_answer['image']}'); + """) + + +@database_common.connection_handler +def get_question_by_id(cursor, question_id): + cursor.execute(f""" + SELECT * FROM question + WHERE id = {question_id}; + """) + question = cursor.fetchall() + return question + + +@database_common.connection_handler +def get_all_comment_from_question_id(cursor, question_id): + cursor.execute(f""" + SELECT * FROM comment + WHERE question_id={question_id} AND answer_id is null;""") + question_comment = cursor.fetchall() + return question_comment + + +@database_common.connection_handler +def get_all_comment_from_answer_id(cursor, question_id): + cursor.execute(f""" + SELECT * FROM comment + WHERE question_id={question_id} and answer_id is not null;""") + answer_comment = cursor.fetchall() + return answer_comment + + +@database_common.connection_handler +def get_answer_by_id(cursor, answer_id): + cursor.execute(f""" + SELECT * FROM answer + WHERE id = {answer_id}; + """) + answer = cursor.fetchall() + return answer + + +@database_common.connection_handler +def get_comment_by_id(cursor, comment_id): + cursor.execute(f""" + SELECT * FROM comment + WHERE id = {comment_id}; + """) + comment = cursor.fetchall() + return comment + + +@database_common.connection_handler +def get_all_answer_by_question_id_sorted(cursor, question_id, order_by='submission_time', order_direction='asc'): + cursor.execute(f""" + SELECT * FROM answer + WHERE question_id={question_id} + ORDER BY {order_by} {order_direction} + """) + answers = cursor.fetchall() + return answers + + +@database_common.connection_handler +def delete_question(cursor, question_id): + delete_tag_by_question_id(question_id) + cursor.execute(f""" + DELETE FROM comment + WHERE question_id = {question_id}""") + cursor.execute(f""" + DELETE FROM answer + WHERE question_id = {question_id};""") + cursor.execute(f""" + DELETE FROM question + WHERE id = {question_id};""") + + +@database_common.connection_handler +def delete_answer(cursor, answer_id): + cursor.execute(f""" + DELETE FROM comment + WHERE answer_id = {answer_id}""") + cursor.execute(f""" + DELETE FROM answer + WHERE id = {answer_id}""") + + +@database_common.connection_handler +def delete_comment(cursor, comment_id): + cursor.execute(f""" + DELETE FROM comment + WHERE id = {comment_id}""") + + +@database_common.connection_handler +def search_in_questions(cursor, search_phrase): + cursor.execute(F""" + SELECT question.id, question.submission_time, question.view_number, question.vote_number, question.title, question.message, question.image + FROM question + FULL JOIN answer a on question.id = a.question_id + FULL JOIN question_tag qt on question.id = qt.question_id + FULL JOIN tag t on qt.tag_id = t.id + WHERE question.title LIKE '%{search_phrase}%' + OR question.message LIKE '%{search_phrase}%' + OR a.message LIKE '%{search_phrase}%' + OR t.name LIKE '%{search_phrase}%'; + """) + searched_question = cursor.fetchall() + return searched_question + + +@database_common.connection_handler +def search_in_answers(cursor, search_phrase): + cursor.execute(F""" + SELECT * + FROM answer + WHERE message LIKE '%{search_phrase}%' + """) + searched_answer = cursor.fetchall() + return searched_answer + + +@database_common.connection_handler +def write_new_comment(cursor, to_write_dict): + columns = to_write_dict.keys() + values = [to_write_dict[column] for column in columns] + + insert_statement = 'insert into comment (%s) values %s' + + cursor.execute(insert_statement, (AsIs(','.join(columns)), tuple(values))) + + +@database_common.connection_handler +def vote(cursor, id, type, vote): + cursor.execute(f""" + UPDATE {type} + SET vote_number = vote_number {vote} 1 + WHERE id = {id} + """) + + +@database_common.connection_handler +def update_question(cursor, updated_question): + cursor.execute(f""" + UPDATE question + SET title = '{updated_question['title']}', message = '{updated_question['message']}' + WHERE id = {updated_question['id']}""") + + +@database_common.connection_handler +def view_up(cursor, id): + cursor.execute(f""" + UPDATE question + SET view_number = view_number + 1 + WHERE id = {id}""") + + +@database_common.connection_handler +def update_answer(cursor, answer, id): + cursor.execute(f""" + UPDATE answer + SET message = '{answer['message']}' + WHERE id = {id}""") + + +@database_common.connection_handler +def update_comment(cursor, comment, id): + cursor.execute(f""" + UPDATE comment + SET message = '{comment['message']}', edited_count=edited_count+1, submission_time='{comment['submission_time']}' + WHERE id = {id}""") + + +@database_common.connection_handler +def all_tag_name(cursor): + cursor.execute(""" + SELECT * FROM tag + """) + tags = cursor.fetchall() + return tags + + +@database_common.connection_handler +def tag_id_by_tag_name(cursor, tag): + cursor.execute(f""" + SELECT * FROM tag + WHERE name = '{tag}' + """) + tag = cursor.fetchall() + return tag + + +@database_common.connection_handler +def add_tag(cursor, tag, question_id): + cursor.execute(f""" + INSERT INTO tag (name) + VALUES ('{tag['name']}'); + """) + tag_id = tag_id_by_tag_name(tag['name'])[0] + cursor.execute(f""" + INSERT INTO question_tag (question_id, tag_id) + VALUES ({question_id}, {tag_id['id']}); + """) + +@database_common.connection_handler +def add_old_tag(cursor, tag, question_id): + tag_id = tag_id_by_tag_name(tag['name'])[0] + cursor.execute(f""" + INSERT INTO question_tag (question_id, tag_id) + VALUES ({question_id}, {tag_id['id']}); + """) + + +@database_common.connection_handler +def delete_tag_by_question_id(cursor, question_id): + cursor.execute(f""" + DELETE FROM question_tag + WHERE question_id = {question_id} + """) + + +@database_common.connection_handler +def delete_tag(cursor, tag_id, question_id): + cursor.execute(f""" + DELETE FROM question_tag + WHERE tag_id = {tag_id} AND question_id = {question_id} + """) + + +@database_common.connection_handler +def all_question_by_tag_id(cursor, tag_id): + cursor.execute(f""" + SELECT question.id, question.submission_time, question.view_number, question.vote_number, question.title, question.message, question.image + FROM question + FULL JOIN question_tag qt on question.id = qt.question_id + WHERE qt.tag_id = {tag_id} + """) + questions = cursor.fetchall() + return questions + + +@database_common.connection_handler +def tag_by_tag_id(cursor, tag_id): + cursor.execute(f""" + SELECT * FROM tag + WHERE id = {tag_id} + """) + tag = cursor.fetchall() + return tag + + +@database_common.connection_handler +def all_tag(cursor): + cursor.execute(f""" + SELECT * FROM question_tag + FULL JOIN tag t on question_tag.tag_id = t.id + WHERE question_tag.question_id IS NOT null; + """) + tags = cursor.fetchall() + return tags diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..a6d2d3aa7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +Click==7.0 +Flask==1.1.1 +itsdangerous==1.1.0 +Jinja2==2.10.3 +MarkupSafe==1.1.1 +Werkzeug==0.16.0 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..369355a65 --- /dev/null +++ b/sample_data/askmatepart2-sample-data.sql @@ -0,0 +1,127 @@ +-- +-- 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; + +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, + view_number integer, + vote_number integer, + title text, + message text, + image text +); + +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, + vote_number integer, + question_id integer, + message text, + image text +); + +DROP TABLE IF EXISTS public.comment; +DROP SEQUENCE IF EXISTS public.comment_id_seq; +CREATE TABLE comment ( + id serial NOT NULL, + question_id integer, + answer_id integer, + message text, + submission_time timestamp without time zone, + edited_count 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; +DROP SEQUENCE IF EXISTS public.tag_id_seq; +CREATE TABLE tag ( + id serial NOT NULL, + name text +); + + +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 comment + ADD CONSTRAINT fk_answer_id FOREIGN KEY (answer_id) REFERENCES answer(id); + +ALTER TABLE ONLY answer + ADD CONSTRAINT fk_question_id FOREIGN KEY (question_id) REFERENCES question(id); + +ALTER TABLE ONLY question_tag + ADD CONSTRAINT fk_question_id FOREIGN KEY (question_id) REFERENCES question(id); + +ALTER TABLE ONLY comment + ADD CONSTRAINT fk_question_id FOREIGN KEY (question_id) REFERENCES question(id); + +ALTER TABLE ONLY question_tag + ADD CONSTRAINT fk_tag_id FOREIGN KEY (tag_id) REFERENCES tag(id); + +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 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..003cc9de0 --- /dev/null +++ b/server.py @@ -0,0 +1,173 @@ +from flask import Flask, render_template, redirect, request, url_for +import data_handler +import database_manager +import os +from werkzeug.utils import secure_filename +app = Flask(__name__) + + +@app.route('/') +@app.route('/lists') +def route_lists(): + questions = data_handler.get_all_questions() + try: + order_by = request.args['order_by'] + order_direction = request.args['order_direction'] + except: + order_by = 'submission_time' + order_direction = 'asc' + + sorted_questions = data_handler.sort_data(questions, order_by, order_direction) + return render_template("lists.html", question=sorted_questions, order_by=order_by, order_direction=order_direction) + + +@app.route('/add_question', methods=['GET', 'POST']) +def route_new_question(): + if request.method == 'POST': + comment = {'title': request.form.get('title'), + 'message': request.form.get('message'), + 'submission_time': request.form.get('submission_time'), + 'view_number': request.form.get('view_number'), + 'vote_number': request.form.get('vote_number'), + } + if request.files['image'].filename != "": + + image = request.files['image'] + if not data_handler.allowed_image(image.filename): + return redirect(request.url) + else: + filename = secure_filename(image.filename) + + image.save(os.path.join(data_handler.IMAGE_UPLOAD_PATH, filename)) + comment.update({'image': f"{data_handler.IMAGE_UPLOAD_PATH}/{image.filename}"}) + + data_handler.add_question(comment) + return redirect('/lists') + + return render_template("add_question.html", + comment_name='Add new question', + form_url=url_for('route_new_question'), + comment_title='Question title', + comment_message='Question message', + type='question') + + +@app.route('/question/') +def route_question(question_id): + question = data_handler.one_question(question_id, time=True) + print(question['image']) + answers = data_handler.all_answer_for_one_question(question_id) + try: + order_by = request.args['order_by'] + order_direction = request.args['order_direction'] + except: + order_by = 'submission_time' + order_direction = 'asc' + sorted_answers = data_handler.sort_data(answers, order_by, order_direction) + + return render_template("answer.html", + question=question, + answer=sorted_answers, order_by=order_by, order_direction=order_direction) + + +@app.route('/question//new-answer', methods=['GET', 'POST']) +def route_new_answer(question_id): + if request.method == 'POST': + comment = {'message': request.form.get('message'), + 'submission_time': request.form.get('submission_time'), + 'vote_number': request.form.get('vote_number'), + 'image': request.form.get('image'), + 'question_id': request.form.get('question_id')} + data_handler.add_answer(comment, question_id) + return redirect(f'/question/{question_id}') + + return render_template("add_question.html", + type='answer', + comment_name='Add new answer', + form_url=url_for('route_new_answer', question_id=question_id), + comment_message='Answer message', + question_id=question_id, + timestamp=data_handler.date_time_in_timestamp()) + +@app.route('/question//delete') +def delete_question(question_id): + data_handler.delete_image_by_question_id(question_id) + data_handler.delete_question(question_id) + data_handler.delete_answers_by_question_id(question_id) + return redirect('/lists') + +@app.route('/answer//delete') +def delete_answer(answer_id): + questionid = data_handler.search_question_id_by_answer(answer_id) + data_handler.delete_specific_answer(answer_id) + return redirect(f'/question/{questionid}') + + +@app.route('/question//vote_up') +def question_vote_up(question_id): + data_handler.vote(question_id) + return redirect(f'/question/{question_id}') + + +@app.route('/question//vote_down') +def question_vote_down(question_id): + data_handler.vote(question_id, type_vote_up=False) + return redirect(f'/question/{question_id}') + + +@app.route('/answer//vote_up') +def answer_vote_up(answer_id): + data_handler.vote(answer_id, question_type=False) + question_id = data_handler.search_question_id_by_answer(answer_id) + return redirect(f'/question/{question_id}') + + +@app.route('/answer//vote_down') +def answer_vote_down(answer_id): + question_id = data_handler.search_question_id_by_answer(answer_id) + data_handler.vote(answer_id, question_type=False, type_vote_up=False) + return redirect(f'/question/{question_id}') + + +@app.route('/question//edit',methods = ['GET', 'POST']) +def edit_question(question_id): + question = data_handler.get_question_by_id(question_id) + if request.method == 'POST': + edited_question = {} + datas_from_question = ['id', 'submission_time', 'view_number', 'vote_number', 'image'] + for data in datas_from_question: + edited_question[data] = question[data] + datas_from_edit = ['title', 'message'] + for data in datas_from_edit: + edited_question[data] = request.form[data] + + data_handler.update_question(edited_question) + return redirect(url_for('route_question', question_id=edited_question['id'])) + + return render_template('edit_question.html', question=question) + + +@app.route('/answer//edit', methods=['GET', 'POST']) +def edit_answer(answer_id): + answer = data_handler.get_answer_by_id(answer_id) + if request.method == 'POST': + edited_answer = {} + datas_from_answer = ['id', 'submission_time', 'vote_number', 'question_id', 'image'] + for data in datas_from_answer: + edited_answer[data] = answer[data] + datas_from_edit = ['message'] + for data in datas_from_edit: + edited_answer[data] = request.form[data] + + data_handler.update_answer(edited_answer) + return redirect(url_for('route_question', question_id=edited_answer['question_id'])) + + return render_template('edit_answer.html', answer=answer) + + +if __name__ == "__main__": + app.run( + host='0.0.0.0', + port=8000, + debug=True, + ) diff --git a/server_database.py b/server_database.py new file mode 100644 index 000000000..1555c4615 --- /dev/null +++ b/server_database.py @@ -0,0 +1,325 @@ +from flask import Flask, render_template, redirect, request, url_for +import data_handler +import database_manager +import os +from werkzeug.utils import secure_filename +from datetime import datetime + +app = Flask(__name__) + + +@app.route('/') +def main_page(): + order_by = request.args.get('order_by', 'submission_time') + order_direction = request.args.get('order_direction', 'desc') + tags = database_manager.all_tag() + first_five_sorted_questions = database_manager.get_five_latest_questions_sorted(order_by, order_direction) + return render_template("lists.html", + question=first_five_sorted_questions, + order_by=order_by, + order_direction=order_direction, + tags=tags) + + +@app.route('/lists') +def route_lists(): + order_by = request.args.get('order_by', 'submission_time') + order_direction = request.args.get('order_direction', 'asc') + tags = database_manager.all_tag() + sorted_questions = database_manager.get_all_questions_sorted(order_by, order_direction) + return render_template("lists.html", + question=sorted_questions, + order_by=order_by, + order_direction=order_direction, + tags=tags) + + +@app.route('/add_question', methods=['GET', 'POST']) +def route_new_question(): + if request.method == 'POST': + new_question = {'submission_time': datetime.now(), + 'title': request.form.get('title'), + 'message': request.form.get('message'), + 'view_number': request.form.get('view_number'), + 'vote_number': request.form.get('vote_number'), + 'image': request.form.get('image')} + if request.files['image'].filename != "": + + image = request.files['image'] + if not data_handler.allowed_image(image.filename): + return redirect(request.url) + else: + filename = secure_filename(image.filename) + + image.save(os.path.join(data_handler.IMAGE_UPLOAD_PATH, filename)) + new_question.update({'image': f"{data_handler.IMAGE_UPLOAD_PATH}/{image.filename}"}) + new_question = data_handler.apostroph_change(new_question) + database_manager.add_question(new_question) + return redirect('/lists') + + return render_template("add_question.html", + comment_name='Add new question', + form_url=url_for('route_new_question'), + comment_title='Question title', + comment_message='Question message', + type='question') + + +@app.route('/view_up/') +def view_up(question_id): + question = database_manager.get_question_by_id(question_id)[0] + question['view_number'] = question['view_number'] + 1 + database_manager.view_up(question['id']) + return redirect(f'/question/{question_id}') + + +@app.route('/question/') +def route_question(question_id): + question = database_manager.get_question_by_id(question_id) + order_by = request.args.get('order_by', 'submission_time') + order_direction = request.args.get('order_direction', 'asc') + sorted_answers = database_manager.get_all_answer_by_question_id_sorted(question_id, order_by, order_direction) + + question_comment = database_manager.get_all_comment_from_question_id(question_id) + answer_comment = database_manager.get_all_comment_from_answer_id(question_id) + tags = database_manager.all_tag() + return render_template("answer.html", + question=question[0], + answer=sorted_answers, + question_comment=question_comment, + answer_comment=answer_comment, + order_by=order_by, + order_direction=order_direction, + tags=tags) + +@app.route('/question//') +def full_screen(question_id, image): + image_route = '/' + data_handler.ROOT_PATH + '/' + image + return render_template('full_image.html', + image=image_route, + question_id=question_id) + +@app.route('/question//new-answer', methods=['GET', 'POST']) +def route_new_answer(question_id): + if request.method == 'POST': + new_answer = {'message': request.form.get('message'), + 'vote_number': request.form.get('vote_number'), + 'image': request.form.get('image'), + 'question_id': request.form.get('question_id'), + 'submission_time': datetime.now()} + if request.files['image'].filename != "": + image = request.files['image'] + if not data_handler.allowed_image(image.filename): + return redirect(request.url) + else: + filename = secure_filename(image.filename) + + image.save(os.path.join(data_handler.IMAGE_UPLOAD_PATH, filename)) + new_answer.update({'image': f"{data_handler.IMAGE_UPLOAD_PATH}/{image.filename}"}) + new_answer = data_handler.apostroph_change(new_answer) + database_manager.add_answer(new_answer) + return redirect(f'/question/{question_id}') + + return render_template("add_question.html", + type='answer', + comment_name='Add new answer', + form_url=url_for('route_new_answer', question_id=question_id), + comment_message='Answer message', + question_id=question_id) + + +@app.route('/question//delete') +def delete_question(question_id): + data_handler.delete_image_by_id(question_id) + database_manager.delete_question(question_id) + return redirect('/lists') + + +@app.route('/answer//delete') +def delete_answer(answer_id): + question_id = database_manager.get_answer_by_id(answer_id)[0]['question_id'] + data_handler.delete_image_by_id(answer_id, answer=True) + database_manager.delete_answer(answer_id) + return redirect(f'/question/{question_id}') + + +@app.route('/question//vote_up') +def question_vote_up(question_id): + database_manager.vote(question_id, type='question', vote='+') + return redirect((url_for('route_question', question_id=question_id))) + +@app.route('/question//vote_down') +def question_vote_down(question_id): + database_manager.vote(question_id, type='question', vote='-') + return redirect(f'/question/{question_id}') + + +@app.route('/answer//vote_up') +def answer_vote_up(answer_id): + question = database_manager.get_answer_by_id(answer_id) + question_id = question[0]['question_id'] + database_manager.vote(answer_id, type='answer', vote='+') + return redirect(f'/question/{question_id}') + + +@app.route('/answer//vote_down') +def answer_vote_down(answer_id): + answer = database_manager.get_answer_by_id(answer_id) + question_id = answer[0]['question_id'] + database_manager.vote(answer_id, type='answer', vote='-') + return redirect(f'/question/{question_id}') + + +@app.route('/question//edit', methods=['GET', 'POST']) +def edit_question(question_id): + question = database_manager.get_question_by_id(question_id)[0] + if request.method == 'POST': + datas_from_edit = ['title', 'message'] + for data in datas_from_edit: + question[data] = request.form[data] + question['submission_time'] = datetime.now() + question = data_handler.apostroph_change(question) + database_manager.update_question(question) + return redirect(url_for('route_question', question_id=question_id)) + + return render_template('edit_question.html', + question=question, + from_url=url_for('edit_question', question_id=question_id)) + + +@app.route('/answer//edit', methods=['GET', 'POST']) +def edit_answer(answer_id): + answer = database_manager.get_answer_by_id(answer_id)[0] + if request.method == 'POST': + datas_from_edit = ['message'] + for data in datas_from_edit: + answer[data] = request.form[data] + answer['submission_time'] = datetime.now() + answer = data_handler.apostroph_change(answer) + database_manager.update_answer(answer, answer_id) + return redirect(url_for('route_question', question_id=answer['question_id'])) + + return render_template('edit_answer.html', + answer=answer, + from_url=url_for('edit_answer', answer_id=answer_id)) + + +@app.route('/search') +def route_search(): + search_phrase = request.args.get('search') + questions = database_manager.search_in_questions(search_phrase) + data_handler.remove_from_list(questions) + questions = data_handler.search_highlight(questions, search_phrase) + answers = database_manager.search_in_answers(search_phrase) + answers = data_handler.search_highlight(answers, search_phrase) + tags = database_manager.all_tag() + return render_template('Search.html', + question=questions, + answer=answers, + type='search', + search_word=search_phrase, + tags=tags) + + +@app.route('/question//new_comment', methods=['GET', 'POST']) +def add_new_comment_to_question(question_id): + if request.method == 'POST': + new_comment = request.form.to_dict() + new_comment['submission_time'] = datetime.now() + new_comment = data_handler.apostroph_change(new_comment) + database_manager.write_new_comment(new_comment) + return redirect(f'/question/{question_id}') + + return render_template("new_comment.html", + comment_name='Add Comment', + form_url=url_for('add_new_comment_to_question', question_id=question_id), + comment_message='Add Comment', + question_id=question_id, ) + + +@app.route('/answer//new_comment', methods=['GET', 'POST']) +def add_new_comment_to_answer(answer_id): + question_id = database_manager.get_answer_by_id(answer_id)[0]['question_id'] + if request.method == 'POST': + new_comment = request.form.to_dict() + new_comment['submission_time'] = datetime.now() + new_comment = data_handler.apostroph_change(new_comment) + database_manager.write_new_comment(new_comment) + return redirect(f'/question/{question_id}') + + return render_template("new_comment.html", + comment_name='Add Comment', + type='answer', + form_url=url_for('add_new_comment_to_answer', answer_id=answer_id), + comment_message='Add Comment', + answer_id=answer_id, + question_id=question_id) + +@app.route('/comment//edit' , methods=['GET','POST']) +def edit_comment(comment_id): + comment = database_manager.get_comment_by_id(comment_id)[0] + if request.method == 'POST': + datas_from_edit = ['message'] + for data in datas_from_edit: + comment[data] = request.form[data] + comment['submission_time']=datetime.now() + comment = data_handler.apostroph_change(comment) + database_manager.update_comment(comment, comment_id) + return redirect(url_for('route_question', question_id=comment['question_id'])) + + return render_template('edit_answer.html', + comment=comment, + type='comment', + from_url=url_for('edit_comment', comment_id=comment_id)) + +@app.route('/comment//delete') +def delete_comment(comment_id): + comment = database_manager.get_comment_by_id(comment_id)[0] + database_manager.delete_comment(comment_id) + return redirect(url_for('route_question', question_id=comment['question_id'])) + +@app.route('/question//new_tag', methods=['GET', 'POST']) +def add_tag(question_id): + all_tag = database_manager.all_tag_name() + if request.method == 'POST': + tag = {} + new_tag = request.form.get('new_tag') + old_tag = request.form.get('use_tag') + if new_tag is not '': + tag['name'] = new_tag + duplicate = data_handler.tag_duplicate_check(new_tag) + if duplicate: + database_manager.add_old_tag(tag, question_id) + else: + database_manager.add_tag(tag, question_id) + else: + tag['name'] = old_tag + database_manager.add_old_tag(tag, question_id) + return redirect(f'/question/{question_id}') + + return render_template('add_tag.html', + question_id=question_id, + tags=all_tag) + +@app.route('/question//tag//delete') +def delete_tag(question_id, tag_id): + database_manager.delete_tag(tag_id, question_id) + return redirect(f'/question/{question_id}') + +@app.route('/tag/search/') +def search_with_tag(tag_id): + questions_by_tag_id = database_manager.all_question_by_tag_id(tag_id) + tags = database_manager.all_tag() + tag = database_manager.tag_by_tag_id(tag_id)[0] + return render_template('Search.html', + question=questions_by_tag_id, + tag=tag, + tags=tags) + + +if __name__ == "__main__": + app.run( + host='0.0.0.0', + port=8000, + debug=True, + ) diff --git a/static/css/basic.css b/static/css/basic.css new file mode 100644 index 000000000..ed05c2488 --- /dev/null +++ b/static/css/basic.css @@ -0,0 +1,187 @@ +body { + font-family: "JetBrains Mono", "arial", "helvetica", serif; + font-size: 14px; + background-image: url("../images/romans.jpg"); + background-attachment: fixed; +} + +#content { + background-color: white; + opacity: 0.9; + margin: 5% 10%; + padding: 2%; +} + +h1 { + font-size: 4em; + font-style: normal; + margin: 2%; + padding: 1% 9%; + color : gold; +} +strong { + color: darkred; + font-style: italic; +} + +h5 { + font-size: 1em; + color: black; +} + +p { + letter-spacing: 0.5em; + word-spacing: 2em; + line-height: 1.5; + text-align: center; +} + +a { + color: darkred; + text-decoration: none; + font-size: 1.3em; +} + +table { + color : darkred; + table-layout: auto; + font-size: 1.4em; +} + +td { + padding: 1em; +} + +tr { + display: block; +} + +label { + color: black; + font-size: 1.3em; +} + +ul.ribbon { + background-color: black; + opacity: 0.8; + padding: 1.6% 6%; +} + +ul.ribbon li { + display: inline; + padding: 0 4%; + text-transform: uppercase; +} + +ul.ribbon li a { + color: gold; + font: normal 2em arial; +} + +a.question-title:hover, ul.ribbon a:hover { + color: red; +} + +#question_listed { + background-color: gold; + margin: 1em; + border-radius: 0.6em; + border: 1px darkred solid; +} + +b { + color: green; + background-color: aqua; +} + +form.order_by { + display: inline-block; + font-size: 1.5em; +} + +form.search { + display: inline-block; + float: right; + font-size: 1.5em; +} + +select, button, input { + font-size: 1em; +} + +td.question-statistics { + list-style-type: none; + text-align: center; + padding: 0.5em; +} + +td.question-statistics p { + letter-spacing: normal; + word-spacing: normal; + margin: 0; +} + +p.show-big { + font-size: 1.6em; + padding-top: 0.5em; + font-weight: bold; +} + +table.question { + width: 100%; + position: relative; +} + +td.question-content { + width: 100%; + position: relative; + border-left: darkred 1px solid; + +} + +a.question-title{ + color: black; + font-weight: bold; + display: inline-block; +} + +p.question-message { + letter-spacing: normal; + word-spacing: normal; + text-align: left; +} + +table.tags { + display: inline-block; +} + +#question-submission-data-box { + display: inline-block; + float: right; +} + +#question-submission-data { + position: absolute; + bottom: 1em; + right: 1em; +} + +ul.tags li { + display: inline; + border: 1px solid darkred; + border-radius: 5px; + padding: 0.5em; +} + +ul.tag-buttons li { + display: inline; +} + +ul.tag-buttons li a { + color: black; + font-size: 1.5em; +} + +ul.tag-buttons li a:hover { + color: red; +} \ No newline at end of file diff --git a/static/images/background.jpg b/static/images/background.jpg new file mode 100644 index 000000000..c5db89221 Binary files /dev/null and b/static/images/background.jpg differ diff --git a/static/images/bg.png b/static/images/bg.png new file mode 100644 index 000000000..49e023e0f Binary files /dev/null and b/static/images/bg.png differ diff --git a/static/images/romans.jpg b/static/images/romans.jpg new file mode 100644 index 000000000..d75f51210 Binary files /dev/null and b/static/images/romans.jpg differ diff --git a/static/images/ronaldo.jpg b/static/images/ronaldo.jpg new file mode 100644 index 000000000..98068c5e6 Binary files /dev/null and b/static/images/ronaldo.jpg differ diff --git a/templates/Search.html b/templates/Search.html new file mode 100644 index 000000000..74be29b22 --- /dev/null +++ b/templates/Search.html @@ -0,0 +1,78 @@ +{% extends 'layout.html' %} + +{% block content %} + {% if type == 'search' %} +

Search with: {{ search_word }}

+ {% else %} +

Search with: {{ tag['name'] }} tag

+ {% endif %} + {% for quest in question %} + + + + + + + + + + + + + +
ID:
{{ quest['id'] }}
Title:
{{ quest['title'] | safe }}
Submission time:
{{ quest['submission_time'] }}
View:
{{ quest['view_number'] }}
Like:
{{ quest['vote_number'] }}
+ + + + + + {% if quest['image'] is not none %} + + {% endif %} + +
Message:
{{ quest['message'] | safe }}{{ quest['image'] }}

+ + + + {% for tag in tags %} + {% if tag['question_id'] == quest['id'] %} + + {% endif %} + {% endfor %} + + + + +
Tags:
{{ tag['name'] }} + x
Add new tag
+ {% if type == 'search' %} + {% for comment in answer %} + {% if quest['id'] == comment['question_id'] %} + + + + + + + + + +
ID: {{ comment['id'] }}
Submission time:
{{ comment['submission_time'] }}
Like:
{{ comment['vote_number'] }}
+ + + + + + {% if comment['image'] is not none %} + + {% endif %} + +
Message:
{{ comment['message'] | safe }}
+ {% endif %} + {% endfor %} + {% endif %} + {% endfor %} +

+ Back +

+{% endblock %} \ No newline at end of file diff --git a/templates/add_question.html b/templates/add_question.html new file mode 100644 index 000000000..781209669 --- /dev/null +++ b/templates/add_question.html @@ -0,0 +1,33 @@ +{% extends 'layout.html' %} + +{% block content %} +

{{ comment_name }}

+
+
+ +

+ {% if type == 'question' %} +
+ + {% endif %} +

+

+
+ +

+

+ + +

+ {% if comment is not defined and type == "question" %} + + + {% elif comment is not defined and type == "answer" %} + + + {% endif %} +

+ +

+
+{% endblock %} \ No newline at end of file diff --git a/templates/add_tag.html b/templates/add_tag.html new file mode 100644 index 000000000..54d544e8e --- /dev/null +++ b/templates/add_tag.html @@ -0,0 +1,15 @@ +{% extends 'layout.html' %} + +{% block content %} +
+

+

+

+ +

+
+{% endblock %} \ No newline at end of file diff --git a/templates/answer.html b/templates/answer.html new file mode 100644 index 000000000..ea0a141db --- /dev/null +++ b/templates/answer.html @@ -0,0 +1,129 @@ +{% extends 'layout.html' %} + +{% block content %} + + + + + + + + + + + + + + + + + +
ID:
{{ question['id'] }}
Title:
{{ question['title'] | safe }}
Submission time:
{{ question['submission_time'] }}
View:
{{ question['view_number'] }}
Like:
{{ question['vote_number'] }}
Vote:
+-comment
+ + + + + {% if question['image'] is not none %} + + {% endif %} + +
Message:
{{ question['message'] | safe }}
+ + + + + + +
Add new answerDeleteEdit question
+ + + + {% for tag in tags %} + {% if tag['question_id'] == question['id'] %} + + {% endif %} + {% endfor %} + + + + +
Tags:
{{ tag['name'] }} + x
Add new tag
+
+ {% for q_comment in question_comment %} + + + + + + + + + + + + +
{{ q_comment['submission_time'] }}Time's edited: {{ q_comment['edited_count'] }}EditDelete
{{ q_comment['message'] }}
+ {% endfor %} +
+ + + + +
+ {% for comment in answer %} + + + + + + + + + + + + + + +
ID: {{ comment['id'] }}
Submission time:
{{ comment['submission_time'] }}
Like:
{{ comment['vote_number'] }}
Vote:
+-DeleteEdit answer
+ + + + + + {% if comment['image'] is not none %} + + {% endif %} + + +
Message:
{{ comment['message'] | safe }}comment
+ {% for a_comment in answer_comment %} + {% if a_comment['answer_id'] == comment['id'] %} + + + + + + + + + + + + +
{{ a_comment['submission_time'] }}Time's edited: {{ a_comment['edited_count'] }}EditDelete
{{ a_comment['message'] }}
+ {% endif %} + {% endfor %} + {% endfor %} +

+ Back +

+{% endblock %} \ No newline at end of file diff --git a/templates/edit_answer.html b/templates/edit_answer.html new file mode 100644 index 000000000..530b4d1e6 --- /dev/null +++ b/templates/edit_answer.html @@ -0,0 +1,25 @@ +{% extends 'layout.html' %} + +{% block content %} + {% if type=="comment"%} +

Edit comment

+

Comment id: {{ comment['id'] }}

+
+

+
+ +

+ +
+ {%else%} +

Edit answer

+

Answer id: {{ answer['id'] }}

+
+

+
+ +

+ +
+ {%endif%} +{% endblock %} \ No newline at end of file diff --git a/templates/edit_question.html b/templates/edit_question.html new file mode 100644 index 000000000..c001cd607 --- /dev/null +++ b/templates/edit_question.html @@ -0,0 +1,18 @@ +{% extends 'layout.html' %} + +{% block content %} +

Edit question

+

Question id: {{ question['id'] }}

+
+

+
+ +

+

+
+ +

+ + +
+{% endblock %} \ No newline at end of file diff --git a/templates/footer.html b/templates/footer.html new file mode 100644 index 000000000..e69de29bb diff --git a/templates/full_image.html b/templates/full_image.html new file mode 100644 index 000000000..8ef5f63bc --- /dev/null +++ b/templates/full_image.html @@ -0,0 +1,10 @@ + + + + + Full screen + + + + + \ No newline at end of file diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 000000000..e30de7d46 --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,20 @@ + + + + + Ask Mate Forum + + + +

Ask Mate

+ +
+ {% block content %} {% endblock %} +
+ {% include 'footer.html' %} + + \ No newline at end of file diff --git a/templates/lists.html b/templates/lists.html new file mode 100644 index 000000000..bbc82bddf --- /dev/null +++ b/templates/lists.html @@ -0,0 +1,62 @@ +{% extends 'layout.html' %} + +{% block content %} +
+ + + + +
+ +{% for quest in question %} +
+ + + + + + +
+

ID: {{ quest['id'] }}

+

{{ quest['vote_number'] }}

+

votes

+

{{ quest['view_number'] }}

+

views

+
+ {{ quest['title'] | safe }} +

{{ quest['message'] }}

+
    + {% for tag in tags %} + {% if tag['question_id'] == quest['id'] %} +
  • {{ tag['name'] }} + x
  • + {% endif %} + {% endfor %} +
+ +
+
Submission time: {{ quest['submission_time'] }}
+
+
+
+ {% if quest['image'] is not none %} + image + {% endif %} +
+
+ +
+ {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/templates/new_comment.html b/templates/new_comment.html new file mode 100644 index 000000000..34d1f8594 --- /dev/null +++ b/templates/new_comment.html @@ -0,0 +1,30 @@ +{% extends 'layout.html' %} + +{% block content %} +

{{ comment_name }}

+
+ {% if type == 'answer' %} +
+ + + +

+
+ +

+ +

+
+ {% else %} +
+ + +

+
+ +

+ +

+
+ {% endif %} +{% endblock %} \ No newline at end of file