Сделал задачи в проектах, можно создавать задачу, устанавливать дедлайн, отвечать на задачу файлами или текстом
This commit is contained in:
parent
d18352d166
commit
88c2430ef4
@ -5,12 +5,11 @@ from datetime import datetime
|
|||||||
from .db_session import SqlAlchemyBase
|
from .db_session import SqlAlchemyBase
|
||||||
|
|
||||||
|
|
||||||
class Proofs(SqlAlchemyBase, UserMixin):
|
class Answer(SqlAlchemyBase, UserMixin):
|
||||||
__tablename__ = 'proofs'
|
__tablename__ = 'answer'
|
||||||
|
|
||||||
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True)
|
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True)
|
||||||
quest = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("quests.id"), nullable=True, default=None)
|
quest = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("quests.id"), nullable=True, default=None)
|
||||||
file = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("files.id"), nullable=True, default=None)
|
|
||||||
text = sqlalchemy.Column(sqlalchemy.Text, nullable=True, default=None)
|
text = sqlalchemy.Column(sqlalchemy.Text, nullable=True, default=None)
|
||||||
creator = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("users.id"), nullable=True,
|
creator = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("users.id"), nullable=True,
|
||||||
default=None)
|
default=None)
|
||||||
12
data/proof_file.py
Normal file
12
data/proof_file.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import sqlalchemy
|
||||||
|
from flask_login import UserMixin
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .db_session import SqlAlchemyBase
|
||||||
|
|
||||||
|
|
||||||
|
class FileProof(SqlAlchemyBase, UserMixin):
|
||||||
|
__tablename__ = 'file_proof'
|
||||||
|
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True)
|
||||||
|
answer = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("answer.id"), nullable=True, default=None)
|
||||||
|
file = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("files.id"), nullable=True, default=None)
|
||||||
@ -1,5 +1,6 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, SubmitField, TextAreaField, DateField, TimeField, FileField
|
from wtforms import StringField, SubmitField, TextAreaField, DateField, TimeField, MultipleFileField, \
|
||||||
|
BooleanField
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
|
||||||
@ -13,5 +14,8 @@ class NewTask(FlaskForm):
|
|||||||
|
|
||||||
class AnswerTask(FlaskForm):
|
class AnswerTask(FlaskForm):
|
||||||
text = TextAreaField('Письменный ответ')
|
text = TextAreaField('Письменный ответ')
|
||||||
file = FileField('Файловый ответ')
|
file = MultipleFileField('Файловый ответ')
|
||||||
submit = SubmitField('Ответить')
|
realized = BooleanField('Задача решена')
|
||||||
|
deadline_date = DateField('Дедлайн', validators=[DataRequired()])
|
||||||
|
deadline_time = TimeField('', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Сохранить')
|
||||||
|
|||||||
37
functions.py
37
functions.py
@ -1,10 +1,13 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
import smtplib
|
import smtplib
|
||||||
from json import loads
|
from json import loads
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
from data.roles import Roles
|
from data.roles import Roles
|
||||||
from data.users import User
|
from data.users import User
|
||||||
from data.staff_projects import StaffProjects
|
from data.staff_projects import StaffProjects
|
||||||
|
from data.answer import Answer
|
||||||
|
from data.files import Files
|
||||||
from data import db_session
|
from data import db_session
|
||||||
import uuid
|
import uuid
|
||||||
import pymorphy2
|
import pymorphy2
|
||||||
@ -95,12 +98,14 @@ def save_project_logo(photo):
|
|||||||
|
|
||||||
|
|
||||||
def overdue_quest_project(quest):
|
def overdue_quest_project(quest):
|
||||||
if str(quest.deadline.date()) == str(datetime.datetime.now().date()):
|
if quest.deadline is None:
|
||||||
|
quest.overdue = ''
|
||||||
|
elif str(quest.deadline.date()) == str(datetime.datetime.now().date()):
|
||||||
quest.overdue = 'today'
|
quest.overdue = 'today'
|
||||||
elif quest.deadline < datetime.datetime.now():
|
elif quest.deadline < datetime.datetime.now():
|
||||||
quest.overdue = 'yes'
|
quest.overdue = 'yes'
|
||||||
quest.time_left = 'Просрочено на' + round_date(quest.deadline)
|
quest.time_left = 'Просрочено на' + round_date(quest.deadline)
|
||||||
else:
|
elif quest.deadline > datetime.datetime.now():
|
||||||
quest.overdue = 'no'
|
quest.overdue = 'no'
|
||||||
quest.time_left = 'Еще есть: ' + round_date(quest.deadline)
|
quest.time_left = 'Еще есть: ' + round_date(quest.deadline)
|
||||||
return quest
|
return quest
|
||||||
@ -120,3 +125,31 @@ def round_date(date_time):
|
|||||||
if difference:
|
if difference:
|
||||||
resp += ', ' if resp else ' ' + f'{difference} {morph.parse("день")[0].make_agree_with_number(difference).word}'
|
resp += ', ' if resp else ' ' + f'{difference} {morph.parse("день")[0].make_agree_with_number(difference).word}'
|
||||||
return f'{resp}'
|
return f'{resp}'
|
||||||
|
|
||||||
|
|
||||||
|
def save_proof_quest(project, file, user_id):
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
path = f'static/app_files/all_projects/{str(project.id)}/{str(file.filename)}'
|
||||||
|
file_check = data_session.query(Files).filter(Files.path == path).first()
|
||||||
|
file.save(path)
|
||||||
|
if file_check:
|
||||||
|
return file_check.id
|
||||||
|
file = Files(
|
||||||
|
path=path,
|
||||||
|
user=user_id,
|
||||||
|
up_date=datetime.datetime.now()
|
||||||
|
)
|
||||||
|
data_session.add(file)
|
||||||
|
data_session.flush()
|
||||||
|
data_session.refresh(file)
|
||||||
|
file_id = file.id
|
||||||
|
data_session.commit()
|
||||||
|
data_session.close()
|
||||||
|
return file_id
|
||||||
|
|
||||||
|
|
||||||
|
def find_files_answer(file_id):
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
file = data_session.query(Files).filter(Files.id == file_id).first()
|
||||||
|
return {'id': file.id, 'path': file.path, 'user': file.user, 'up_date': file.up_date,
|
||||||
|
'current_path': file.path[str(file.path).find('all_projects') + 13:].split('/')}
|
||||||
|
|||||||
106
main.py
106
main.py
@ -12,7 +12,7 @@ from sqlalchemy import or_
|
|||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo, \
|
from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo, \
|
||||||
overdue_quest_project
|
overdue_quest_project, save_proof_quest, find_files_answer
|
||||||
from forms.edit_profile import EditProfileForm
|
from forms.edit_profile import EditProfileForm
|
||||||
from forms.login import LoginForm
|
from forms.login import LoginForm
|
||||||
from forms.find_project import FindProjectForm
|
from forms.find_project import FindProjectForm
|
||||||
@ -24,6 +24,8 @@ from forms.task import NewTask, AnswerTask
|
|||||||
|
|
||||||
from data.users import User
|
from data.users import User
|
||||||
from data.quests import Quests
|
from data.quests import Quests
|
||||||
|
from data.answer import Answer
|
||||||
|
from data.proof_file import FileProof
|
||||||
from data.files import Files
|
from data.files import Files
|
||||||
from data.projects import Projects
|
from data.projects import Projects
|
||||||
from data.staff_projects import StaffProjects
|
from data.staff_projects import StaffProjects
|
||||||
@ -50,7 +52,35 @@ def base():
|
|||||||
return redirect('/projects')
|
return redirect('/projects')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/project/<int:id_project>/quest/<int:id_task>')
|
@app.route('/project/<int:id_project>/file/<int:id_file>/delete')
|
||||||
|
def delete_file(id_project, id_file):
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
current_project = data_session.query(Projects).filter(Projects.id == id_project).first()
|
||||||
|
current_file = data_session.query(Files).filter(Files.id == id_file).first()
|
||||||
|
if current_project and current_file:
|
||||||
|
if current_user.id in map(lambda x: x[0], data_session.query(StaffProjects.user).filter(
|
||||||
|
StaffProjects.project == current_project.id).all()) or current_user.id == current_project.creator:
|
||||||
|
current_proof = data_session.query(FileProof).filter(FileProof.file == id_file).all()
|
||||||
|
os.remove(current_file.path)
|
||||||
|
data_session.delete(current_file)
|
||||||
|
if current_proof:
|
||||||
|
quest = data_session.query(Answer.quest).filter(Answer.id == current_proof[0].answer).first()
|
||||||
|
for i in current_proof:
|
||||||
|
data_session.delete(i)
|
||||||
|
data_session.commit()
|
||||||
|
return redirect(f'/project/{current_project.id}/quest/{quest[0]}')
|
||||||
|
data_session.commit()
|
||||||
|
return redirect(f'/project/{current_project.id}')
|
||||||
|
else:
|
||||||
|
abort(403)
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
else:
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/project/<int:id_project>/quest/<int:id_task>', methods=['GET', 'POST'])
|
||||||
def task_project(id_project, id_task):
|
def task_project(id_project, id_task):
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
data_session = db_session.create_session()
|
data_session = db_session.create_session()
|
||||||
@ -58,15 +88,75 @@ def task_project(id_project, id_task):
|
|||||||
current_task = data_session.query(Quests).filter(Quests.id == id_task).first()
|
current_task = data_session.query(Quests).filter(Quests.id == id_task).first()
|
||||||
if current_project and current_task and current_task.project == current_project.id:
|
if current_project and current_task and current_task.project == current_project.id:
|
||||||
form = AnswerTask()
|
form = AnswerTask()
|
||||||
return render_template('decision.html', title='Решение', project=current_project, task=current_task,
|
current_answer = data_session.query(Answer).filter(Answer.quest == current_task.id).first()
|
||||||
form=form)
|
list_files = None
|
||||||
|
if form.validate_on_submit():
|
||||||
|
if form.deadline_date.data and form.deadline_time.data:
|
||||||
|
deadline = datetime.datetime.combine(form.deadline_date.data, form.deadline_time.data)
|
||||||
|
else:
|
||||||
|
deadline = None
|
||||||
|
current_task.deadline = deadline
|
||||||
|
if current_answer:
|
||||||
|
current_answer.text = form.text.data if form.text.data else None
|
||||||
|
current_answer.date_edit = datetime.datetime.now()
|
||||||
|
current_task.realized = form.realized.data
|
||||||
|
data_session.commit()
|
||||||
|
if form.file.data[0].filename:
|
||||||
|
files = list(
|
||||||
|
map(lambda x: save_proof_quest(current_project, x, current_user.id), form.file.data))
|
||||||
|
for i in files:
|
||||||
|
if not data_session.query(FileProof).filter(FileProof.answer == current_answer.id,
|
||||||
|
FileProof.file == i).first():
|
||||||
|
proof_file = FileProof(
|
||||||
|
answer=current_answer.id,
|
||||||
|
file=i
|
||||||
|
)
|
||||||
|
data_session.add(proof_file)
|
||||||
|
data_session.commit()
|
||||||
|
else:
|
||||||
|
if form.file.data[0].filename:
|
||||||
|
files = list(
|
||||||
|
map(lambda x: save_proof_quest(current_project, x, current_user.id), form.file.data))
|
||||||
|
else:
|
||||||
|
files = False
|
||||||
|
current_task.realized = form.realized.data
|
||||||
|
current_answer = Answer(
|
||||||
|
quest=current_task.id,
|
||||||
|
text=form.text.data if form.text.data else None,
|
||||||
|
creator=current_user.id,
|
||||||
|
date_create=datetime.datetime.now(),
|
||||||
|
date_edit=datetime.datetime.now()
|
||||||
|
)
|
||||||
|
data_session.add(current_answer)
|
||||||
|
data_session.flush()
|
||||||
|
data_session.refresh(current_answer)
|
||||||
|
if files:
|
||||||
|
for i in files:
|
||||||
|
proof_file = FileProof(
|
||||||
|
proof=current_answer.id,
|
||||||
|
file=i
|
||||||
|
)
|
||||||
|
data_session.add(proof_file)
|
||||||
|
data_session.commit()
|
||||||
|
return redirect(f'/project/{current_project.id}')
|
||||||
|
if current_answer:
|
||||||
|
form.text.data = current_answer.text
|
||||||
|
form.realized.data = current_task.realized
|
||||||
|
files = data_session.query(FileProof).filter(FileProof.answer == current_answer.id).all()
|
||||||
|
if files:
|
||||||
|
list_files = list(map(lambda x: find_files_answer(x.file), files))
|
||||||
|
if current_task.deadline and current_task.deadline:
|
||||||
|
form.deadline_date.data = current_task.deadline.date()
|
||||||
|
form.deadline_time.data = current_task.deadline.time()
|
||||||
|
return render_template('answer.html', title='Решение', project=current_project, task=current_task,
|
||||||
|
form=form, list_files=list_files)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
else:
|
else:
|
||||||
return redirect('/login')
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/project/<int:id_project>/task/new', methods=['GET', 'POST'])
|
@app.route('/project/<int:id_project>/quest/new', methods=['GET', 'POST'])
|
||||||
def new_task_project(id_project):
|
def new_task_project(id_project):
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
data_session = db_session.create_session()
|
data_session = db_session.create_session()
|
||||||
@ -166,7 +256,11 @@ def project(id_project):
|
|||||||
User.id.in_(list(map(lambda x: x.user, staff)))).all())) if staff else []
|
User.id.in_(list(map(lambda x: x.user, staff)))).all())) if staff else []
|
||||||
quests = data_session.query(Quests).filter(Quests.project == current_project.id).all()
|
quests = data_session.query(Quests).filter(Quests.project == current_project.id).all()
|
||||||
if quests:
|
if quests:
|
||||||
quests.sort(key=lambda x: (x.realized, x.deadline))
|
quests_sort = sorted(list(filter(lambda x: x.deadline is not None, quests)),
|
||||||
|
key=lambda x: (x.realized, x.deadline))
|
||||||
|
quests = list(filter(lambda x: x.realized == 0, quests_sort)) + list(
|
||||||
|
filter(lambda x: x.deadline is None, quests)) + list(
|
||||||
|
filter(lambda x: x.realized == 1, quests_sort))
|
||||||
quests = list(map(lambda x: overdue_quest_project(x), quests))
|
quests = list(map(lambda x: overdue_quest_project(x), quests))
|
||||||
return render_template('project.html',
|
return render_template('project.html',
|
||||||
project=current_project,
|
project=current_project,
|
||||||
|
|||||||
0
static/app_files/all_projects/заглушка
Normal file
0
static/app_files/all_projects/заглушка
Normal file
243
static/css/answer.css
Normal file
243
static/css/answer.css
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
body {
|
||||||
|
background-color: #dcb495 !important;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.decision_page {
|
||||||
|
background-color: #dcb495;
|
||||||
|
min-height: 100vw;
|
||||||
|
height: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin: 3vw;
|
||||||
|
margin-bottom: 20vw;
|
||||||
|
}
|
||||||
|
.link_back_block {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
.link_back {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
width: 15vw;
|
||||||
|
height: 4.5vw;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 5vw;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.link_back:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.link_back_text {
|
||||||
|
font-size: 1.5vw;
|
||||||
|
margin-top: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.name_block {
|
||||||
|
margin-top: 3vw;
|
||||||
|
width: 90%;
|
||||||
|
height: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.title_block {
|
||||||
|
width: 90%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.title_task, .files_title {
|
||||||
|
text-align: center;
|
||||||
|
color: #000000;
|
||||||
|
font-size: 4vw;
|
||||||
|
}
|
||||||
|
.description_task {
|
||||||
|
width: 80%;
|
||||||
|
background-color: #EDCBB0;
|
||||||
|
height: auto;
|
||||||
|
max-height: 15vw;
|
||||||
|
border-radius: 2vw;
|
||||||
|
display: flex;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.description_task::-webkit-scrollbar {
|
||||||
|
width: 0.8vw !important;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.description_task::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #d49d51 !important; /* цвет плашки */
|
||||||
|
border-radius: 5vw !important; /* закругления плашки */
|
||||||
|
border: 0.25vw solid #ffffff !important;
|
||||||
|
}
|
||||||
|
.description {
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
.description_text {
|
||||||
|
font-size: 1.5vw;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
.data_block {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
.bottom_data {
|
||||||
|
margin: 2vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.form_label {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.3vw;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.input_data {
|
||||||
|
color: #000000;
|
||||||
|
border: 0.1vw solid #595008;
|
||||||
|
height: 4.5vw;
|
||||||
|
min-height: 4.5vw;
|
||||||
|
width: 30vw;
|
||||||
|
background-color: #dbc3af;
|
||||||
|
border-radius: 5vw;
|
||||||
|
font-size: 1.3vw;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.input_button {
|
||||||
|
width: 10vw;
|
||||||
|
height: 5vw;
|
||||||
|
border-radius: 5vw;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.form_data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 2%;
|
||||||
|
}
|
||||||
|
.decision_block {
|
||||||
|
margin-top: 3vw;
|
||||||
|
width: 90%;
|
||||||
|
height: 25vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.padding_data {
|
||||||
|
padding: 1vw;
|
||||||
|
}
|
||||||
|
.quest_button {
|
||||||
|
color: #ffffff;
|
||||||
|
width: 13vw;
|
||||||
|
height: 5vw;
|
||||||
|
background-color: #000000;
|
||||||
|
border: 2px solid #ffffff;
|
||||||
|
border-radius: 3vw;
|
||||||
|
margin-left: 2vw;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.form_data_button {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
width: 30vw;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.deadline {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.text_data {
|
||||||
|
width: 80%;
|
||||||
|
border-radius: 2vw !important;
|
||||||
|
min-height: 10vw;
|
||||||
|
max-height: 20vw;
|
||||||
|
}
|
||||||
|
.form_text_one {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.files_block {
|
||||||
|
width: 100%;
|
||||||
|
margin: 2vw;
|
||||||
|
background-color: #dbc3af;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
border-radius: 2vw;
|
||||||
|
min-height: 25vw;
|
||||||
|
}
|
||||||
|
.files_list {
|
||||||
|
margin: 2vw;
|
||||||
|
height: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.files {
|
||||||
|
width: 80%;
|
||||||
|
margin: 2vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 25vw;
|
||||||
|
max-height: 30vw;
|
||||||
|
}
|
||||||
|
.file {
|
||||||
|
width: 98%;
|
||||||
|
display: flex;
|
||||||
|
background-color: #694a2d;
|
||||||
|
margin: 0.5vw;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 4.5vw;
|
||||||
|
border-radius: 2vw;
|
||||||
|
}
|
||||||
|
.file_head {
|
||||||
|
width: 30vw;
|
||||||
|
margin-left: 1vw;
|
||||||
|
height: 4vw;
|
||||||
|
background-color: #694a2d !important;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.file_head_path, .file_path {
|
||||||
|
font-size: 1.5vw;
|
||||||
|
color: #ffffff !important;
|
||||||
|
font-weight: bold;
|
||||||
|
height: 3vw;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
background-color: #694a2d !important;
|
||||||
|
}
|
||||||
|
.file_buttons {
|
||||||
|
margin-right: 2vw;
|
||||||
|
}
|
||||||
|
.file_delete, .file_download {
|
||||||
|
border-radius: 1vw !important;
|
||||||
|
margin: 1vw;
|
||||||
|
width: 8vw;
|
||||||
|
height: 3vw;
|
||||||
|
}
|
||||||
|
.file_delete {
|
||||||
|
background-color: hsla(0, 100%, 62%, 0.785) !important;
|
||||||
|
border-color: hsla(0, 100%, 62%, 0.785) !important;
|
||||||
|
}
|
||||||
|
.button_text {
|
||||||
|
font-size: 1.3vw;
|
||||||
|
}
|
||||||
@ -1,124 +0,0 @@
|
|||||||
.decision_page {
|
|
||||||
height: 90vw;
|
|
||||||
background-color: #dcb495;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.link_back_block {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
}
|
|
||||||
.link_back {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #000000;
|
|
||||||
width: 15vw;
|
|
||||||
height: 4.5vw;
|
|
||||||
vertical-align: middle;
|
|
||||||
border-radius: 5vw;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.link_back:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
.link_back_text {
|
|
||||||
font-size: 1.5vw;
|
|
||||||
margin-top: 15px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.name_block {
|
|
||||||
margin-top: 3vw;
|
|
||||||
width: 90%;
|
|
||||||
height: 25vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.title_block {
|
|
||||||
width: 90%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.title_task {
|
|
||||||
text-align: center;
|
|
||||||
color: #000000;
|
|
||||||
font-size: 4vw;
|
|
||||||
}
|
|
||||||
.description_task {
|
|
||||||
width: 80%;
|
|
||||||
background-color: #EDCBB0;
|
|
||||||
height: auto;
|
|
||||||
max-height: 15vw;
|
|
||||||
border-radius: 2vw;
|
|
||||||
display: flex;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
.description {
|
|
||||||
margin: 15px;
|
|
||||||
}
|
|
||||||
.description_text {
|
|
||||||
font-size: 1.5vw;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
.data_block {
|
|
||||||
width: 90%;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center
|
|
||||||
}
|
|
||||||
.bottom_data {
|
|
||||||
margin: 2vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.form_label {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 1.3vw;
|
|
||||||
color: #000000;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.input_data {
|
|
||||||
color: #000000;
|
|
||||||
border: 0.1vw solid #595008;
|
|
||||||
height: 4.5vw;
|
|
||||||
min-height: 4.5vw;
|
|
||||||
width: 30vw;
|
|
||||||
background-color: #dbc3af;
|
|
||||||
border-radius: 5vw;
|
|
||||||
font-size: 1.3vw;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.input_button {
|
|
||||||
width: 10vw;
|
|
||||||
height: 5vw;
|
|
||||||
border-radius: 5vw;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.text_data {
|
|
||||||
border-radius: 2vw !important;
|
|
||||||
width: 35vw;
|
|
||||||
}
|
|
||||||
.form_data {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-left: 2%;
|
|
||||||
}
|
|
||||||
.decision_block {
|
|
||||||
margin-top: 3vw;
|
|
||||||
width: 90%;
|
|
||||||
height: 25vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
@ -2,6 +2,7 @@ var edit_button = document.getElementById("edit_button"),
|
|||||||
new_task_link = document.getElementById("new_task_link"),
|
new_task_link = document.getElementById("new_task_link"),
|
||||||
quest_solve_link = document.getElementById("quest_solve_link"),
|
quest_solve_link = document.getElementById("quest_solve_link"),
|
||||||
quest_solve_link_id = document.getElementById("quest_solve_link_id");
|
quest_solve_link_id = document.getElementById("quest_solve_link_id");
|
||||||
|
|
||||||
edit_button.href = String(window.location.href) + '/edit';
|
edit_button.href = String(window.location.href) + '/edit';
|
||||||
new_task_link.href = String(window.location.href) + '/task/new';
|
new_task_link.href = String(window.location.href) + '/quest/new';
|
||||||
quest_solve_link.href = String(window.location.href) + '/quest/' + quest_solve_link_id.className;
|
quest_solve_link.href = String(window.location.href) + '/quest/' + quest_solve_link_id.className;
|
||||||
94
templates/answer.html
Normal file
94
templates/answer.html
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<link rel="stylesheet" href="../../../static/css/answer.css"/>
|
||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<div class="decision_page">
|
||||||
|
<div class="link_back_block">
|
||||||
|
<a class="link_back" href="../../../project/{{ project.id }}">
|
||||||
|
<p class="link_back_text">К проекту</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="name_block">
|
||||||
|
<div class="title_block">
|
||||||
|
<h3 class="title_task">{{ task.name }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="description_task">
|
||||||
|
<div class="description">
|
||||||
|
<p class="description_text">{{ task.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if list_files %}
|
||||||
|
<div class="files">
|
||||||
|
<h2 class="files_title">Файлы</h2>
|
||||||
|
<div class="files_block">
|
||||||
|
<div class="files_list">
|
||||||
|
{% for file in list_files %}
|
||||||
|
<div class="file">
|
||||||
|
<div class="file_head">
|
||||||
|
<nav class="file_head_group" style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb file_head_path">
|
||||||
|
{% for path in file['current_path'] %}
|
||||||
|
<li class="breadcrumb-item active file_path" aria-current="page">{{ path }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="file_buttons">
|
||||||
|
<div class="btn-group file_buttons_groud">
|
||||||
|
{% if current_user.id == project.creator or task.creator == current_user.id or file.user == current_user.id %}
|
||||||
|
<a href="../file/{{ file.id }}/delete" class="btn btn-primary file_delete"><p class="button_text">Удалить</p></a>
|
||||||
|
{% endif %}
|
||||||
|
<a href="../../../{{ file['path'] }}" download="" class="btn btn-primary file_download"><p class="button_text">Скачать</p></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form_data bottom_data form_text_one">
|
||||||
|
<label class="form_label">{{ form.text.label }}</label>
|
||||||
|
{{ form.text(class="input_data text_data", type="text", id="text_data", placeholder='your answer') }}
|
||||||
|
{% for error in form.text.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="decision_block">
|
||||||
|
<form action="" method="post" class="answer_form" enctype="multipart/form-data">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<div class="data_block">
|
||||||
|
<div class="form_data bottom_data">
|
||||||
|
<label class="form_label">{{ form.file.label }}</label>
|
||||||
|
{{ form.file(class="input_data padding_data", type="file") }}
|
||||||
|
{% for error in form.file.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if current_user.id == project.creator %}
|
||||||
|
<div class="form_data">
|
||||||
|
<label class="form_label">{{ form.deadline_date.label }}</label>
|
||||||
|
{{ form.deadline_date(class="input_data deadline padding_data", type="date") }}
|
||||||
|
{% for error in form.deadline_date.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{{ form.deadline_time(class="input_data deadline padding_data", type="time") }}
|
||||||
|
{% for error in form.deadline_time.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form_data_button">
|
||||||
|
{{ form.submit(type="submit", class="quest_button") }}
|
||||||
|
<div class="box">
|
||||||
|
{{ form.realized(class="realized")}}
|
||||||
|
{{form.realized.label }}<br/>
|
||||||
|
{% for error in form.realized.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -1,41 +0,0 @@
|
|||||||
<link rel="stylesheet" href="../../../static/css/decision.css"/>
|
|
||||||
{% extends "base.html" %} {% block content %}
|
|
||||||
<div class="decision_page">
|
|
||||||
<div class="link_back_block">
|
|
||||||
<a class="link_back" href="../../../project/{{ project.id }}">
|
|
||||||
<p class="link_back_text">К проекту</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="name_block">
|
|
||||||
<div class="title_block">
|
|
||||||
<h3 class="title_task">{{ task.name }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="description_task">
|
|
||||||
<div class="description">
|
|
||||||
<p class="description_text">{{ task.description }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="decision_block">
|
|
||||||
<form action="" method="post" class="answer_form" enctype="multipart/form-data">
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
<div class="data_block">
|
|
||||||
<div class="form_data bottom_data">
|
|
||||||
<label class="form_label">{{ form.text.label }}</label>
|
|
||||||
{{ form.text(class="input_data label_data text_data", type="text", placeholder='your answer') }}
|
|
||||||
{% for error in form.text.errors %}
|
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="form_data bottom_data">
|
|
||||||
<label class="form_label">{{ form.file.label }}</label>
|
|
||||||
{{ form.file(class="input_data padding_data", type="file") }}
|
|
||||||
{% for error in form.file.errors %}
|
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@ -32,8 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
{{ form.remember_me(class="remember")}} {{
|
{{ form.remember_me(class="remember")}} {{form.remember_me.label }}<br/>
|
||||||
form.remember_me.label }}<br/>
|
|
||||||
{% for error in form.remember_me.errors %}
|
{% for error in form.remember_me.errors %}
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -72,6 +72,10 @@
|
|||||||
<div class="deadline_block alert alert-success" role="alert">
|
<div class="deadline_block alert alert-success" role="alert">
|
||||||
{{ quest.time_left }}
|
{{ quest.time_left }}
|
||||||
</div>
|
</div>
|
||||||
|
{% elif quest.overdue == '' and quest.realized != 1 %}
|
||||||
|
<div class="deadline_block alert alert-warning" role="alert">
|
||||||
|
Дедлайна нет
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="deadline_block alert alert-success" role="alert">
|
<div class="deadline_block alert alert-success" role="alert">
|
||||||
Задача выполнена
|
Задача выполнена
|
||||||
@ -92,14 +96,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="quest_solve_button">
|
<div class="quest_solve_button">
|
||||||
<a class="quest_solve_link" id="quest_solve_link">
|
<a class="quest_solve_link" href="{{ project.id }}/quest/{{ quest.id }}">
|
||||||
<p id="quest_solve_link_id" class="{{ quest.id }}"></p>
|
|
||||||
<p class="quest_solve_text">Решить</p>
|
<p class="quest_solve_text">Решить</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<div class="quest_body">
|
||||||
|
<div class="quest_description_block">
|
||||||
|
<p class="quest_description_title">Описание</p>
|
||||||
|
<div class="quest_description">
|
||||||
|
<p class="quest_description_text">{{ quest.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quest_solve_button">
|
||||||
|
<a class="quest_solve_link" href="{{ project.id }}/quest/{{ quest.id }}">
|
||||||
|
<p class="quest_solve_text">Посмотреть</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user