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

This commit is contained in:
Andrei 2023-01-26 22:41:23 +05:00
parent 709d2f971d
commit 22da6ee3ef
14 changed files with 235 additions and 58 deletions

View File

@ -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)

View File

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

View File

@ -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

66
main.py
View File

@ -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():

View File

@ -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;
}

View File

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

View File

@ -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;
}

View File

@ -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;

View File

@ -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 %}

View File

@ -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
templates/page_error.html Normal file
View File

@ -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 %}

View File

@ -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 %}

View File

@ -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">