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
|
2022-06-27 21:27:09 +00:00
|
|
|
from flask_admin.model.template import EndpointLinkRowAction
|
2022-02-23 19:36:29 +00:00
|
|
|
from flask_security import admin_change_password, current_user
|
2022-06-20 01:08:57 +00:00
|
|
|
from flask_wtf import FlaskForm
|
2021-07-30 00:50:49 +00:00
|
|
|
|
2022-06-01 21:57:28 +00:00
|
|
|
from .advlabdb_independent_funs import (
|
|
|
|
deep_getattr,
|
2022-06-30 17:44:35 +00:00
|
|
|
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,
|
|
|
|
initActiveSemesterMenuLinks,
|
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
|
2022-08-09 12:46:48 +00:00
|
|
|
from .models import Assistant, GroupExperiment, SemesterExperiment, User, db
|
|
|
|
|
|
|
|
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):
|
|
|
|
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}")
|
|
|
|
raise ModelViewException("Unauthorized action!")
|
|
|
|
|
|
|
|
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():
|
2022-06-30 17:49:38 +00:00
|
|
|
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
|
|
|
|
):
|
2022-06-30 17:49:38 +00:00
|
|
|
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
|
|
|
|
|
2022-06-30 17:49:38 +00:00
|
|
|
if any_final_experiment_mark_changed:
|
2022-06-30 15:53:46 +00:00
|
|
|
group_experiment.update_experiment_marks_missing()
|
2022-06-28 11:05:57 +00:00
|
|
|
|
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(
|
|
|
|
"assistant_group_experiment_form.html",
|
|
|
|
form=form,
|
|
|
|
experiment_label=group_experiment.semester_experiment.experiment.str(),
|
|
|
|
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):
|
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-05-30 15:02:00 +00:00
|
|
|
return self.render("docs/docs.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.init_app(app)
|
|
|
|
|
|
|
|
assistantSpace.add_view(AssistantGroupExperimentView(GroupExperiment, url="group_experiment"))
|
|
|
|
assistantSpace.add_view(AssistantUserView(User, url="user"))
|
|
|
|
assistantSpace.add_view(AssistantDocsView(name="Docs", url="docs"))
|
2021-07-30 22:12:37 +00:00
|
|
|
|
2022-08-09 12:46:48 +00:00
|
|
|
initActiveSemesterMenuLinks(assistantSpace, app)
|