Kaynağa Gözat

Создана страница добавления проектов, проекты отображаются в списке проектов, добавлена функция удаления проекта

Andrei 3 yıl önce
ebeveyn
işleme
22da6ee3ef

+ 1 - 1
data/projects.py

@@ -13,7 +13,7 @@ class Projects(SqlAlchemyBase, UserMixin):
     name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
     name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
     description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
     description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
     photo = sqlalchemy.Column(sqlalchemy.Text)
     photo = sqlalchemy.Column(sqlalchemy.Text)
-    date_create = sqlalchemy.Column(sqlalchemy.Date,
+    date_create = sqlalchemy.Column(sqlalchemy.DateTime,
                                     default=date.today())
                                     default=date.today())
     creator = sqlalchemy.Column(sqlalchemy.Integer,
     creator = sqlalchemy.Column(sqlalchemy.Integer,
                                 sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)
                                 sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)

+ 1 - 1
forms/new_project.py

@@ -7,4 +7,4 @@ class NewProjectForm(FlaskForm):
     name = StringField('Название', validators=[DataRequired()])
     name = StringField('Название', validators=[DataRequired()])
     description = TextAreaField('Описание')
     description = TextAreaField('Описание')
     logo = FileField('Логотип')
     logo = FileField('Логотип')
-    submit = SubmitField('Регистрация')
+    submit = SubmitField('Создать')

+ 12 - 1
functions.py

@@ -4,6 +4,7 @@ from data.roles import Roles
 from data.users import User
 from data.users import User
 from data.staff_projects import StaffProjects
 from data.staff_projects import StaffProjects
 from data import db_session
 from data import db_session
+import uuid
 
 
 
 
 def check_password(password=''):
 def check_password(password=''):
@@ -56,6 +57,7 @@ def init_db_default():
 
 
 def get_user_data(user):
 def get_user_data(user):
     resp = {
     resp = {
+        'id': user.id,
         'name': user.name,
         'name': user.name,
         'surname': user.surname,
         'surname': user.surname,
         'login': user.login,
         'login': user.login,
@@ -68,12 +70,21 @@ def get_user_data(user):
 
 
 def get_projects_data(project):
 def get_projects_data(project):
     data_session = db_session.create_session()
     data_session = db_session.create_session()
+    staff = data_session.query(StaffProjects.user).filter(StaffProjects.project == project.id).all()
     resp = {
     resp = {
         'id': project.id,
         'id': project.id,
         'name': project.name,
         'name': project.name,
         'logo': project.photo,
         'logo': project.photo,
         'description': project.description,
         'description': project.description,
         'staff': list(map(lambda x: get_user_data(x), data_session.query(User).filter(
         'staff': list(map(lambda x: get_user_data(x), data_session.query(User).filter(
-            User.id.in_(*data_session.query(StaffProjects.user).filter(StaffProjects.id == project.id).all())).all()))
+            User.id.in_(list(map(lambda x: x[0], staff)))).all())) if staff else []
     }
     }
+    resp['staff'].insert(0, get_user_data(data_session.query(User).filter(User.id == project.creator).first()))
     return resp
     return resp
+
+
+def save_project_logo(photo):
+    filename = f'static/app_files/project_logo/{uuid.uuid4()}.png'
+    with open(filename, 'wb') as f:
+        photo.save(f)
+    return filename

+ 61 - 5
main.py

@@ -1,15 +1,16 @@
 import datetime
 import datetime
 import os
 import os
-import pprint
 
 
 from flask import Flask, render_template, request, url_for
 from flask import Flask, render_template, request, url_for
 from flask_login import login_user, current_user, LoginManager, logout_user, login_required
 from flask_login import login_user, current_user, LoginManager, logout_user, login_required
+from flask_wtf import CSRFProtect
+from flask_restful import abort
 from werkzeug.datastructures import CombinedMultiDict
 from werkzeug.datastructures import CombinedMultiDict
 from werkzeug.utils import redirect
 from werkzeug.utils import redirect
 from itsdangerous import URLSafeTimedSerializer, SignatureExpired
 from itsdangerous import URLSafeTimedSerializer, SignatureExpired
 from sqlalchemy import or_
 from sqlalchemy import or_
 
 
-from functions import check_password, mail, init_db_default, get_projects_data
+from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo
 from forms.edit_profile import EditProfileForm
 from forms.edit_profile import EditProfileForm
 from forms.login import LoginForm
 from forms.login import LoginForm
 from forms.register import RegisterForm
 from forms.register import RegisterForm
@@ -25,6 +26,7 @@ from data import db_session
 app = Flask(__name__)
 app = Flask(__name__)
 key = 'test_secret_key'
 key = 'test_secret_key'
 app.config['SECRET_KEY'] = key
 app.config['SECRET_KEY'] = key
+csrf = CSRFProtect(app)
 s = URLSafeTimedSerializer(key)
 s = URLSafeTimedSerializer(key)
 login_manager = LoginManager()
 login_manager = LoginManager()
 login_manager.init_app(app)
 login_manager.init_app(app)
@@ -38,13 +40,62 @@ def base():
         return redirect('/projects')
         return redirect('/projects')
 
 
 
 
+@app.route('/projects/delete/<int:id_project>', methods=['GET', 'POST'])
+def delete_project(id_project):
+    if current_user.is_authenticated:
+        data_session = db_session.create_session()
+        project_del = data_session.query(Projects).filter(Projects.id == id_project).first()
+        if project_del:
+            if project_del.creator == current_user.id:
+                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)
+                data_session.delete(project_del)
+                data_session.commit()
+                data_session.close()
+                return redirect('/projects')
+            else:
+                abort(403)
+        else:
+            abort(404)
+    else:
+        return redirect('/login')
+
+
 @app.route('/projects/new', methods=['GET', 'POST'])
 @app.route('/projects/new', methods=['GET', 'POST'])
 def new_project():
 def new_project():
     if current_user.is_authenticated:
     if current_user.is_authenticated:
         form = NewProjectForm()
         form = NewProjectForm()
+        data_session = db_session.create_session()
+        list_users = list(
+            map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all()))
         if form.validate_on_submit():
         if form.validate_on_submit():
-            pass
-        return render_template('new_project.html', title='Новый проект', form=form)
+            project = Projects(
+                name=form.name.data,
+                description=form.description.data,
+                date_create=datetime.datetime.now(),
+                creator=current_user.id
+            )
+            project.photo = save_project_logo(form.logo.data) if form.logo.data else 'static/images/none_project.png'
+            data_session.add(project)
+            data_session.flush()
+            data_session.refresh(project)
+            for i in list_users:
+                if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id:
+                    new_staffer = StaffProjects(
+                        user=i['id'],
+                        project=project.id,
+                        role='user',
+                        permission=3
+                    )
+                    data_session.add(new_staffer)
+            data_session.commit()
+            data_session.close()
+            return redirect('/projects')
+        data_session.close()
+        return render_template('new_project.html', title='Новый проект', form=form, list_users=list_users)
     else:
     else:
         return redirect('/login')
         return redirect('/login')
 
 
@@ -218,7 +269,12 @@ def confirmation(token):
 
 
 @app.errorhandler(404)
 @app.errorhandler(404)
 def page_not_found(error):
 def page_not_found(error):
-    return render_template('page404.html', title='Страница не найдена')
+    return render_template('page_error.html', title='Страница не найдена', error='404', message='Страница не найдена')
+
+
+@app.errorhandler(403)
+def page_not_found(error):
+    return render_template('page_error.html', title='Ошибка доступа', error='403', message='Доступ сюда запрещен')
 
 
 
 
 def main():
 def main():

+ 0 - 0
static/app_files/project_logo/заглушка


+ 96 - 1
static/css/new_project.css

@@ -26,13 +26,108 @@
     align-items: center;
     align-items: center;
 }
 }
 .input_button {
 .input_button {
-    width: 10vw;
+    width: 35vw;
     height: 5vw;
     height: 5vw;
     border-radius: 5vw;
     border-radius: 5vw;
     vertical-align: middle;
     vertical-align: middle;
 }
 }
 .form_label {
 .form_label {
+    margin-top: 10px;
     font-size: 1.3vw;
     font-size: 1.3vw;
     color: #ffffff;
     color: #ffffff;
     font-weight: bold;
     font-weight: bold;
+}
+.description {
+    border-radius: 2vw !important;
+    width: 50vw;
+}
+.padding_data {
+    padding-top: 1vw;
+    padding-left: 1vw;
+}
+.label_data {
+    padding-left: 0.8vw;
+    width: 50vw;
+}
+.project_button {
+    margin-top: 15px;
+    width: 35vw;
+    height: 5vw;
+    background-color: #000000;
+    color: #ffffff;
+    border-radius: 5vw;
+    vertical-align: middle;
+    font-size: 1.5vw;
+}
+.collaborator_block {
+    width: 30%;
+    height: 20vw;
+    background-color: #EDCBB0;
+    border-radius: 2vw;
+    overflow-y: auto;
+}
+.user {
+    width: 30vw;
+    height: 3.5vw;
+    background-color: #ffffff;
+    border: 2px solid #9E795A;
+    border-radius: 3vw;
+    margin-top: 5px;
+    display: inline-flex;
+    justify-content: space-between;
+}
+.user_logo {
+    margin-left: 3px;
+    width: 3vw;
+    height: 3vw;
+    border-radius: 5vw;
+    background-color: #000000;
+}
+.user_names {
+    margin-left: 9px;
+    margin-top: 10px;
+    overflow-x: auto;
+}
+.name_form_block {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+.data_form_block {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: row;
+}
+.buttons_form_block {
+    width: 45%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+.staff_form_block, .collaborator_block {
+    margin-top: 10px;
+    width: 60%;
+    height: 20vw;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.staff_block {
+    margin: 5%;
+    width: 90%;
+    height: 20vw;
+}
+.choose_user {
+    align-self: flex-end;
+    margin-bottom: 1.1vw;
+    width: 10%;
+}
+.user_data {
+    display: inline-flex;
+    align-items: center;
+    justify-content: flex-start;
+    flex-direction: row;
 }
 }

+ 1 - 1
static/css/page404.css → static/css/page_error.css

@@ -1,7 +1,7 @@
 .navbar {
 .navbar {
     display: none !important;
     display: none !important;
 }
 }
-.page_404 {
+.page_error {
     height: 55vw;
     height: 55vw;
     background-color: #dcb495;
     background-color: #dcb495;
     display: flex;
     display: flex;

+ 4 - 1
static/css/profile.css

@@ -150,7 +150,7 @@ form {
     width: 20vw;
     width: 20vw;
     height: 5vw;
     height: 5vw;
     vertical-align: middle;
     vertical-align: middle;
-    border-radius: 30px;
+    border-radius: 5vw;
 }
 }
 .open_button:hover {
 .open_button:hover {
     text-decoration: none;
     text-decoration: none;
@@ -162,4 +162,7 @@ form {
     text-align: center;
     text-align: center;
     font-size: 1.5vw;
     font-size: 1.5vw;
     margin-top: 5%;
     margin-top: 5%;
+}
+.about {
+    border-radius: 2vw !important;
 }
 }

+ 10 - 8
static/css/projects.css

@@ -39,8 +39,9 @@
     width: 45vw;
     width: 45vw;
     height: 5vw;
     height: 5vw;
     color: #776658;
     color: #776658;
-    border-radius: 30px;
+    border-radius: 5vw;
     vertical-align: middle;
     vertical-align: middle;
+    font-size: 1.5vw;
 }
 }
 .find_input_button {
 .find_input_button {
     margin-left: 12px;
     margin-left: 12px;
@@ -49,22 +50,23 @@
     width: 10vw;
     width: 10vw;
     height: 5vw;
     height: 5vw;
     color: #ffffff;
     color: #ffffff;
-    border-radius: 30px;
+    border-radius: 5vw;
     vertical-align: middle;
     vertical-align: middle;
+    font-size: 1.5vw;
 }
 }
 .list_project_block {
 .list_project_block {
     margin-left: 3%;
     margin-left: 3%;
-    border: 2px solid #694a2d;
-    border-radius: 25px;
+    border: 0.2vw solid #694a2d;
+    border-radius: 4.5vw;
     width: 94%;
     width: 94%;
     height: 45vw;
     height: 45vw;
+    overflow-y: auto;
 }
 }
 .list_project {
 .list_project {
     width: 95%;
     width: 95%;
     margin-left: 2.5%;
     margin-left: 2.5%;
-    height: 95%;
-    margin-top: 2.5%;
-    overflow-y: auto;
+    margin-top: 2vw;
+    overflow-y: hidden;
     overflow-x: hidden;
     overflow-x: hidden;
 }
 }
 .project_header_button {
 .project_header_button {
@@ -88,7 +90,7 @@
 .project_logo_block {
 .project_logo_block {
     width: 4.5vw;
     width: 4.5vw;
     height: 4.5vw;
     height: 4.5vw;
-    border:2px solid #ffffff;
+    border: 0.3vw solid #ffffff;
     background-color: #ffffff;
     background-color: #ffffff;
     border-radius: 2vw;
     border-radius: 2vw;
     display: flex;
     display: flex;

+ 15 - 5
templates/new_project.html

@@ -7,14 +7,14 @@
             <div class="name_form_block">
             <div class="name_form_block">
                 <div class="form_data">
                 <div class="form_data">
                     <label class="form_label">{{ form.name.label }}</label>
                     <label class="form_label">{{ form.name.label }}</label>
-                    {{ form.name(class="input_data", type="name", placeholder='your project name') }}
+                    {{ form.name(class="input_data label_data", type="name", placeholder='your project name') }}
                     {% for error in form.name.errors %}
                     {% for error in form.name.errors %}
                     <div class="alert alert-danger" role="alert">{{ error }}</div>
                     <div class="alert alert-danger" role="alert">{{ error }}</div>
                     {% endfor %}
                     {% endfor %}
                 </div>
                 </div>
                 <div class="form_data">
                 <div class="form_data">
                     <label class="form_label">{{ form.description.label }}</label>
                     <label class="form_label">{{ form.description.label }}</label>
-                    {{ form.description(class="input_data", type="description", placeholder='your project description') }}
+                    {{ form.description(class="input_data description padding_data", type="description", placeholder='your project description') }}
                     {% for error in form.description.errors %}
                     {% for error in form.description.errors %}
                     <div class="alert alert-danger" role="alert">{{ error }}</div>
                     <div class="alert alert-danger" role="alert">{{ error }}</div>
                     {% endfor %}
                     {% endfor %}
@@ -22,14 +22,24 @@
             </div>
             </div>
             <div class="data_form_block">
             <div class="data_form_block">
                 <div class="staff_form_block">
                 <div class="staff_form_block">
-                    <div class="staff_list">
-
+                    <div class="collaborator_block">
+                        <div class="staff_block">
+                            {% for user in list_users %}
+                            <div class="user">
+                                <div class="user_data">
+                                    <img class="user_logo" src="../{{user.photo}}">
+                                    <p class="user_names">{{user.name}}</p>
+                                </div>
+                                <input class="choose_user" name="choose_{{user.login}}" type="checkbox" value="y">
+                            </div>
+                            {% endfor %}
+                        </div>
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="buttons_form_block">
                 <div class="buttons_form_block">
                     <div class="form_data">
                     <div class="form_data">
                         <label class="form_label">{{ form.logo.label }}</label>
                         <label class="form_label">{{ form.logo.label }}</label>
-                        {{ form.logo(class="input_data file_data", type="file") }}
+                        {{ form.logo(class="input_data padding_data", type="file") }}
                         {% for error in form.logo.errors %}
                         {% for error in form.logo.errors %}
                         <div class="alert alert-danger" role="alert">{{ error }}</div>
                         <div class="alert alert-danger" role="alert">{{ error }}</div>
                         {% endfor %}
                         {% endfor %}

+ 0 - 27
templates/page404.html

@@ -1,27 +0,0 @@
-<link rel="stylesheet" href="../static/css/page404.css"/>
-{% extends "base.html" %} {% block content %}
-<div class="page_404">
-    <div class="header_block">
-        <div class="header">
-            <hr class="line_top">
-            <div class="header_rect">
-                <strong class="header_rect_text">Ошибка 404</strong>
-            </div>
-            <hr class="line_top">
-        </div>
-        <div class="header">
-            <h2 class="header_title">Страница не найдена</h2>
-        </div>
-        <div class="header">
-            <hr class="line_bottom">
-        </div>
-    </div>
-    <div class="link_block">
-            <div class="block_to_home">
-                <a class="link_to_home" href="/#header_block">
-                    <img class="link_image" src="../static/images/logo_w.png">
-                </a>
-            </div>
-    </div>
-</div>
-{% endblock %}

+ 27 - 0
templates/page_error.html

@@ -0,0 +1,27 @@
+<link rel="stylesheet" href="../../../../static/css/page_error.css"/>
+{% extends "base.html" %} {% block content %}
+<div class="page_error">
+    <div class="header_block">
+        <div class="header">
+            <hr class="line_top">
+            <div class="header_rect">
+                <strong class="header_rect_text">{{ error }}</strong>
+            </div>
+            <hr class="line_top">
+        </div>
+        <div class="header">
+            <h2 class="header_title">{{ message }}</h2>
+        </div>
+        <div class="header">
+            <hr class="line_bottom">
+        </div>
+    </div>
+    <div class="link_block">
+        <div class="block_to_home">
+            <a class="link_to_home" href="/#header_block">
+                <img class="link_image" src="../../../../static/images/logo_w.png">
+            </a>
+        </div>
+    </div>
+</div>
+{% endblock %}

+ 1 - 1
templates/profile.html

@@ -64,7 +64,7 @@
                         </div>
                         </div>
                         <div class="form_data">
                         <div class="form_data">
                             <label class="form-label">{{ form.about.label }}</label>
                             <label class="form-label">{{ form.about.label }}</label>
-                            {{ form.about(class="input_data dop_data", type="name",
+                            {{ form.about(class="input_data dop_data about", type="name",
                             placeholder='about') }} {% for error in form.about.errors %}
                             placeholder='about') }} {% for error in form.about.errors %}
                             <div class="alert alert-danger" role="alert">{{ error }}</div>
                             <div class="alert alert-danger" role="alert">{{ error }}</div>
                             {% endfor %}
                             {% endfor %}

+ 6 - 6
templates/projects.html

@@ -19,12 +19,12 @@
     </div>
     </div>
     <div class="list_project_block">
     <div class="list_project_block">
         {% for project in list_projects %}
         {% for project in list_projects %}
-        <div class="accordion list_project" id="accordionPanelsStayOpenExample">
+        <div class="accordion list_project" id="accordionPanelsStayOpen{{ project.id }}">
             <div class="accordion-item project">
             <div class="accordion-item project">
-                <h2 class="accordion-header project_header" id="panelsStayOpen-headingOne">
+                <h2 class="accordion-header project_header" id="panelsStayOpen-heading{{ project.id }}">
                     <button class="accordion-button project_header_button" type="button" data-bs-toggle="collapse"
                     <button class="accordion-button project_header_button" type="button" data-bs-toggle="collapse"
-                            data-bs-target="#panelsStayOpen-collapseOne" aria-expanded="true"
-                            aria-controls="panelsStayOpen-collapseOne">
+                            data-bs-target="#panelsStayOpen-collapse{{ project.id }}" aria-expanded="true"
+                            aria-controls="panelsStayOpen-collapse{{ project.id }}">
                         <div class="project_button_block_one">
                         <div class="project_button_block_one">
                             <div class="project_logo_block">
                             <div class="project_logo_block">
                                 <img src="{{ project.logo }}" class="project_logo">
                                 <img src="{{ project.logo }}" class="project_logo">
@@ -35,8 +35,8 @@
                         </div>
                         </div>
                     </button>
                     </button>
                 </h2>
                 </h2>
-                <div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse project_description_block"
-                     aria-labelledby="panelsStayOpen-headingOne">
+                <div id="panelsStayOpen-collapse{{ project.id }}" class="accordion-collapse collapse project_description_block"
+                     aria-labelledby="panelsStayOpen-heading{{ project.id }}">
                     <div class="accordion-body project_description">
                     <div class="accordion-body project_description">
                         <div class="collaborator_block">
                         <div class="collaborator_block">
                             <div class="staff_block">
                             <div class="staff_block">