from flask import flash, redirect, request, url_for from flask_admin import expose from flask_admin.model.template import EndpointLinkRowAction from flask_security import admin_change_password, current_user from flask_wtf import FlaskForm from markupsafe import Markup from wtforms.fields import TextAreaField from wtforms.validators import Optional from . import assistantSpace, db from .advlabdb_independent_funs import ( deep_getattr, flashRandomPassword, missing_formatter, str_formatter, str_without_semester_formatter, ) from .customClasses import SecureAssistantBaseView, SecureAssistantModelView from .exceptions import DataBaseException, ModelViewException from .forms import assistant_group_experiment_form_factory from .model_dependent_funs import ( generate_new_password_field, initActiveSemesterMenuLinks, mark_field, parse_selection_mark_field, user_info_fields, ) from .model_independent_funs import randomPassword, reportBadAttempt from .models import ( MAX_MARK, MIN_MARK, Appointment, Assistant, ExperimentMark, GroupExperiment, SemesterExperiment, User, ) class AssistantGroupExperimentView(SecureAssistantModelView): column_display_actions = True column_list = [ "semester_experiment.experiment", "group.number", "group.part_students", "appointments", "experiment_marks_missing", "note", ] column_labels = { "semester_experiment.experiment": "Experiment", "group.number": "Group number", "group.part_students": "Students", } column_default_sort = ("experiment_marks_missing", True) column_extra_row_actions = [ EndpointLinkRowAction( icon_class="fa fa-pencil", endpoint="assistant_groupexperiment.form_view", title="Edit", ) ] def part_students_formatter(view, context, model, name): part_students = deep_getattr(model, name) if part_students is not None: return ", ".join([str(part_student.student) for part_student in part_students]) return "" def appointments_formatter(view, context, model, name): appointments = deep_getattr(model, name) if appointments is not None: return ", ".join([str(appointment.date) for appointment in appointments]) return "" def experiment_marks_missing_formatter(view, context, model, name): experiment_marks_missing = getattr(model, name) if experiment_marks_missing is True: return Markup("Yes") return "No" column_formatters = { "semester_experiment.experiment": str_formatter, "group.part_students": part_students_formatter, "appointments": appointments_formatter, "experiment_marks_missing": experiment_marks_missing_formatter, } def query_modifier(self, query): return ( query.join(SemesterExperiment) .where(SemesterExperiment.semester == current_user.active_semester) .join(SemesterExperiment.assistants) .where(Assistant.user == current_user) ) @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)) except: red = url_for("index") + "assistant/group_experiment" 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(): try: for ind, appointment in enumerate(appointments): appointment.date = appointment_fields[ind].data for ind, experiment_mark in enumerate(experiment_marks): 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 ): experiment_mark.oral_mark = form_oral_mark experiment_mark.protocol_mark = form_protocol_mark experiment_mark.assistant = current_user.assistant experiment_mark.part_student.checkThenSetFinalPartMark() group_experiment.note = form.note.data db.session.commit() red = url_for("index") + "assistant/group_experiment" return redirect(red) except Exception as ex: flash(str(ex), "error") db.session.rollback() final_experiment_marks = [experiment_mark.final_experiment_mark for experiment_mark in experiment_marks] 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( experiment_mark_students, oral_experiment_mark_fields, protocol_experiment_mark_fields, final_experiment_marks, ), ) class AssistantUserView(SecureAssistantModelView): class EditForm(FlaskForm): phone_number, mobile_phone_number, building, room = user_info_fields() generate_new_password = generate_new_password_field() can_edit = True can_view_details = True column_display_actions = True column_sortable_list = [] column_list = [ "email", "phone_number", "mobile_phone_number", "building", "room", "assistant.semester_experiments", ] column_labels = { "assistant.semester_experiments": "Semester Experiments", } def query_modifier(self, query): return query.where(User.id == current_user.id) def on_model_change(self, form, model, is_created): if form.generate_new_password.data: password = randomPassword() flashRandomPassword(password) admin_change_password(model, password, notify=False) # Password is automatically hashed with this function class AssistantDocsView(SecureAssistantBaseView): @expose("/") def index(self): return self.render("docs/docs.html", role="assistant") assistantSpace.add_view( AssistantGroupExperimentView( GroupExperiment, db.session, endpoint="assistant_groupexperiment", url="group_experiment" ) ) assistantSpace.add_view(AssistantUserView(User, db.session, endpoint="assistant_user", url="user")) assistantSpace.add_view(AssistantDocsView(name="Docs", endpoint="assistant_docs", url="docs")) initActiveSemesterMenuLinks(assistantSpace)