diff --git a/data/projects.py b/data/projects.py index 7a41ded..11d2412 100644 --- a/data/projects.py +++ b/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()) diff --git a/data/staff_projects.py b/data/staff_projects.py index 0193517..e04d0bf 100644 --- a/data/staff_projects.py +++ b/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) \ No newline at end of file + sqlalchemy.ForeignKey("roles.id"), nullable=True, default=None) diff --git a/forms/edit_profile.py b/forms/edit_profile.py index 8aff299..c11d8bb 100644 --- a/forms/edit_profile.py +++ b/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'], 'Только фотографии!')]) diff --git a/forms/new_project.py b/forms/new_project.py new file mode 100644 index 0000000..30373ec --- /dev/null +++ b/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('Регистрация') diff --git a/forms/register.py b/forms/register.py index 0e6613e..8cced83 100644 --- a/forms/register.py +++ b/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 diff --git a/functions.py b/functions.py index 7c4cdca..49e2d32 100644 --- a/functions.py +++ b/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 diff --git a/main.py b/main.py index 74638ab..1a80c4a 100644 --- a/main.py +++ b/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') diff --git a/static/css/new_project.css b/static/css/new_project.css new file mode 100644 index 0000000..a2da0eb --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/static/css/profile.css b/static/css/profile.css index 33c4d18..e08cae5 100644 --- a/static/css/profile.css +++ b/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; diff --git a/static/css/projects.css b/static/css/projects.css index 59aa669..539b117 100644 --- a/static/css/projects.css +++ b/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; } \ No newline at end of file diff --git a/static/images/none_project.png b/static/images/none_project.png new file mode 100644 index 0000000..4aabb8b Binary files /dev/null and b/static/images/none_project.png differ diff --git a/templates/new_project.html b/templates/new_project.html new file mode 100644 index 0000000..a9715fb --- /dev/null +++ b/templates/new_project.html @@ -0,0 +1,45 @@ + +{% extends "base.html" %} {% block content %} +
+
+
+ {{ form.hidden_tag() }} +
+
+ + {{ form.name(class="input_data", type="name", placeholder='your project name') }} + {% for error in form.name.errors %} + + {% endfor %} +
+
+ + {{ form.description(class="input_data", type="description", placeholder='your project description') }} + {% for error in form.description.errors %} + + {% endfor %} +
+
+
+
+
+ +
+
+
+
+ + {{ form.logo(class="input_data file_data", type="file") }} + {% for error in form.logo.errors %} + + {% endfor %} +
+
+ {{ form.submit(type="submit", class="project_button") }} +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/profile.html b/templates/profile.html index 643ff7f..4a55ff3 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -5,7 +5,8 @@

Профиль

Здесь можно поменять настройки учетной записи

-
+ {% for project in list_projects %}

-

-
-
- This is the first item's accordion body. 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 .accordion-body, though the transition - does limit overflow. +
+
+
+ {% for user in project.staff %} +
+ +

{{user.name}}

+
+ {% endfor %} +
+
+
+
+

Описание

+
+
+

{{ project.description }}

+
+
+
+ {% endfor %}
{% endblock %} \ No newline at end of file