Переглянути джерело

Поправил requirements.txt, сделал валидацию загрузки логотипа проекта, сделал логирование ошибок, авторизации и выхода, сделал полное удаление проекта и его составляющих

Andrei 2 роки тому
батько
коміт
ab82863761
8 змінених файлів з 78 додано та 36 видалено
  1. 2 1
      forms/project.py
  2. 40 2
      functions.py
  3. 0 0
      logfiles/заглушка
  4. 22 17
      main.py
  5. BIN
      requirements.txt
  6. 1 1
      static/css/answer.css
  7. 13 10
      templates/answer.html
  8. 0 5
      tets.py

+ 2 - 1
forms/project.py

@@ -1,4 +1,5 @@
 from flask_wtf import FlaskForm
+from flask_wtf.file import FileAllowed
 from wtforms import StringField, SubmitField, TextAreaField, FileField, MultipleFileField
 from wtforms.validators import DataRequired
 
@@ -6,7 +7,7 @@ from wtforms.validators import DataRequired
 class ProjectForm(FlaskForm):
     name = StringField('Название', validators=[DataRequired()])
     description = TextAreaField('Описание')
-    logo = FileField('Логотип')
+    logo = FileField('Логотип', validators=[FileAllowed(['jpg', 'png', 'bmp', 'ico', 'jpeg'], 'Только изображения')])
     submit = SubmitField('Создать')
     del_photo = SubmitField('Удалить фотографию')
     save = SubmitField('Сохранить')

+ 40 - 2
functions.py

@@ -1,8 +1,14 @@
 import datetime
 import os
+import shutil
 import smtplib
 from json import loads
 from email.message import EmailMessage
+from sqlalchemy import or_
+
+from data.answer import Answer
+from data.proof_file import FileProof
+from data.quests import Quests
 from data.roles import Roles
 from data.users import User
 from data.staff_projects import StaffProjects
@@ -27,8 +33,8 @@ def check_password(password=''):
 
 
 def mail(msg, to, topic='Подтверждение почты'):
-    with open('incepted.config', 'r', encoding='utf-8').read() as file:
-        file = loads(file)
+    with open('incepted.config', 'r', encoding='utf-8') as file:
+        file = loads(file.read())
     login, password = file["mail_login"], file["mail_password"]
     email_server = "smtp.yandex.ru"
     sender = "incepted@yandex.ru"
@@ -182,3 +188,35 @@ def file_tree(path):
             h += 1
     data_session.close()
     return tree
+
+
+def delete_file_proof_data(file_proof, data_session):
+    file = data_session.query(Files).filter(Files.id == file_proof.file).first()
+    data_session.delete(file)
+
+
+def delete_answer_data(answer, data_session):
+    file_proofs = data_session.query(FileProof).filter(FileProof.answer == answer.id).all()
+    list(map(lambda file: delete_file_proof_data(file, data_session), file_proofs))
+    list(map(data_session.delete, file_proofs))
+
+
+def delete_quest_data(quest, data_session):
+    answers = data_session.query(Answer).filter(Answer.quest == quest.id).all()
+    list(map(lambda answer: delete_answer_data(answer, data_session), answers))
+    list(map(data_session.delete, answers))
+
+
+def delete_project_data(project, data_session):
+    staff = data_session.query(StaffProjects).filter(StaffProjects.project == project.id).all()
+    list(map(data_session.delete, staff))
+    if 'none_project' not in project.photo:
+        os.remove(project.photo)
+    quests = data_session.query(Quests).filter(Quests.project == project.id).all()
+    list(map(lambda quest: delete_quest_data(quest, data_session), quests))
+    list(map(data_session.delete, quests))
+    list(map(data_session.delete,
+             data_session.query(Files).filter(Files.path.contains(f'all_projects/{str(project.id)}/')).all()))
+    shutil.rmtree(f'static/app_files/all_projects/{str(project.id)}')
+    data_session.delete(project)
+    data_session.commit()

+ 0 - 0
logfiles/заглушка


+ 22 - 17
main.py

@@ -1,5 +1,7 @@
 import datetime
 import os
+import logging
+import shutil
 
 from flask import Flask, render_template, request, url_for
 from flask_login import login_user, current_user, LoginManager, logout_user, login_required
@@ -12,7 +14,7 @@ from sqlalchemy import or_
 from json import loads
 
 from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo, \
-    overdue_quest_project, save_proof_quest, find_files_answer, file_tree
+    overdue_quest_project, save_proof_quest, find_files_answer, file_tree, delete_project_data
 from forms.edit_profile import EditProfileForm
 from forms.login import LoginForm
 from forms.find_project import FindProjectForm
@@ -38,6 +40,8 @@ with open('incepted.config', 'r', encoding='utf-8') as file:
     file = loads(file)
 key = file["encrypt_key"]
 app.config['SECRET_KEY'] = key
+logging.basicConfig(level=logging.INFO, filename="logfiles/main.log", format="%(asctime)s %(levelname)s %(message)s",
+                    encoding='utf-8')
 csrf = CSRFProtect(app)
 s = URLSafeTimedSerializer(key)
 login_manager = LoginManager()
@@ -100,7 +104,7 @@ def task_project(id_project, id_task):
                     deadline = None
                 current_task.deadline = deadline
                 if current_answer:
-                    current_answer.text = form.text.data if form.text.data else None
+                    current_answer.text = form.text.data
                     current_answer.date_edit = datetime.datetime.now()
                     current_task.realized = form.realized.data
                     data_session.commit()
@@ -125,7 +129,7 @@ def task_project(id_project, id_task):
                     current_task.realized = form.realized.data
                     current_answer = Answer(
                         quest=current_task.id,
-                        text=form.text.data if form.text.data else None,
+                        text=form.text.data,
                         creator=current_user.id,
                         date_create=datetime.datetime.now(),
                         date_edit=datetime.datetime.now()
@@ -136,19 +140,19 @@ def task_project(id_project, id_task):
                     if files:
                         for i in files:
                             proof_file = FileProof(
-                                proof=current_answer.id,
+                                answer=current_answer.id,
                                 file=i
                             )
                             data_session.add(proof_file)
                     data_session.commit()
                 return redirect(f'/project/{current_project.id}')
-            if current_answer:
+            if current_answer and request.method == 'GET':
                 form.text.data = current_answer.text
                 form.realized.data = current_task.realized
                 files = data_session.query(FileProof).filter(FileProof.answer == current_answer.id).all()
                 if files:
                     list_files = list(map(lambda x: find_files_answer(x.file), files))
-            if current_task.deadline and current_task.deadline:
+            if current_task.deadline and current_task.deadline and request.method == 'GET':
                 form.deadline_date.data = current_task.deadline.date()
                 form.deadline_time.data = current_task.deadline.time()
             return render_template('answer.html', title='Решение', project=current_project, task=current_task,
@@ -271,6 +275,7 @@ def project(id_project):
                     if form_file.file.data[0].filename:
                         files = list(
                             map(lambda x: save_proof_quest(current_project, x, current_user.id), form_file.file.data))
+                    return redirect(f'/project/{str(current_project.id)}')
                 return render_template('project.html',
                                        project=current_project,
                                        title=current_project.name,
@@ -338,18 +343,11 @@ def delete_project(id_project):
             if project_del.creator == current_user.id:
                 form = DeleteProjectForm()
                 if form.validate_on_submit():
-                    if form.conf.data != f'delete/{project_del.name}':
+                    if str(form.conf.data).lower().strip() != f'delete/{str(project_del.name)}'.lower().strip():
                         return render_template('delete_project.html', title='Удаление проекта', form=form,
                                                project=project_del,
                                                message='Вы не правильно ввели фразу')
-                    staff = data_session.query(StaffProjects).filter(StaffProjects.project == id_project).all()
-                    for i in staff:
-                        data_session.delete(i)
-                    if 'none_project' not in project_del.photo:
-                        os.remove(project_del.photo)
-                    shutil.rmtree(f'static/app_files/all_projects/{str(project_del.id)}')
-                    data_session.delete(project_del)
-                    data_session.commit()
+                    delete_project_data(project_del, data_session)
                     return redirect('/projects')
                 return render_template('delete_project.html', title='Удаление проекта', form=form, project=project_del,
                                        message='')
@@ -512,6 +510,7 @@ def login():
             if user and user.check_password(form.password.data):
                 if user.activated:
                     login_user(user, remember=form.remember_me.data)
+                    logging.info(f'{user.login} logged in')
                     return redirect('/projects')
                 else:
                     return render_template('login.html',
@@ -531,6 +530,7 @@ def login():
 @app.route('/logout')
 @login_required
 def logout():
+    logging.info(f'{current_user.login} logged out')
     logout_user()
     return redirect("/")
 
@@ -565,6 +565,7 @@ def register():
             link_conf = url_for('confirmation', token=token, _external=True)
             mail(f'Для завершения регистрации пройдите по ссылке: {link_conf}', form.email.data,
                  'Подтверждение регистрации')
+            logging.info(f'{form.login.data} was registered')
             return redirect('/login?message=Мы выслали ссылку для подтверждения почты')
         return render_template('register.html', form=form, message='', title='Регистрация')
     else:
@@ -580,6 +581,7 @@ def confirmation(token):
         if user:
             user.activated = True
             data_session.commit()
+            logging.info(f'{user.login} has been confirmed')
             return redirect('/login?message=Почта успешно подтверждена')
         else:
             return redirect('/login?message=Пользователь не найден&danger=True')
@@ -614,8 +616,11 @@ def main():
     db_session.global_init(db_path)
     if not db:
         init_db_default()
-    serve(app, host='0.0.0.0', port=5000)
+    serve(app, host='0.0.0.0', port=5000, threads=10)
 
 
 if __name__ == '__main__':
-    main()
+    try:
+        main()
+    except Exception as error:
+        logging.warning(f'{error}')

BIN
requirements.txt


+ 1 - 1
static/css/answer.css

@@ -170,7 +170,7 @@ form {
     max-height: 20vw;
 }
 .form_text_one {
-    width: 100%;
+    width: 200%;
 }
 .files_block {
     width: 100%;

+ 13 - 10
templates/answer.html

@@ -33,10 +33,13 @@
                         </div>
                         <div class="file_buttons">
                             <div class="btn-group file_buttons_groud">
-                                {% if current_user.id == project.creator or task.creator == current_user.id or file['user'] == current_user.id %}
-                                <a href="../file/{{ file.id }}/delete" class="btn btn-primary file_delete"><p class="button_text">Удалить</p></a>
+                                {% if current_user.id == project.creator or task.creator == current_user.id or
+                                file['user'] == current_user.id %}
+                                <a href="../file/{{ file.id }}/delete" class="btn btn-primary file_delete"><p
+                                        class="button_text">Удалить</p></a>
                                 {% endif %}
-                                <a href="../../../{{ file['path'] }}" download="" class="btn btn-primary file_download"><p class="button_text">Скачать</p></a>
+                                <a href="../../../{{ file['path'] }}" download="" class="btn btn-primary file_download">
+                                    <p class="button_text">Скачать</p></a>
                             </div>
                         </div>
                     </div>
@@ -45,17 +48,17 @@
             </div>
         </div>
         {% endif %}
-        <div class="form_data bottom_data form_text_one">
-            <label class="form_label">{{ form.text.label }}</label>
-            {{ form.text(class="input_data text_data", type="text", id="text_data", placeholder='your answer') }}
-            {% for error in form.text.errors %}
-            <div class="alert alert-danger" role="alert">{{ error }}</div>
-            {% endfor %}
-        </div>
     </div>
     <div class="decision_block">
         <form action="" method="post" class="answer_form" enctype="multipart/form-data">
             {{ form.hidden_tag() }}
+            <div class="form_data bottom_data form_text_one">
+                <label class="form_label">{{ form.text.label }}</label>
+                {{ form.text(class="input_data text_data", type="text", id="text_data", placeholder='your answer') }}
+                {% for error in form.text.errors %}
+                <div class="alert alert-danger" role="alert">{{ error }}</div>
+                {% endfor %}
+            </div>
             <div class="data_block">
                 <div class="form_data bottom_data">
                     <label class="form_label">{{ form.file.label }}</label>

+ 0 - 5
tets.py

@@ -1,5 +0,0 @@
-import os
-
-path = os.listdir('.')
-print(path)
-print(os.path.isdir(path[6]))