from flask import flash from flask_admin import expose from flask_admin.contrib.sqla.fields import QuerySelectField, QuerySelectMultipleField from flask_security import admin_change_password, current_user from sqlalchemy import and_ from wtforms import BooleanField, Form, RadioField, SelectField, StringField from wtforms.fields import DateField from wtforms.validators import NumberRange, Optional from . import assistantSpace, db from .advlabdb_independent_funs import flashRandomPassword, randomPassword from .customClasses import SecureAssistantBaseView, SecureAssistantModelView from .exceptions import DataBaseException, ModelViewException from .model_dependent_funs import initActiveSemesterMenuLinks from .models import ( MAX_MARK, MIN_MARK, Appointment, Assistant, Experiment, ExperimentMark, Group, GroupExperiment, Part, PartStudent, Program, Role, Semester, SemesterExperiment, Student, User, ) class AssistantAppointmentView(SecureAssistantModelView): can_edit = True column_list = [ "date", "special", "group_experiment.semester_experiment.experiment", "group_experiment.group", "group_experiment.group.part_students", ] column_labels = { "group_experiment.semester_experiment.experiment": "Experiment", "group_experiment.group": "Group", "group_experiment.group.part_students": "Students", } column_editable_list = [ "date", ] form_columns = column_editable_list def query_modifier(self, query): return ( query.join(GroupExperiment) .join(SemesterExperiment) .where( SemesterExperiment.semester == current_user.active_semester, Appointment.assistant == current_user.assistant, ) ) class AssistantExperimentMarkView(SecureAssistantModelView): can_edit = True column_list = [ "oral_mark", "protocol_mark", "final_experiment_mark", "group_experiment.semester_experiment.experiment", "part_student.student.first_name", "part_student.student.last_name", "part_student.group", "part_student.student.uni_email", "part_student.student.contact_email", "part_student.part", "assistant", "admin", ] column_labels = { "group_experiment.semester_experiment.experiment": "Experiment", "part_student.student.first_name": "First Name", "part_student.student.last_name": "Last Name", "part_student.group": "Group", "part_student.student.uni_email": "Uni Email", "part_student.student.contact_email": "Contact Email", "part_student.part": "Part", } column_descriptions = { "oral_mark": f"Between {MIN_MARK} and {MAX_MARK}", "protocol_mark": f"Between {MIN_MARK} and {MAX_MARK}", "final_experiment_mark": "Calculated automatically with oral and protocol marks and weightings", "part_student.student.contact_email": "The preferred contact email address if entered by the student", "assistant": "The last assistant who edited the mark", "admin": "The last admin who edited the mark", } column_editable_list = [ "oral_mark", "protocol_mark", ] form_columns = column_editable_list form_args = { "oral_mark": {"validators": [NumberRange(MIN_MARK, MAX_MARK)]}, "protocol_mark": {"validators": [NumberRange(MIN_MARK, MAX_MARK)]}, } column_default_sort = [("oral_mark", False), ("protocol_mark", False)] def query_modifier(self, query): return ( query.join(GroupExperiment) .join(SemesterExperiment) .where(SemesterExperiment.semester == current_user.active_semester) .join(SemesterExperiment.assistants) .where(Assistant.user == current_user) ) def update_model(self, form, model): if (form.oral_mark is not None and form.oral_mark.data != model.oral_mark) or ( form.protocol_mark is not None and form.protocol_mark.data != model.protocol_mark ): model.assistant = current_user.assistant updateSuccessful = super().update_model(form, model) model.part_student.checkThenSetFinalPartMark() return updateSuccessful else: # Nothing changed return True class AssistantUserView(SecureAssistantModelView): class EditForm(Form): phone_number = StringField( "Phone Number", validators=[Optional()], ) mobile_phone_number = StringField( "Mobile Phone Number", validators=[Optional()], ) building = StringField( "Building", validators=[Optional()], ) room = StringField( "Room", validators=[Optional()], ) generate_new_password = BooleanField( "Generate new random password. For security reasons, it is not possible to manually enter a password. Please use a password manager like Bitwarden or KeepassXC to save the randomly generated password.", default=False, ) can_edit = 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/assistant.html") assistantSpace.add_view( AssistantAppointmentView(Appointment, db.session, endpoint="assistant_appointment", url="appointment") ) assistantSpace.add_view( AssistantExperimentMarkView(ExperimentMark, db.session, endpoint="assistant_experimentmark", url="experimentmark") ) 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)