Реализован поиск проектов по имени, восстановление пароля по почте. Начал работать на "задачами" в проектах
This commit is contained in:
parent
25e24462ff
commit
b0837ce46f
17
data/quests.py
Normal file
17
data/quests.py
Normal file
@ -0,0 +1,17 @@
|
||||
import sqlalchemy
|
||||
from flask_login import UserMixin
|
||||
|
||||
from .db_session import SqlAlchemyBase
|
||||
|
||||
|
||||
class Quests(SqlAlchemyBase, UserMixin):
|
||||
__tablename__ = 'quests'
|
||||
|
||||
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True)
|
||||
project = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("projects.id"), nullable=True, default=None)
|
||||
creator = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)
|
||||
name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
|
||||
description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
|
||||
date_create = sqlalchemy.Column(sqlalchemy.DateTime, default=date.today())
|
||||
deadline = sqlalchemy.Column(sqlalchemy.DateTime, default=date.today())
|
||||
realized = sqlalchemy.Column(sqlalchemy.Boolean, default=False)
|
||||
7
forms/find_project.py
Normal file
7
forms/find_project.py
Normal file
@ -0,0 +1,7 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SubmitField, StringField
|
||||
|
||||
|
||||
class FindProjectForm(FlaskForm):
|
||||
project = StringField('', default='')
|
||||
submit = SubmitField('Поиск')
|
||||
14
forms/recovery.py
Normal file
14
forms/recovery.py
Normal file
@ -0,0 +1,14 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import EmailField, SubmitField, PasswordField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class RecoveryForm(FlaskForm):
|
||||
email = EmailField('Введите почту для восстановления', validators=[DataRequired()])
|
||||
submit = SubmitField('Восстановить')
|
||||
|
||||
|
||||
class NewPasswordForm(FlaskForm):
|
||||
password = PasswordField('Пароль', validators=[DataRequired()])
|
||||
repeat_password = PasswordField('Повторите пароль', validators=[DataRequired()])
|
||||
submit = SubmitField('Восстановить')
|
||||
107
main.py
107
main.py
@ -13,10 +13,13 @@ from sqlalchemy import or_
|
||||
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.find_project import FindProjectForm
|
||||
from forms.register import RegisterForm
|
||||
from forms.new_project import NewProjectForm
|
||||
from forms.recovery import RecoveryForm, NewPasswordForm
|
||||
|
||||
from data.users import User
|
||||
from data.quests import Quests
|
||||
from data.files import Files
|
||||
from data.projects import Projects
|
||||
from data.staff_projects import StaffProjects
|
||||
@ -40,6 +43,66 @@ def base():
|
||||
return redirect('/projects')
|
||||
|
||||
|
||||
@app.route('/project/<int:id_project>')
|
||||
def project(id_project):
|
||||
if current_user.is_authenticated:
|
||||
data_session = db_session.create_session()
|
||||
current_project = data_session.query(Projects).filter(Projects.id == id_project).first()
|
||||
if current_project:
|
||||
staff = data_session.query(StaffProjects).filter(StaffProjects.project == current_project.id).all()
|
||||
if current_user.id == current_project.creator or current_user.id in list(map(lambda x: x.user, staff)):
|
||||
return render_template('project.html', project=current_project, title=current_project.name)
|
||||
else:
|
||||
abort(403)
|
||||
else:
|
||||
abort(404)
|
||||
else:
|
||||
return redirect('/login')
|
||||
|
||||
|
||||
@app.route('/recovery/confirmation/<token>', methods=['GET', 'POST'])
|
||||
def conf_recovery(token):
|
||||
try:
|
||||
user_email = s.loads(token, max_age=86400)
|
||||
data_session = db_session.create_session()
|
||||
user = data_session.query(User).filter(User.email == user_email).first()
|
||||
if user:
|
||||
form = NewPasswordForm()
|
||||
if form.validate_on_submit():
|
||||
if form.password.data != form.repeat_password.data:
|
||||
return render_template('recovery.html', title='Восстановление', form=form, recovery=0,
|
||||
message='Пароли не совпадают')
|
||||
status_password = check_password(form.password.data)
|
||||
if status_password != 'OK':
|
||||
return render_template('recovery.html', title='Восстановление', form=form, recovery=0,
|
||||
message=str(status_password))
|
||||
user.set_password(form.password.data)
|
||||
data_session.commit()
|
||||
mail(f'Для аккаунта {user.login}, успешно был обновлен пароль', user.email,
|
||||
'Изменение пароля')
|
||||
return redirect('/login?message=Пароль обновлен')
|
||||
return render_template('recovery.html', title='Восстановление', form=form, recovery=0, message='')
|
||||
else:
|
||||
return redirect('/login?message=Пользователь не найден&danger=True')
|
||||
except SignatureExpired:
|
||||
return redirect('/login?message=Срок действия ссылки истек&danger=True')
|
||||
|
||||
|
||||
@app.route('/recovery', methods=['GET', 'POST'])
|
||||
def recovery():
|
||||
if not current_user.is_authenticated:
|
||||
form = RecoveryForm()
|
||||
if form.validate_on_submit():
|
||||
token = s.dumps(form.email.data)
|
||||
link_conf = url_for('conf_recovery', token=token, _external=True)
|
||||
mail(f'Для сбросы пароля пройдите по ссылке: {link_conf}', form.email.data,
|
||||
'Восстановление доступа')
|
||||
return redirect('/login?message=Мы выслали ссылку для сброса вам на почту')
|
||||
return render_template('recovery.html', title='Восстановление пароля', form=form, recovery=True, message='')
|
||||
else:
|
||||
return redirect('/')
|
||||
|
||||
|
||||
@app.route('/projects/delete/<int:id_project>', methods=['GET', 'POST'])
|
||||
def delete_project(id_project):
|
||||
if current_user.is_authenticated:
|
||||
@ -70,11 +133,11 @@ def user_view(_login):
|
||||
data_session = db_session.create_session()
|
||||
user = data_session.query(User).filter(User.login == _login).first()
|
||||
if user:
|
||||
projects = data_session.query(Projects).filter(or_(Projects.creator == user.id, Projects.id.in_(
|
||||
current_projects = data_session.query(Projects).filter(or_(Projects.creator == user.id, Projects.id.in_(
|
||||
list(map(lambda x: x[0], data_session.query(
|
||||
StaffProjects.project).filter(
|
||||
StaffProjects.user == user.id).all()))))).all()
|
||||
resp = list(map(lambda x: get_projects_data(x), projects))
|
||||
resp = list(map(lambda x: get_projects_data(x), current_projects))
|
||||
return render_template('user_view.html', title=user.name + ' ' + user.surname, user=user,
|
||||
list_projects=resp)
|
||||
else:
|
||||
@ -91,21 +154,22 @@ def new_project():
|
||||
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():
|
||||
project = Projects(
|
||||
currnet_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)
|
||||
currnet_project.photo = save_project_logo(
|
||||
form.logo.data) if form.logo.data else 'static/images/none_project.png'
|
||||
data_session.add(currnet_project)
|
||||
data_session.flush()
|
||||
data_session.refresh(project)
|
||||
data_session.refresh(currnet_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,
|
||||
project=currnet_project.id,
|
||||
role='user',
|
||||
permission=3
|
||||
)
|
||||
@ -120,18 +184,29 @@ def new_project():
|
||||
|
||||
|
||||
@app.route('/projects', methods=['GET', 'POST'])
|
||||
def project():
|
||||
def projects():
|
||||
if current_user.is_authenticated:
|
||||
find = False
|
||||
form = FindProjectForm()
|
||||
data_session = db_session.create_session()
|
||||
resp = []
|
||||
if request.method == 'POST':
|
||||
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)
|
||||
current_projects = \
|
||||
data_session.query(Projects).filter(or_(Projects.creator == current_user.id,
|
||||
Projects.id.in_(
|
||||
list(map(lambda x: x[0],
|
||||
data_session.query(
|
||||
StaffProjects.project).filter(
|
||||
StaffProjects.user
|
||||
== current_user.id).all()))))).all()
|
||||
if form.validate_on_submit():
|
||||
new_resp = []
|
||||
for i in range(len(current_projects)):
|
||||
if str(form.project.data).lower().strip() in str(current_projects[i].name).lower().strip():
|
||||
new_resp.append(current_projects[i])
|
||||
current_projects = new_resp
|
||||
find = True
|
||||
resp = list(map(lambda x: get_projects_data(x), current_projects))
|
||||
return render_template('projects.html', title='Проекты', list_projects=resp, form=form, find=find)
|
||||
else:
|
||||
return redirect('/login')
|
||||
|
||||
|
||||
@ -92,4 +92,14 @@
|
||||
}
|
||||
.box {
|
||||
margin-left: 9vw;
|
||||
}
|
||||
.recovery_button {
|
||||
color: #ffffff;
|
||||
font-size: 1.5vw;
|
||||
transition: color 0.5s ease-in, border-bottom 0.5s ease-in;
|
||||
}
|
||||
.recovery_button:hover {
|
||||
color: #694a2d;
|
||||
border-bottom: 3px solid #f3c79e;
|
||||
text-decoration: none;
|
||||
}
|
||||
28
static/css/project.css
Normal file
28
static/css/project.css
Normal file
@ -0,0 +1,28 @@
|
||||
.projects_page {
|
||||
height: 120vw;
|
||||
background-color: #dcb495;
|
||||
}
|
||||
.project_header {
|
||||
height: 25vw;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.project_logo {
|
||||
margin-top: 30px;
|
||||
width: 15vw;
|
||||
height: 15vw;
|
||||
border: 0.2vw solid #ffffff;
|
||||
border-radius: 2vw;
|
||||
}
|
||||
.brand_block {
|
||||
height: 25vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.name_project {
|
||||
font-size: 3vw;
|
||||
}
|
||||
@ -234,7 +234,7 @@
|
||||
overflow-x: auto;
|
||||
color: #000000 !important;
|
||||
}
|
||||
.new_project_button {
|
||||
.new_project_button, .find_project_button {
|
||||
width: 13vw;
|
||||
height: 5vw;
|
||||
background-color: #000000;
|
||||
@ -242,7 +242,7 @@
|
||||
border-radius: 3vw;
|
||||
margin-left: 2vw;
|
||||
}
|
||||
.new_project_button_text {
|
||||
.new_project_button_text, .find_project_button_text {
|
||||
width: 13vw;
|
||||
height: 5vw;
|
||||
text-align: center;
|
||||
@ -252,11 +252,11 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.new_project_button_link {
|
||||
.new_project_button_lin, find_project_button_linkk {
|
||||
width: 13vw;
|
||||
height: 5vw;
|
||||
}
|
||||
.new_project_button_link:hover {
|
||||
.new_project_button_link:hover, .find_project_button_linkk:hover {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
61
static/css/recovery.css
Normal file
61
static/css/recovery.css
Normal file
@ -0,0 +1,61 @@
|
||||
.recovery_page {
|
||||
height: 60vw;
|
||||
background-color: #dcb495;
|
||||
}
|
||||
.recovery {
|
||||
width: 100%;
|
||||
height: 60vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.header_title {
|
||||
text-align: center;
|
||||
color: #000000;
|
||||
font-size: 3.5vw;
|
||||
width: 100%;
|
||||
}
|
||||
.recovery_form {
|
||||
margin-top: 55px;
|
||||
width: 70%;
|
||||
display: flex;
|
||||
}
|
||||
.data_block {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.form_data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 2%;
|
||||
margin-left: 2%;
|
||||
height: 100%;
|
||||
}
|
||||
.form-label {
|
||||
font-size: 1.3vw;
|
||||
}
|
||||
.input_data {
|
||||
color: #000000;
|
||||
border: 0.1vw solid #595008;
|
||||
height: 4.7vw;
|
||||
width: 30vw;
|
||||
background-color: #dbc3af;
|
||||
border-radius: 5vw;
|
||||
font-size: 1.3vw;
|
||||
}
|
||||
.recovery_button {
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
min-width: 20vw !important;
|
||||
height: 5vw;
|
||||
border-radius: 5vw;
|
||||
vertical-align: middle;
|
||||
font-size: 1.5vw;
|
||||
margin-left: 20px;
|
||||
}
|
||||
@ -2,14 +2,14 @@
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="../static/css/base.css" />
|
||||
<link rel="stylesheet" href="../../static/css/base.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="icon" href="../static/images/logo_b.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="../../static/images/logo_b.ico" type="image/x-icon" />
|
||||
<title>{{title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
@ -38,7 +38,7 @@
|
||||
<nav class="navbar" id="navbar">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="../static/images/logo_b.png" class="nav_logo" />
|
||||
<img src="../../static/images/logo_b.png" class="nav_logo" />
|
||||
</a>
|
||||
<a class="auth_button" href="/login">Авторизация</a>
|
||||
</div>
|
||||
@ -49,7 +49,7 @@
|
||||
<footer class="footer">
|
||||
<div class="footer_block">
|
||||
<a href="/#header_block"
|
||||
><img class="footer_logo" src="../static/images/logo_w.png"
|
||||
><img class="footer_logo" src="../../static/images/logo_w.png"
|
||||
/></a>
|
||||
<strong class="footer_rights">© All rights reserved</strong>
|
||||
</div>
|
||||
|
||||
@ -26,6 +26,9 @@
|
||||
<a class="input_button register_button" type="submit" href="/register">
|
||||
<div class="register"><strong>Регистрация</strong></div>
|
||||
</a>
|
||||
<a class="recovery_button" type="submit" href="/recovery">
|
||||
<div class="recovery"><strong>Забыли пароль?</strong></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
|
||||
28
templates/project.html
Normal file
28
templates/project.html
Normal file
@ -0,0 +1,28 @@
|
||||
<link rel="stylesheet" href="../static/css/project.css"/>
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="projects_page">
|
||||
<div class="project_header">
|
||||
<div class="edit_block">
|
||||
|
||||
</div>
|
||||
<div class="brand_block">
|
||||
<img class="project_logo" src="../{{project.photo}}"/>
|
||||
<p class="name_project">{{ project.name }}</p>
|
||||
</div>
|
||||
<div class="notification_block">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="body_block">
|
||||
<div class="staff_block">
|
||||
|
||||
</div>
|
||||
<div class="task_block">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="files_block">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -8,8 +8,19 @@
|
||||
</div>
|
||||
<div class="find_block">
|
||||
<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>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.project(class="find_input_text", type="text", placeholder='Имя проекта') }}
|
||||
{% for error in form.project.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
{{ form.submit(type="submit", class="find_input_button") }}
|
||||
{% if find == 1 %}
|
||||
<div class="find_project_button">
|
||||
<a class="find_project_button_link" href="/projects">
|
||||
<p class="find_project_button_text">Сброс</p>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="new_project_button">
|
||||
<a class="new_project_button_link" href="/projects/new">
|
||||
<p class="new_project_button_text">Создать</p>
|
||||
@ -35,7 +46,8 @@
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="panelsStayOpen-collapse{{ project.id }}" class="accordion-collapse collapse project_description_block"
|
||||
<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">
|
||||
@ -60,7 +72,7 @@
|
||||
</div>
|
||||
<div class="open_project_block">
|
||||
<div class="open_button">
|
||||
<a class="open_button_link" href="/projects/{{ project.id }}">
|
||||
<a class="open_button_link" href="/project/{{ project.id }}">
|
||||
<p class="open_button_text">Открыть</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
52
templates/recovery.html
Normal file
52
templates/recovery.html
Normal file
@ -0,0 +1,52 @@
|
||||
<link rel="stylesheet" href="../../static/css/recovery.css"/>
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="recovery_page">
|
||||
<div class="recovery">
|
||||
{% if recovery == 1 %}
|
||||
<h1 class="header_title">Восстановление пароля</h1>
|
||||
<form action="" method="post" class="recovery_form">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="data_block">
|
||||
<div class="form_data">
|
||||
<label class="form-label">{{ form.email.label }}</label>
|
||||
{{ form.email(class="input_data", type="email", placeholder='example@mail.ex') }}
|
||||
{% for error in form.email.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ form.submit(type="submit", class="recovery_button") }}
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<h1 class="header_title">Восстановление пароля</h1>
|
||||
<form action="" method="post" class="recovery_form">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="data_block">
|
||||
<div class="form_data">
|
||||
<label class="form-label">{{ form.password.label }}</label>
|
||||
{{ form.password(class="input_data", type="password", placeholder='good_password') }}
|
||||
{% for error in form.password.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="form_data">
|
||||
<label class="form-label">{{ form.repeat_password.label }}</label>
|
||||
{{ form.repeat_password(class="input_data", type="password", placeholder='good_password') }}
|
||||
{% for error in form.repeat_password.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ form.submit(type="submit", class="recovery_button") }}
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="message_block">
|
||||
{% if message != '' %}
|
||||
<div class="alert alert-danger message" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user