1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-11-12 21:40:41 +00:00
AdvLabDB/advlabdb/assistantModelViews.py

275 lines
9.9 KiB
Python
Raw Normal View History

2022-06-20 01:08:57 +00:00
from flask import flash, redirect, request, url_for
2022-08-09 12:46:48 +00:00
from flask_admin import Admin as FlaskAdmin
2022-02-23 18:37:09 +00:00
from flask_admin import expose
from flask_admin.contrib.sqla.fields import QuerySelectField
2022-09-11 18:05:09 +00:00
from flask_admin.menu import MenuLink
2022-06-27 21:27:09 +00:00
from flask_admin.model.template import EndpointLinkRowAction
2022-08-15 20:22:36 +00:00
from flask_login import current_user
from flask_security.changeable import admin_change_password
2022-06-20 01:08:57 +00:00
from flask_wtf import FlaskForm
from markupsafe import Markup
from wtforms.validators import DataRequired
2021-07-30 00:50:49 +00:00
2022-06-01 21:57:28 +00:00
from .advlabdb_independent_funs import (
deep_getattr,
experiment_marks_missing_formatter,
2022-06-01 21:57:28 +00:00
flashRandomPassword,
str_formatter,
)
2022-08-09 12:46:48 +00:00
from .custom_classes import (
SecureAssistantBaseView,
SecureAssistantIndexView,
SecureAssistantModelView,
)
2022-07-03 15:11:33 +00:00
from .exceptions import ModelViewException
2022-06-20 01:08:57 +00:00
from .forms import assistant_group_experiment_form_factory
2022-06-17 18:56:14 +00:00
from .model_dependent_funs import (
generate_new_password_field,
2022-06-26 22:20:30 +00:00
parse_selection_mark_field,
2022-06-17 18:56:14 +00:00
user_info_fields,
)
2022-06-20 01:08:57 +00:00
from .model_independent_funs import randomPassword, reportBadAttempt
from .models import Assistant, GroupExperiment, Semester, SemesterExperiment, User, db
2022-08-09 12:46:48 +00:00
assistantSpace = FlaskAdmin(
name="Assistant@AdvLabDB",
url="/assistant",
template_mode="bootstrap4",
index_view=SecureAssistantIndexView(name="Home", url="/assistant", endpoint="assistant"),
)
2021-07-30 00:50:49 +00:00
2022-06-17 20:20:42 +00:00
class AssistantGroupExperimentView(SecureAssistantModelView):
def is_accessible(self):
if not super().is_accessible():
return False
active_semester = current_user.active_semester
if active_semester.done:
semester_changed = current_user.set_last_semester_as_active()
if not semester_changed:
flash(
Markup(
2022-09-21 14:52:04 +00:00
f"Active semester {active_semester} is set as done. Therefore, you are not allowed to view or edit any marks in this semester. You should change your active semester in <a href='/user-settings'>user settings</a> if possible."
),
"danger",
)
return False
return True
2022-06-17 20:20:42 +00:00
column_display_actions = True
column_list = [
"semester_experiment.experiment",
"group.number",
2022-06-27 20:12:52 +00:00
"group.part_students",
"appointments",
2022-06-27 22:58:14 +00:00
"experiment_marks_missing",
2022-06-17 20:20:42 +00:00
"note",
]
column_labels = {
"semester_experiment.experiment": "Experiment",
"group.number": "Group number",
2022-06-27 20:12:52 +00:00
"group.part_students": "Students",
2022-06-17 20:20:42 +00:00
}
2022-06-27 20:12:52 +00:00
2022-06-28 01:53:37 +00:00
column_default_sort = ("experiment_marks_missing", True)
column_extra_row_actions = [
EndpointLinkRowAction(
icon_class="fa fa-pencil",
2022-06-28 14:57:55 +00:00
endpoint="assistant_group_experiment.form_view",
2022-06-28 01:53:37 +00:00
title="Edit",
)
]
2022-07-02 22:48:05 +00:00
@staticmethod
2022-06-27 20:12:52 +00:00
def part_students_formatter(view, context, model, name):
part_students = deep_getattr(model, name)
if part_students is not None:
2022-07-02 14:46:02 +00:00
return ", ".join(str(part_student.student) for part_student in part_students)
2022-06-27 20:12:52 +00:00
2022-06-28 01:53:37 +00:00
return ""
2022-06-27 20:12:52 +00:00
2022-07-02 22:48:05 +00:00
@staticmethod
2022-06-27 20:12:52 +00:00
def appointments_formatter(view, context, model, name):
appointments = deep_getattr(model, name)
if appointments is not None:
2022-07-02 14:46:02 +00:00
return ", ".join(str(appointment.date) for appointment in appointments)
2022-06-27 20:12:52 +00:00
2022-06-28 01:53:37 +00:00
return ""
2022-06-17 20:20:42 +00:00
column_formatters = {
"semester_experiment.experiment": str_formatter,
2022-06-27 20:12:52 +00:00
"group.part_students": part_students_formatter,
"appointments": appointments_formatter,
2022-06-28 01:53:37 +00:00
"experiment_marks_missing": experiment_marks_missing_formatter,
2022-06-17 20:20:42 +00:00
}
def query_modifier(self, query):
return (
query.join(SemesterExperiment)
.where(SemesterExperiment.semester == current_user.active_semester)
.join(SemesterExperiment.assistants)
.where(Assistant.user == current_user)
)
2022-06-20 01:08:57 +00:00
@expose("/form/", methods=("GET", "POST"))
def form_view(self):
group_experiment_id_str = request.args.get("id")
try:
group_experiment = db.session.get(GroupExperiment, int(group_experiment_id_str))
2022-07-03 15:11:33 +00:00
except Exception:
2022-08-09 12:46:48 +00:00
red = url_for("main.index") + "assistant/group_experiment"
2022-06-20 01:08:57 +00:00
flash("No valid group experiment id")
return redirect(red)
if group_experiment not in self.get_query():
reportBadAttempt("Assistant {current_user} tried to edit {group_experiment}")
2022-09-21 14:52:04 +00:00
self.handle_view_exception(ModelViewException("Unauthorized action!"))
return redirect(self.url)
2022-06-20 01:08:57 +00:00
form, appointments, experiment_marks = assistant_group_experiment_form_factory(current_user, group_experiment)
num_appointments = len(appointments)
appointment_fields = [
getattr(form, f"appointment_{appointment_num}") for appointment_num in range(1, num_appointments + 1)
]
num_experiment_marks = len(experiment_marks)
experiment_mark_students = [experiment_mark.part_student.student for experiment_mark in experiment_marks]
oral_experiment_mark_fields = [
getattr(form, f"oral_experiment_mark_{experiment_mark_num}")
for experiment_mark_num in range(1, num_experiment_marks + 1)
]
protocol_experiment_mark_fields = [
getattr(form, f"protocol_experiment_mark_{experiment_mark_num}")
for experiment_mark_num in range(1, num_experiment_marks + 1)
]
if form.validate_on_submit():
any_final_experiment_mark_changed = False
2022-06-26 22:20:30 +00:00
try:
for ind, appointment in enumerate(appointments):
appointment.date = appointment_fields[ind].data
2022-06-20 01:08:57 +00:00
2022-06-26 22:20:30 +00:00
for ind, experiment_mark in enumerate(experiment_marks):
2022-06-27 21:27:09 +00:00
form_oral_mark = parse_selection_mark_field(oral_experiment_mark_fields[ind])
form_protocol_mark = parse_selection_mark_field(protocol_experiment_mark_fields[ind])
if (
form_oral_mark != experiment_mark.oral_mark
or form_protocol_mark != experiment_mark.protocol_mark
):
final_experiment_mark_changed = experiment_mark.set_oral_protocol_mark(
form_oral_mark,
form_protocol_mark,
call_update_experiment_marks_missing=False,
)
if final_experiment_mark_changed:
any_final_experiment_mark_changed = True
2022-06-27 21:27:09 +00:00
experiment_mark.assistant = current_user.assistant
if any_final_experiment_mark_changed:
group_experiment.update_experiment_marks_missing()
2022-06-26 22:20:30 +00:00
group_experiment.note = form.note.data
db.session.commit()
2022-08-09 12:46:48 +00:00
red = url_for("main.index") + "assistant/group_experiment"
2022-06-26 22:20:30 +00:00
return redirect(red)
except Exception as ex:
flash(str(ex), "error")
db.session.rollback()
2022-06-20 01:08:57 +00:00
2022-06-27 20:12:52 +00:00
final_experiment_marks = [experiment_mark.final_experiment_mark for experiment_mark in experiment_marks]
2022-06-20 01:08:57 +00:00
return self.render(
2022-09-11 12:55:53 +00:00
"assistant_group_experiment_form.jinja.html",
2022-06-20 01:08:57 +00:00
form=form,
2022-09-19 13:49:57 +00:00
semester_experiment=group_experiment.semester_experiment,
2022-06-20 01:08:57 +00:00
group_number=group_experiment.group.number,
appointment_fields=appointment_fields,
experiment_mark_zip=zip(
2022-06-27 20:12:52 +00:00
experiment_mark_students,
oral_experiment_mark_fields,
protocol_experiment_mark_fields,
final_experiment_marks,
2022-06-20 01:08:57 +00:00
),
)
2022-06-17 20:20:42 +00:00
2021-11-30 00:47:37 +00:00
class AssistantUserView(SecureAssistantModelView):
2022-06-20 01:08:57 +00:00
class EditForm(FlaskForm):
@staticmethod
def semesterQueryFactory():
# Show only last two semesters to assistants
2023-11-02 17:09:10 +00:00
return Semester.query.order_by(Semester.id.desc()).where(Semester.done is False).limit(2)
active_semester = QuerySelectField(
"Active Semester",
query_factory=semesterQueryFactory,
validators=[DataRequired()],
default=Semester.lastSemester,
description="You should change the active semester to the last semester. Do not forget to click save! Only last two semesters are shown that are not set as done.",
)
2022-06-17 18:56:14 +00:00
phone_number, mobile_phone_number, building, room = user_info_fields()
2022-02-23 19:36:29 +00:00
2022-06-17 18:56:14 +00:00
generate_new_password = generate_new_password_field()
2022-02-23 19:36:29 +00:00
can_edit = True
2022-05-30 13:38:20 +00:00
can_view_details = True
2022-02-23 19:36:29 +00:00
column_display_actions = True
2022-07-02 22:48:05 +00:00
column_sortable_list: list[str] = []
2021-11-30 00:47:37 +00:00
column_list = [
"email",
"phone_number",
"mobile_phone_number",
"building",
2022-02-23 19:36:29 +00:00
"room",
2021-11-30 00:47:37 +00:00
"assistant.semester_experiments",
]
column_labels = {
"assistant.semester_experiments": "Semester Experiments",
}
2022-05-21 16:30:23 +00:00
def query_modifier(self, query):
return query.where(User.id == current_user.id)
2021-11-30 00:47:37 +00:00
2022-02-23 19:36:29 +00:00
def on_model_change(self, form, model, is_created):
if form.generate_new_password.data:
password = randomPassword()
2022-06-28 15:01:42 +00:00
flashRandomPassword(model.email, password)
2022-02-23 19:36:29 +00:00
admin_change_password(model, password, notify=False) # Password is automatically hashed with this function
2021-11-30 00:47:37 +00:00
2022-02-23 18:37:09 +00:00
class AssistantDocsView(SecureAssistantBaseView):
2022-03-04 02:49:02 +00:00
@expose("/")
2022-02-23 18:37:09 +00:00
def index(self):
2022-09-11 12:55:53 +00:00
return self.render("docs/docs.jinja.html", role="assistant")
2022-02-23 18:37:09 +00:00
2022-08-09 12:46:48 +00:00
def init_assistant_model_views(app):
assistantSpace.add_view(AssistantGroupExperimentView(GroupExperiment, url="group_experiment"))
assistantSpace.add_view(AssistantDocsView(name="Docs", url="docs"))
2022-09-21 14:52:04 +00:00
# Don't add to menu
# Has to be placed before assistantSpace.init_app
assistantSpace._views.append(AssistantUserView(User, url="user"))
assistantSpace.add_link(MenuLink(name="User settings", url="/user-settings"))
2022-09-11 18:05:09 +00:00
assistantSpace.add_link(MenuLink(name="Logout", url="/logout"))
2022-09-21 14:52:04 +00:00
assistantSpace.init_app(app)