Forráskód Böngészése

Изменена страница списка проектов, создан шаблон страницы создания нового проекта, изменены таблицы в базе данных

Andrei 3 éve
szülő
commit
709d2f971d

+ 1 - 1
data/projects.py

@@ -11,7 +11,7 @@ class Projects(SqlAlchemyBase, UserMixin):
     id = sqlalchemy.Column(sqlalchemy.Integer,
                            primary_key=True, autoincrement=True)
     name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
-    about = sqlalchemy.Column(sqlalchemy.String, nullable=True)
+    description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
     photo = sqlalchemy.Column(sqlalchemy.Text)
     date_create = sqlalchemy.Column(sqlalchemy.Date,
                                     default=date.today())

+ 3 - 1
data/staff_projects.py

@@ -11,6 +11,8 @@ class StaffProjects(SqlAlchemyBase, UserMixin):
                            primary_key=True, autoincrement=True)
     user = sqlalchemy.Column(sqlalchemy.Integer,
                              sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)
+    project = sqlalchemy.Column(sqlalchemy.Integer,
+                                sqlalchemy.ForeignKey("projects.id"), nullable=True, default=None)
     role = sqlalchemy.Column(sqlalchemy.Text)
     permission = sqlalchemy.Column(sqlalchemy.Integer,
-                             sqlalchemy.ForeignKey("roles.id"), nullable=True, default=None)
+                                   sqlalchemy.ForeignKey("roles.id"), nullable=True, default=None)

+ 1 - 1
forms/edit_profile.py

@@ -7,7 +7,7 @@ from wtforms.validators import DataRequired
 class EditProfileForm(FlaskForm):
     email = EmailField('Почта', validators=[DataRequired()])
     name = StringField('Имя', validators=[DataRequired()])
-    surname = StringField('Фамилия', )
+    surname = StringField('Фамилия')
     about = TextAreaField('Расскажите о себе', default='')
     birthday = DateField('Дата рождения')
     photo = FileField('Фото', validators=[FileAllowed(['jpg', 'png', 'bmp'], 'Только фотографии!')])

+ 10 - 0
forms/new_project.py

@@ -0,0 +1,10 @@
+from flask_wtf import FlaskForm
+from wtforms import StringField, SubmitField, TextAreaField, FileField
+from wtforms.validators import DataRequired
+
+
+class NewProjectForm(FlaskForm):
+    name = StringField('Название', validators=[DataRequired()])
+    description = TextAreaField('Описание')
+    logo = FileField('Логотип')
+    submit = SubmitField('Регистрация')

+ 0 - 1
forms/register.py

@@ -1,5 +1,4 @@
 from flask_wtf import FlaskForm
-from flask_wtf.file import FileAllowed
 from wtforms import EmailField, StringField, PasswordField, SubmitField
 from wtforms.validators import DataRequired
 

+ 27 - 0
functions.py

@@ -1,6 +1,8 @@
 import smtplib
 from email.message import EmailMessage
 from data.roles import Roles
+from data.users import User
+from data.staff_projects import StaffProjects
 from data import db_session
 
 
@@ -50,3 +52,28 @@ def init_db_default():
         data_session.add(role)
     data_session.commit()
     data_session.close()
+
+
+def get_user_data(user):
+    resp = {
+        'name': user.name,
+        'surname': user.surname,
+        'login': user.login,
+        'email': user.email,
+        'photo': user.photo,
+        'role': user.role
+    }
+    return resp
+
+
+def get_projects_data(project):
+    data_session = db_session.create_session()
+    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()))
+    }
+    return resp

+ 27 - 3
main.py

@@ -1,18 +1,24 @@
 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 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
+from functions import check_password, mail, init_db_default, get_projects_data
 from forms.edit_profile import EditProfileForm
 from forms.login import LoginForm
 from forms.register import RegisterForm
+from forms.new_project import NewProjectForm
+
 from data.users import User
 from data.files import Files
+from data.projects import Projects
+from data.staff_projects import StaffProjects
 from waitress import serve
 from data import db_session
 
@@ -32,12 +38,30 @@ def base():
         return redirect('/projects')
 
 
+@app.route('/projects/new', methods=['GET', 'POST'])
+def new_project():
+    if current_user.is_authenticated:
+        form = NewProjectForm()
+        if form.validate_on_submit():
+            pass
+        return render_template('new_project.html', title='Новый проект', form=form)
+    else:
+        return redirect('/login')
+
+
 @app.route('/projects', methods=['GET', 'POST'])
 def project():
     if current_user.is_authenticated:
+        data_session = db_session.create_session()
+        resp = []
         if request.method == 'POST':
-            print(request.form.to_dict())
-        return render_template('projects.html', title='Проекты')
+            pass
+        else:
+            projects = data_session.query(Projects).filter(or_(Projects.creator == current_user.id, current_user.id in
+                                                               data_session.query(StaffProjects.project).filter(
+                                                                   StaffProjects.user == current_user.id).all())).all()
+            resp = list(map(lambda x: get_projects_data(x), projects))
+        return render_template('projects.html', title='Проекты', list_projects=resp)
     else:
         return redirect('/login')
 

+ 38 - 0
static/css/new_project.css

@@ -0,0 +1,38 @@
+.new_project_page {
+    height: 120vw;
+    background-color: #dcb495;
+}
+.form_data, .form_data_button {
+    display: flex;
+    flex-direction: column;
+    margin-left: 2%;
+}
+.form_data_button {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+}
+.input_data {
+    color: #000000;
+    border: 0.1vw solid #595008;
+    height: 4.5vw;
+    min-height: 4.5vw;
+    width: 35vw;
+    background-color: #dbc3af;
+    border-radius: 4.5vw;
+    font-size: 1.3vw;
+    display: inline-flex;
+    align-items: center;
+}
+.input_button {
+    width: 10vw;
+    height: 5vw;
+    border-radius: 5vw;
+    vertical-align: middle;
+}
+.form_label {
+    font-size: 1.3vw;
+    color: #ffffff;
+    font-weight: bold;
+}

+ 0 - 1
static/css/profile.css

@@ -51,7 +51,6 @@ form {
     display: flex;
     flex-direction: column;
     margin-left: 2%;
-    margin-left: 2%;
 }
 .form_data_button {
     display: flex;

+ 197 - 0
static/css/projects.css

@@ -64,4 +64,201 @@
     margin-left: 2.5%;
     height: 95%;
     margin-top: 2.5%;
+    overflow-y: auto;
+    overflow-x: hidden;
+}
+.project_header_button {
+    height: 5.5vw;
+    width: 100%;
+    text-align: left;
+    border-radius: 5vw;
+    background-color: #9E795A;
+    border-color: #9E795A;
+    border-bottom-color: #9E795A;
+    color: #ffffff;
+    display: flex;
+    align-items: center;
+}
+.project_description_block {
+    background-color: #9E795A;
+    width: 100%;
+    height: 20vw;
+    border-radius: 2vw;
+}
+.project_logo_block {
+    width: 4.5vw;
+    height: 4.5vw;
+    border:2px solid #ffffff;
+    background-color: #ffffff;
+    border-radius: 2vw;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+.project_logo {
+    width: 4vw;
+    height: 4vw;
+    border-radius: 5vw;
+}
+.project_title_block {
+    width: 70%;
+    height: 4vw;
+}
+.project_title {
+    font-size: 3.5vw;
+}
+.project_button_block_one {
+    width: 50%;
+    display: flex;
+    justify-content: space-evenly;
+    align-items: flex-start;
+}
+.project_description {
+    width: 98%;
+    height: 100%;
+    margin-left: 1%;
+    display: flex;
+    flex-direction: row;
+    flex-wrap: nowrap;
+    align-content: center;
+    align-items: center;
+    justify-content: space-evenly;
+}
+.collaborator_block {
+    width: 22%;
+    height: 90%;
+    background-color: #EDCBB0;
+    border-radius: 2vw;
+    overflow-y: auto;
+}
+.description_block {
+    width: 48%;
+    height: 90%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    flex-wrap: nowrap;
+}
+.description_header_text {
+    font-size: 2vw;
+}
+.description_block_text {
+    width: 90% !important;
+    height: 80% !important;
+    width: 50%;
+    background-color: #dcb495;
+    border-radius: 2vw;
+}
+.description_text {
+    width: 100% !important;
+    height: 100%;
+    font-size: 1.5vw;
+    overflow-wrap: normal;  /* не поддерживает IE, Firefox; является копией word-wrap */ 
+    word-wrap: normal;
+    word-break: normal;  /* не поддерживает Opera12.14, значение keep-all не поддерживается IE, Chrome */ 
+    line-break: auto;  /* нет поддержки для русского языка */ 
+    hyphens: manual;  /* значение auto не поддерживается Chrome */ 
+    margin: 2vw;
+}
+.open_project_block {
+    width: 20%;
+    height: 90%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    flex-wrap: nowrap;
+}
+.open_button {
+    background-color: #ffffff;
+    color: #000000;
+    width: 15vw;
+    height: 4.5vw;
+    vertical-align: middle;
+    border-radius: 5vw;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.open_button:hover {
+    text-decoration: none;
+    color: #000000;
+}
+.open_button_text {
+    font-size: 1.5vw;
+    margin-top: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.open_button, .open_button_link {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 15vw;
+    height: 4.5vw;
+    color: #000000;
+}
+.open_button_link:hover {
+    text-decoration: none;
+    color: #000000;
+}
+.staff_block {
+    margin: 20px;
+}
+.user {
+    width: 16vw;
+    height: 3.5vw;
+    background-color: #ffffff;
+    border: 2px solid #9E795A;
+    border-radius: 3vw;
+    margin-top: 5px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    flex-direction: row;
+    flex-wrap: no-wrap;
+}
+.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;
+}
+.new_project_button {
+    width: 13vw;
+    height: 5vw;
+    background-color: #000000;
+    border: 2px solid #ffffff;
+    border-radius: 3vw;
+    margin-left: 2vw;
+}
+.new_project_button_text {
+    width: 13vw;
+    height: 5vw;
+    text-align: center;
+    font-size: 1.5vw;
+    color: #ffffff;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.new_project_button_link {
+    width: 13vw;
+    height: 5vw;
+}
+.new_project_button_link:hover {
+    text-decoration: none;
+    color: #000000;
+}
+.form_project_block {
+    display: flex;
+    align-items: center;
+    justify-content: center;
 }

BIN
static/images/none_project.png


+ 45 - 0
templates/new_project.html

@@ -0,0 +1,45 @@
+<link rel="stylesheet" href="../static/css/new_project.css"/>
+{% extends "base.html" %} {% block content %}
+<div class="new_project_page">
+    <div class="form_block">
+        <form action="" method="post" class="register_form" enctype="multipart/form-data">
+            {{ form.hidden_tag() }}
+            <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') }}
+                    {% 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') }}
+                    {% for error in form.description.errors %}
+                    <div class="alert alert-danger" role="alert">{{ error }}</div>
+                    {% endfor %}
+                </div>
+            </div>
+            <div class="data_form_block">
+                <div class="staff_form_block">
+                    <div class="staff_list">
+
+                    </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") }}
+                        {% for error in form.logo.errors %}
+                        <div class="alert alert-danger" role="alert">{{ error }}</div>
+                        {% endfor %}
+                    </div>
+                    <div class="form_data_button">
+                        {{ form.submit(type="submit", class="project_button") }}
+                    </div>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+{% endblock %}

+ 2 - 1
templates/profile.html

@@ -5,7 +5,8 @@
         <div class="open_button_content">
             <h2 class="open_button_title">Профиль</h2>
             <p class="open_button_article">Здесь можно поменять настройки учетной записи</p>
-            <a class="open_button" data-bs-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
+            <a class="open_button" data-bs-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false"
+               aria-controls="collapseExample">
                 <div class="open_button_text">
                     Редикторовать
                 </div>

+ 44 - 11
templates/projects.html

@@ -7,34 +7,67 @@
             добавлять участников в своей проект.</strong>
     </div>
     <div class="find_block">
-        <form action="" method="post">
+        <form action="" method="post" class="form_project_block">
             <input class="find_input_text" type="text" placeholder="Имя проекта" name="find_text">
             <button class="find_input_button">Поиск</button>
+            <div class="new_project_button">
+                <a class="new_project_button_link" href="/projects/new">
+                    <p class="new_project_button_text">Создать</p>
+                </a>
+            </div>
         </form>
     </div>
     <div class="list_project_block">
+        {% for project in list_projects %}
         <div class="accordion list_project" id="accordionPanelsStayOpenExample">
             <div class="accordion-item project">
                 <h2 class="accordion-header project_header" id="panelsStayOpen-headingOne">
-                    <button class="accordion-button priject_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">
-                        Project 1
+                        <div class="project_button_block_one">
+                            <div class="project_logo_block">
+                                <img src="{{ project.logo }}" class="project_logo">
+                            </div>
+                            <div class="project_title_block">
+                                <p class="project_title">{{ project.name }}</p>
+                            </div>
+                        </div>
                     </button>
                 </h2>
-                <div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse"
+                <div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse project_description_block"
                      aria-labelledby="panelsStayOpen-headingOne">
-                    <div class="accordion-body">
-                        <strong>This is the first item's accordion body.</strong> It is shown by default, until the
-                        collapse plugin adds the appropriate classes that we use to style each element. These classes
-                        control the overall appearance, as well as the showing and hiding via CSS transitions. You can
-                        modify any of this with custom CSS or overriding our default variables. It's also worth noting
-                        that just about any HTML can go within the <code>.accordion-body</code>, though the transition
-                        does limit overflow.
+                    <div class="accordion-body project_description">
+                        <div class="collaborator_block">
+                            <div class="staff_block">
+                                {% for user in project.staff %}
+                                <div class="user">
+                                    <img class="user_logo" src="{{user.photo}}">
+                                    <p class="user_names">{{user.name}}</p>
+                                </div>
+                                {% endfor %}
+                            </div>
+                        </div>
+                        <div class="description_block">
+                            <div class="description_header_block">
+                                <p class="description_header_text">Описание</p>
+                            </div>
+                            <div class="description_block_text">
+                                <p class="description_text">{{ project.description }}</p>
+                            </div>
+                        </div>
+                        <div class="open_project_block">
+                            <div class="open_button">
+                                <a class="open_button_link" href="/projects/{{ project.id }}">
+                                    <p class="open_button_text">Открыть</p>
+                                </a>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
         </div>
+        {% endfor %}
     </div>
 </div>
 {% endblock %}