from flask import url_for, flash, request from flask_admin.menu import MenuLink from flask_admin.contrib.sqla.filters import BaseSQLAFilter from flask_security import hash_password from wtforms import BooleanField, SelectField, TextField from wtforms.validators import DataRequired, Email from advlabdb import admin, app, user_datastore, db from advlabdb.utils import randomPassword, userActiveSemester, partFromLabelInUserActiveSemester, setActiveSemester from advlabdb.customClasses import SecureModelView from advlabdb.models import User, Role, Semester, Part, Student, PartStudent, Group, GroupExperiment, Experiment, Assistant, Appointment, PartExperiment, ExperimentMark from advlabdb.configUtils import getConfig class UserModelView(SecureModelView): column_list = ["email", "active", "roles", "assistant"] column_searchable_list = ["email"] column_filters = ["active"] form_columns = ["email", "active", "roles"] form_args = { "email": {"validators": [Email()]}, "active": {"default": True}, "roles": {"validators": [DataRequired(message="A role is required!")]} } def create_model(self, form): password = randomPassword() passwordHash = hash_password(password) email = form.email.data.lower() roles = [role.name for role in form.roles.data] if "admin" in roles: flash("You have registered a new admin!", "danger") model = user_datastore.create_user(email=email, password=passwordHash, roles=roles) db.session.commit() flash(f"{email} registered with roles: {', '.join([role.name for role in form.roles.data])}.", category="success") flash(f"Random password: {password}", category="warning") return model class RoleModelView(SecureModelView): column_exclude_list = ["update_datetime"] class SemesterModelView(SecureModelView): column_list = ["label", "parts"] form_columns = ["label", "create_parts"] form_extra_fields = { "create_parts": BooleanField("Create parts:" + ", ".join(getConfig("partsLabels")) + ".", default=True) } def after_model_change(self, form, model, is_created): if form.create_parts.data: if is_created == False and model.parts != []: flash("This semester already has parts!", "danger") return for partLabel in getConfig("partsLabels"): db.session.add(Part(label=partLabel, semester=model)) db.session.commit() if is_created: admin.add_link(MenuLink(name=model.label, url=url_for("set_semester") + "?semester_id=" + str(model.id), category="Active semester")) setActiveSemester(model.id) class PartModelView(SecureModelView): can_view_details = True column_details_list = ["label", "semester", "part_experiments", "part_students", "groups"] form_columns = ["label", "semester"] partsLabels = getConfig("partsLabels") form_choices = {"label": list(zip(partsLabels, partsLabels))} class StudentModelView(SecureModelView): can_view_details = True column_list = ["student_number", "first_name", "last_name", "uni_email", "contact_email", "part_students"] column_details_list = column_list + ["bachelor_thesis", "bachelor_thesis_work_group", "note"] column_searchable_list = ["student_number", "uni_email", "contact_email", "first_name", "last_name"] form_columns = column_details_list + ["new_part_student_part", "new_part_student_group_number"] form_args = { "uni_email": {"validators": [Email()]}, "contact_email": {"validators": [Email()]}, } partChoices = ["-"] + getConfig("partsLabels") form_extra_fields = { "new_part_student_part": SelectField("Part", choices=list(zip(partChoices, partChoices)), default=partChoices[0]), "new_part_student_group_number": TextField("Group number") } def validate_form(self, form): if request.method == "POST": partLabel = form.new_part_student_part.data groupNumber = form.new_part_student_group_number.data if (partLabel != self.partChoices[0] and groupNumber == "") or (partLabel == self.partChoices[0] and groupNumber != ""): flash("You have to assign both part and group if you want to add a part student!", "danger") return False if partLabel != self.partChoices[0] and not partFromLabelInUserActiveSemester(partLabel): flash(f"Part {partLabel} is not created in {str(userActiveSemester())} yet!", "danger") return False if groupNumber != "": message = "The group number has to be an integer > 0 !" try: groupNumber = int(groupNumber) except: flash(message, "danger") return False if groupNumber < 1: flash(message, "danger") return False return super().validate_form(form) def after_model_change(self, form, model, is_created): partLabel = form.new_part_student_part.data print("\nLL\n") if partLabel != self.partChoices[0]: groupNumber = int(form.new_part_student_group_number.data) part = partFromLabelInUserActiveSemester(partLabel) group = Group.query.filter(Group.number == groupNumber, Group.part == part).first() if group is None: group = Group(number=groupNumber, part=part) db.session.add(group) flash(f"Added the new group with number {str(groupNumber)} in part {str(part)}.", "success") partStudent = PartStudent(student=model, part=part, group=group) db.session.add(partStudent) db.session.commit() flash("Added part student.", "success") class PartFilter(BaseSQLAFilter): def apply(self, query, value, alias=None): return query.filter(self.column == partFromLabelInUserActiveSemester(value).id) def operation(self): return "equals" def validate(self, value): if partFromLabelInUserActiveSemester(value): return True else: flash(f"Part {value} not found in your active semester {userActiveSemester()}!", "danger") return False class PartStudentModelView(SecureModelView): partsLabels = getConfig("partsLabels") column_filters = [PartFilter(PartStudent.part_id, "Part", options=list(zip(partsLabels, partsLabels)))] form_excluded_columns = ["experiment_marks"] admin.add_view(StudentModelView(Student, db.session)) admin.add_view(PartStudentModelView(PartStudent, db.session)) admin.add_view(SecureModelView(Group, db.session)) admin.add_view(SecureModelView(GroupExperiment, db.session)) admin.add_view(SecureModelView(Experiment, db.session)) admin.add_view(SecureModelView(PartExperiment, db.session)) admin.add_view(SecureModelView(Assistant, db.session)) admin.add_view(SecureModelView(Appointment, db.session)) admin.add_view(PartModelView(Part, db.session)) admin.add_view(SemesterModelView(Semester, db.session)) admin.add_view(SecureModelView(ExperimentMark, db.session)) admin.add_view(UserModelView(User, db.session)) admin.add_view(RoleModelView(Role, db.session)) with app.app_context(): semesters = Semester.query.all()[::-1] for semester in semesters: admin.add_link(MenuLink(name=semester.label, url=url_for("set_semester") + "?semester_id=" + str(semester.id), category="Active semester"))