瀏覽代碼

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

Andrei 3 年之前
父節點
當前提交
22da6ee3ef

+ 1 - 1
data/projects.py

@@ -13,7 +13,7 @@ class Projects(SqlAlchemyBase, UserMixin):
     name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
     description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
     photo = sqlalchemy.Column(sqlalchemy.Text)
-    date_create = sqlalchemy.Column(sqlalchemy.Date,
+    date_create = sqlalchemy.Column(sqlalchemy.DateTime,
                                     default=date.today())
     creator = sqlalchemy.Column(sqlalchemy.Integer,
                                 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()])
     description = TextAreaField('Описание')
     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.staff_projects import StaffProjects
 from data import db_session
+import uuid
 
 
 def check_password(password=''):
@@ -56,6 +57,7 @@ def init_db_default():
 
 def get_user_data(user):
     resp = {
+        'id': user.id,
         'name': user.name,
         'surname': user.surname,
         'login': user.login,
@@ -68,12 +70,21 @@ def get_user_data(user):
 
 def get_projects_data(project):
     data_session = db_session.create_session()
+    staff = data_session.query(StaffProjects.user).filter(StaffProjects.project == project.id).all()
     resp = {
         'id': project.id,
         'name': project.name,
         'logo': project.photo,
         'description': project.description,
         '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
+
+
+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 os
-import pprint
 
 from flask import Flask, render_template, request, url_for
 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.utils import redirect
 from itsdangerous import URLSafeTimedSerializer, SignatureExpired
 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.login import LoginForm
 from forms.register import RegisterForm
@@ -25,6 +26,7 @@ from data import db_session
 app = Flask(__name__)
 key = 'test_secret_key'
 app.config['SECRET_KEY'] = key
+csrf = CSRFProtect(app)
 s = URLSafeTimedSerializer(key)
 login_manager = LoginManager()
 login_manager.init_app(app)
@@ -38,13 +40,62 @@ def base():
         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'])
 def new_project():
     if current_user.is_authenticated:
         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():
-            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:
         return redirect('/login')
 
@@ -218,7 +269,12 @@ def confirmation(token):
 
 @app.errorhandler(404)
 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():

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


+ 96 - 1
static/css/new_project.css

@@ -26,13 +26,108 @@
     align-items: center;
 }
 .input_button {
-    width: 10vw;
+    width: 35vw;
     height: 5vw;
     border-radius: 5vw;
     vertical-align: middle;
 }
 .form_label {
+    margin-top: 10px;
     font-size: 1.3vw;
     color: #ffffff;
     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 {
     display: none !important;
 }
-.page_404 {
+.page_error {
     height: 55vw;
     background-color: #dcb495;
     display: flex;

+ 4 - 1
static/css/profile.css

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

+ 10 - 8
static/css/projects.css

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

+ 15 - 5
templates/new_project.html

@@ -7,14 +7,14 @@
             <div class="name_form_block">
                 <div class="form_data">
                     <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 %}
                     <div class="alert alert-danger" role="alert">{{ error }}</div>
                     {% endfor %}
                 </div>
                 <div class="form_data">
                     <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 %}
                     <div class="alert alert-danger" role="alert">{{ error }}</div>
                     {% endfor %}
@@ -22,14 +22,24 @@
             </div>
             <div class="data_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 class="buttons_form_block">
                     <div class="form_data">
                         <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 %}
                         <div class="alert alert-danger" role="alert">{{ error }}</div>
                         {% 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 class="form_data">
                             <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 %}
                             <div class="alert alert-danger" role="alert">{{ error }}</div>
                             {% endfor %}

+ 6 - 6
templates/projects.html

@@ -19,12 +19,12 @@
     </div>
     <div class="list_project_block">
         {% 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">
-                <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"
-                            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_logo_block">
                                 <img src="{{ project.logo }}" class="project_logo">
@@ -35,8 +35,8 @@
                         </div>
                     </button>
                 </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="collaborator_block">
                             <div class="staff_block">