diff --git a/backend/app/__init__.py b/backend/app/__init__.py index 4ee948e..0af96df 100644 --- a/backend/app/__init__.py +++ b/backend/app/__init__.py @@ -1,6 +1,10 @@ from flask import Flask +from flask_cors import CORS app = Flask(__name__, static_folder='static') + +cors = CORS(app, resources={r"/api/*": {"origins": ["http://localhost:4200"]}}) + app.secret_key = ' key' app.config['JSON_AS_ASCII'] = False diff --git a/backend/app/routes.py b/backend/app/routes.py index bba5d59..40f0ebc 100644 --- a/backend/app/routes.py +++ b/backend/app/routes.py @@ -15,12 +15,12 @@ conn = Neo4jConnection(uri, user, password) -@app.route('/') +@app.route('/api/') @app.route('/index') def index(): return redirect(url_for('db_page', entity_type="Disease")) -@app.route('/register', methods=['GET', 'POST']) +@app.route('/api/register', methods=['GET', 'POST']) def register() -> str: ''' Функция отвечает за регистрацию пользователя. Включает в себя валидацию данных, введённых при регистрации. @@ -35,18 +35,20 @@ def register() -> str: ''' msg : str = None - if request.method == 'POST' and 'full_name' in request.form and \ + if request.method == 'POST' and 'fullname' in request.form and \ 'password' in request.form and \ - 'email' in request.form and \ + 'confirmed_password' in request.form and \ + 'mail' in request.form and \ 'sex' in request.form and \ 'birthday' in request.form and \ 'height' in request.form and \ 'weight' in request.form and \ "admin" in request.form: - fullname : str = request.form['full_name'] + fullname : str = request.form['fullname'] password : str = request.form['password'] - mail : str = request.form['email'] + confirmed_password : str = request.form['confirmed_password'] + mail : str = request.form['mail'] sex : str = request.form['sex'] birthday : str = request.form['birthday'] height : float = request.form['height'] @@ -54,27 +56,29 @@ def register() -> str: admin : bool = request.form['admin'] query_string : str = ''' - MATCH(p:Patient {email: $email}) + MATCH(p:Patient {mail: $mail}) RETURN p ''' - patient : list[Record] = conn.query(query_string, {"email": email}) + patient : list[Record] = conn.query(query_string, {"mail": mail}) if patient: msg = "Данная почта уже занята!" - elif not re.match(r'[^@]+@[^@]+\.[^@]+', email): + elif not re.match(r'[^@]+@[^@]+\.[^@]+', mail): msg = "Почта введена некорректно" - elif not re.match(r'[А-Яа-я]+', full_name): + elif not re.match(r'[А-Яа-я]+', fullname): msg = "Имя может содержать только буквы!" - elif not full_name or not password: + elif password != confirmed_password: + msg = "Пароли не совпадают!" + elif not fullname or not password: msg = "Пожалуйста, заполните форму!" else: query_string = ''' - MERGE (p:Patient {fullname: $fullname, password: $password, mail: $email, sex: $sex, birthday: $birthday, height: $height, weight: $weight, registration_date: $rd, admin: $admin}) + MERGE (p:Patient {fullname: $fullname, password: $password, mail: $mail, sex: $sex, birthday: $birthday, height: $height, weight: $weight, registration_date: $rd, admin: $admin}) ''' - conn.query(query_string, {"fullname": fullname, "password": password, "mail": email, + conn.query(query_string, {"fullname": fullname, "password": password, "mail": mail, "sex": sex, "birthday": birthday, "rd": datetime.now().isoformat(), "height": height, "weight": weight, "admin": admin}) msg = "Success" @@ -84,7 +88,7 @@ def register() -> str: return msg -@app.route('/login', methods=['GET', 'POST']) +@app.route('/api/login', methods=['POST']) def login() -> str: ''' Функция отвечает за вход пользователя в аккаунт. Совершает поиск по почте и паролю. @@ -98,50 +102,49 @@ def login() -> str: render_template('login.html', msg = msg) (string) : возвращаем шаблон страницы с комментарием ''' msg = None - if request.method == 'POST' and 'email' in request.form and 'password' in request.form: + user_dict = {} + + data : dict = request.json + mail : str = data.get('mail') + password : str = data.get('password') + + if request.method == 'POST' and mail and password: + query_string : str = ''' - MATCH(p:Patient {email: $email, password: $password}) + MATCH(p:Patient {mail: $mail, password: $password}) RETURN p ''' - patient : list[Record] = conn.query(query_string, {"email": request.form['email'], "password": request.form['password']}) + patient : list[Record] = conn.query(query_string, {"mail": mail, "password": password}) if patient: patient_data : dict = patient[0].data()["p"] - session["loggedin"] = True - session["mail"] = patient_data["mail"] - session["fullname"] = patient_data["fullname"] - session["admin"] = patient_data["admin"] + user_dict["fullname"] = patient_data["fullname"] + user_dict["password"] = patient_data["password"] + user_dict["mail"] = patient_data["mail"] + user_dict["sex"] = patient_data["sex"] + user_dict["birthday"] = patient_data["birthday"] + user_dict["height"] = patient_data["height"] + user_dict["weight"] = patient_data["weight"] + user_dict["admin"] = patient_data["admin"] + else: msg = 'Неправильный логин или пароль' - elif request.method == 'GET': - return render_template('account.html', session = session, certain_page = False) - if msg is None: - return redirect(url_for('db_page', entity_type="Disease")) else: - return render_template('account.html', session = session, certain_page = False, err = msg) + msg = 'Заполните данные логина и пароля' + + return jsonify({"msg": msg, "user_data": user_dict}) -@app.route('/logout', methods=['GET']) -def logout() -> Response: - ''' - Функция отвечает за выход пользователя из аккаунта. - Удаляет данные пользователя из текущей сессии. - - Возвращаемые данные: - redirect(url_for('login')) (BaseResponse) : переадресация на страницу для входа - ''' - session.pop('loggedin', None) - session.pop('email', None) - session.pop('full_name', None) - session.pop('admin', None) - - return redirect(url_for('login')) -@app.route('/entities', methods=['POST']) +@app.route('/api/entities', methods=['POST']) def readEntities() -> json: ''' - Функция отвечает за чтение любой сущности из базы данных. + Функция отвечает за чтение любой сущности из базы данных. Фильтрация имеет следующий вид: + + {"filter_params": {"filter1-field": "height", "filter1-action": "IN", "filter1-value": "[0,5]", + "filter2-field": "fullname", "filter2-action": "CONTAINS", "filter2-value": "ушков", + "filter3-field": "sex", "filter3-action": "IS", "filter3-value": "male", ...}} Ключевые переменные: entity_type (str) : наименование сущности, которую надо считать из БД @@ -150,13 +153,27 @@ def readEntities() -> json: jsonify(entities_parametrs_list) (json) : массив со словарями, которые хранят параметры всех нодов с меткой "entity_type". ''' + data : dict = request.json + entity_type : str = data.get('entity_type') + filter_params : str = data.get('filter_params', {}) - entity_type : str = request.form['entity_type'] + query_string : str = "" + tmp_filter_string : str = "" + + query_string : str = f'MATCH(p:{entity_type})\n' + + if filter_params: + + query_string += f'WHERE p.{filter_params["filter1-field"]} {filter_params["filter1-action"]} {filter_params["filter1-value"]}' + + filter_idx = 2 + + while(filter_params.get(f'filter{filter_idx}-field')): + query_string += "AND\n" + query_string += f'WHERE p.{filter_params[f'filter{filter_idx}-field']} {filter_params[f'filter{filter_idx}-action']} {filter_params[f'filter{filter_idx}-value']}' + + query_string += '\nRETURN p' - query_string : str = f''' - MATCH(p:{entity_type}) - RETURN p - ''' entities_list : list[Record] = conn.query(query_string) @@ -169,7 +186,7 @@ def readEntities() -> json: return jsonify(entities_parametrs_list) -@app.route('/create_entity', methods=['POST']) +@app.route('/api/create_entity', methods=['POST']) def createEntities(): ''' Функция отвечает за добавление элемента сущности в базу данных. Разрешено добавление только @@ -187,8 +204,6 @@ def createEntities(): параметры всех нодов с меткой "entity_type". ''' - print(request) - data : json = request.json entity_type : str = data.get('entity_type') entity_parametrs : dict = data.get('parametrs', {}) @@ -211,50 +226,8 @@ def createEntities(): else: return jsonify({"Error": "Invalid format of form"}), 400 - -@app.route('/db/', methods=['GET']) -def db_page(entity_type): - ''' - Функция отвечает за получение данных, создание таблицы сущностей определённого типа и её визуализацию. - Ключевые переменные: - entity_type (str) : наименование сущности, которую надо добавить в БД - data (dict) : база данных с требуемыми запрошенными по entity_type сущностями - - - Возвращаемые данные: - render_template('data_bases.html', session = session, certain_page = False, entity_type = entity_type, lst = data) (string) : возвращаем шаблон страницы с таблицей и данными о пользователе - ''' - response = requests.post("http://127.0.0.1:5000/entities", data={'entity_type': entity_type}) - data = response.json() - - match(entity_type): - case 'Disease': - data.insert(0, {"name": "Наименование", \ - "description": "Описание", \ - "recommendations": "Рекомендации", \ - "type": "Возбудитель", \ - "course": "Протекание болезни"} ) - case 'Patient': - data.insert(0, {"full_name": "Фамилия и Имя", \ - "email": "Почта", \ - "password": "Пароль", \ - "sex": "Пол", \ - "birthday": "День рождения", \ - "last_update": "Время последнего действия", \ - "registration_date": "Дата регистрации", \ - "height": "Рост", \ - "weight": "Вес", \ - "admin": "Права администратора"}) - case 'Appeal': - data.insert(0, {"date": "Дата", "complaints": "Жалобы"}) - case 'Symptom': - data.insert(0, {"name": "Наименование", "description": "Описание"}) - - return render_template('data_bases.html', session = session, certain_page = False, entity_type = entity_type, lst = data) - - -@app.route('/import_dump', methods=['POST']) +@app.route('/api/import_dump', methods=['POST']) def import_dump(): query_strings : list(str) = [ ''' @@ -332,7 +305,7 @@ def import_dump(): return "Success" -@app.route('/export_dump', methods=['POST']) +@app.route('/api/export_dump', methods=['POST']) def export_dump(): file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'models/dumps/dump.csv')) @@ -341,7 +314,7 @@ def export_dump(): writer.writeheader() for entity_type in allowed_entity_parameters.keys(): - response = requests.post("http://127.0.0.1:5000/entities", data={'entity_type': entity_type}) + response = requests.post("http://127.0.0.1:5000/api/entities", data={'entity_type': entity_type}) data = response.json() for row in data: @@ -363,5 +336,4 @@ def export_dump(): row_dict = create_relation_dict(relation.data()["a"], relation.data()["b"], relation['r'].get('symptom_weight', None), relation_type) writer.writerow(row_dict) - return "Success" - \ No newline at end of file + return "Success" \ No newline at end of file diff --git a/backend/app/static/styles/db.css b/backend/app/static/styles/db.css deleted file mode 100644 index efaa83f..0000000 --- a/backend/app/static/styles/db.css +++ /dev/null @@ -1,183 +0,0 @@ -body{ - margin: 0px; - font-family: "Comfortaa", sans-serif; -} - - -.header{ - width: 100%; - height: 100px; - background-color: #21D9D9; - padding: 0px; -} - -#searching{ - width: 290px; - height: 50px; - border-radius: 25px; - - padding: 0; - border: none; - background: #FFFFFF; - color: #000000; - - font-family: "Comfortaa", sans-serif; - font-size: 18px; - - padding: 8px 8px 8px 8px; - font-weight: 400; - font-size: 18px; - line-height: 1.3; - text-align: right; - - margin-left: 30px; - margin-top: 25px; -} - -h1{ - width: 270px; - height: 70px; - color: #FFFFFF; - font-weight: 400; - font-size: 60px; - text-align: center; - - margin-left: auto; - margin-right: auto; - - position: relative; - top: -110px; -} - -#title { - width: 291px; - height: 50px; - color: #232323; - font-weight: 400; - font-size: 18px; - text-align: center; - - margin-left: auto; - margin-right: auto; - - position: relative; - top: -150px; -} - -#exit{ - width: 60px; - height: 60px; - border-radius: 50px; - - padding: 0; - border: none; - background: #FFFFFF; - color: #000000; - - font-family: "Comfortaa", sans-serif; - font-size: 14px; - - position: relative; - top: -275px; - - float: right; - margin-right: 30px; - - -} - -.menu{ - width: auto; - height: 150px; - color: #21D9D9; - font-weight: 400; - font-size: 40px; - text-align: center; - - margin: 30px; - margin-top: 50px; - - text-decoration: none; - - position: relative; - top: 30px; -} - -.selected{ - text-decoration: underline; -} - -#left_panel { - width: 417px; - height: 103px; - padding: 8px 8px 8px 8px; - background: #21D9D9; - border-radius: 10px 10px 10px 10px; - - margin-left: 30px; - - position: relative; - -} - -#list{ - width: 1337px; - height: 100%; - color: #21D9D9; - border-radius: 10px 10px 0px 0px; - - font-weight: 400; - font-size: 24px; - line-height: 1.3; - text-align: center; - - float: right; - margin-right: 30px; - - position: relative; - bottom: 117px; - - max-width: 100%; - overflow-x: auto; - -} - -table{ - background-color: #FFFFFF; - border: 1px solid #21D9D9; - -} - -th{ - background-color: #21D9D9; - border: 1px solid #21D9D9; - color: #FFFFFF; - - margin: 0px; - border-radius: 10px 10px 0px 0px; -} - -td{ - background-color: #FFFFFF; - border: 1px solid #21D9D9; -} - -.modal { - display: none; - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(0,0,0,0.4); -} - -.modal-content { - background-color: #fefefe; - margin: 15% auto; - padding: 20px; - border: 1px solid #888; - width: 80%; -} diff --git a/backend/app/static/styles/header.css b/backend/app/static/styles/header.css deleted file mode 100644 index 0d0bfe2..0000000 --- a/backend/app/static/styles/header.css +++ /dev/null @@ -1,144 +0,0 @@ -body{ - margin: 0px; - font-family: "Comfortaa", sans-serif; -} - - -.header{ - width: 100%; - height: 100px; - background-color: #21D9D9; - padding: 0px; -} - -button{ - height: 50px; - border-radius: 25px; - padding: 0; - border: none; - background: #FFFFFF; - color: #000000; - - font-family: "Comfortaa", sans-serif; - font-size: 18px; - - padding: 8px 8px 8px 8px; - font-weight: 400; - font-size: 18px; - line-height: 1.3; - text-align: right; - } - - #searching{ - width: 290px; - - - padding: 0; - border: none; - background: #FFFFFF; - color: #000000; - - font-family: "Comfortaa", sans-serif; - font-size: 18px; - - padding: 8px 8px 8px 8px; - font-weight: 400; - font-size: 18px; - line-height: 1.3; - text-align: right; - - margin-left: 30px; - margin-top: 25px; - } - - .enter{ - float: right; - margin-right: 30px; - - position: relative; - top: -265px; - } - - .exit{ - float: right; - position: relative; - top: -270px; - } - - #name{ - float: right; - position: relative; - top: -275px; - right: 10px; - - height: 40px; - color: #FFFFFF; - font-weight: 400; - font-size: 22px; - - } - - #status{ - float: right; - position: relative; - top: -245px; - left: 160px; - - width: 173px; - height: 38px; - color: #1EB9B9; - - font-weight: 400; - font-size: 18px; - text-align: left; - } - -h1{ - width: 270px; - height: 70px; - color: #FFFFFF; - font-weight: 400; - font-size: 60px; - text-align: center; - - margin-left: auto; - margin-right: auto; - - position: relative; - top: -110px; -} - -#title { - width: 291px; - height: 50px; - color: #232323; - font-weight: 400; - font-size: 18px; - text-align: center; - - margin-left: auto; - margin-right: auto; - - position: relative; - top: -150px; -} - -#enter{ - width: 60px; - height: 60px; - border-radius: 50px; - - padding: 0; - border: none; - background: #FFFFFF; - color: #000000; - - font-family: "Comfortaa", sans-serif; - font-size: 14px; - - position: relative; - top: -275px; - - float: right; - margin-right: 30px; -} diff --git a/backend/app/static/styles/login.css b/backend/app/static/styles/login.css deleted file mode 100644 index 9f5d5a2..0000000 --- a/backend/app/static/styles/login.css +++ /dev/null @@ -1,11 +0,0 @@ -#login { - background-color: white; - padding: 20px; - border-radius: 5px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - text-align: center; -} - -#login h2 { - margin-top: 0; -} \ No newline at end of file diff --git a/backend/app/templates/account.html b/backend/app/templates/account.html deleted file mode 100644 index 7f2899d..0000000 --- a/backend/app/templates/account.html +++ /dev/null @@ -1,16 +0,0 @@ - -{% import 'header.html' as header %} -{{ header.input(session, certain_page, "login") }} - -
-

Войти

-
-
-
-
-
- - {% if err is defined -%}

{{err}}

{% endif -%} - - -
\ No newline at end of file diff --git a/backend/app/templates/data_bases.html b/backend/app/templates/data_bases.html deleted file mode 100644 index 1891263..0000000 --- a/backend/app/templates/data_bases.html +++ /dev/null @@ -1,241 +0,0 @@ - - {% import 'header.html' as header %} - {{ header.input(session, certain_page, "db") }} - - - -
- {% if session.admin == "True" -%} - - {% endif -%} -
-
- - - {% for name in lst[0].keys() -%} - - {% endfor %} - - - {% for elem in lst[1:] %} - - {% for key in lst[0].keys() %} - - {% endfor %} - - {% endfor %} -
{{lst[0][name]}}
{{ elem[key] }}
-
- - - - - - \ No newline at end of file diff --git a/backend/app/templates/header.html b/backend/app/templates/header.html deleted file mode 100644 index 58c9ca4..0000000 --- a/backend/app/templates/header.html +++ /dev/null @@ -1,29 +0,0 @@ -{% macro input(session, certain_page, css_file) -%} - - - - - - DataBases - - - - - - - - - -
- {% if not certain_page -%} {% endif -%} -

На дому

-

Твой медицинский помощник

- - {% if not session["loggedin"] -%} - {% else -%} - -

{{session["full_name"]}}

- {% if session.admin == "True" -%}

Администратор

{% endif -%} - {% endif -%} -
-{%- endmacro %} \ No newline at end of file diff --git a/backend/app/templates/register.html b/backend/app/templates/register.html deleted file mode 100644 index e69de29..0000000 diff --git a/backend/requirements.txt b/backend/requirements.txt index beac9fa..fd296c5 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -13,3 +13,4 @@ requests==2.32.3 typing==3.7.4.3 urllib3==2.2.3 Werkzeug==3.1.2 +flask-cors==5.0.0 \ No newline at end of file