1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-12-20 23:41:20 +00:00

Improved Students, PartStudents and Groups view

This commit is contained in:
Mo 2021-06-30 21:58:57 +02:00
parent 5aa408df89
commit d9eaafeb6d

View file

@ -3,8 +3,10 @@ from flask_admin.contrib.sqla.filters import BaseSQLAFilter
from flask_admin.menu import MenuLink from flask_admin.menu import MenuLink
from flask_security import current_user, hash_password from flask_security import current_user, hash_password
from sqlalchemy import func from sqlalchemy import func
from wtforms import BooleanField, SelectField, TextField, RadioField from wtforms import Form, BooleanField, SelectField, TextField, RadioField, FloatField
from wtforms.validators import DataRequired, Email from wtforms.validators import DataRequired, Email, Optional
from flask_admin.contrib.sqla.fields import QuerySelectMultipleField, QuerySelectField
from flask_admin.helpers import get_form_data
from advlabdb import admin, app, db, user_datastore from advlabdb import admin, app, db, user_datastore
from advlabdb.configUtils import getConfig from advlabdb.configUtils import getConfig
@ -59,6 +61,8 @@ class UserModelView(SecureModelView):
try: try:
model = user_datastore.create_user(email=email, password=passwordHash, roles=roles) model = user_datastore.create_user(email=email, password=passwordHash, roles=roles)
self.on_model_change(form, model, True)
self.session.commit() self.session.commit()
except Exception as ex: except Exception as ex:
flash(ex, "error") flash(ex, "error")
@ -120,7 +124,9 @@ class SemesterModelView(SecureModelView):
def create_model(self, form): def create_model(self, form):
try: try:
model = Semester(label=form.semester_label.data + form.year.data) model = Semester(label=form.semester_label.data + form.year.data)
self.session.add(model) self.session.add(model)
self.on_model_change(form, model, True)
self.session.commit() self.session.commit()
except Exception as ex: except Exception as ex:
flash(ex, "error") flash(ex, "error")
@ -183,108 +189,65 @@ class PartModelView(SecureModelView):
class StudentModelView(SecureModelView): class StudentModelView(SecureModelView):
can_view_details = True can_view_details = True
column_list = ["student_number", "first_name", "last_name", "uni_email", "contact_email", "part_students"] 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_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"] column_sortable_list = ["student_number", "first_name", "last_name"]
column_searchable_list = column_sortable_list + ["uni_email", "contact_email"]
form_excluded_columns = ["part_students"]
form_args = { form_args = {
"uni_email": {"validators": [Email()]}, "uni_email": {"validators": [Email()]},
"contact_email": {"validators": [Email()]}, "contact_email": {"validators": [Email()]},
} }
partChoices = ["-"] + getConfig("partLabels")
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): def partQueryFactory():
if request.method == "POST": return Part.query.filter(Part.id.in_([part.id for part in userActiveSemester().parts]))
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 Exception:
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
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:
try:
group = Group(number=groupNumber, part=part)
self.session.add(group)
self.session.commit()
except Exception as ex:
flash(ex, "error")
self.session.rollback()
return
else:
flash(f"Added the new group with number {str(groupNumber)} in part {str(part)}.", "success")
try:
partStudent = PartStudent(student=model, part=part, group=group)
self.session.add(partStudent)
self.session.commit()
except Exception as ex:
flash(ex, "error")
self.session.rollback()
else:
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): class PartStudentModelView(SecureModelView):
partLabels = getConfig("partLabels") class CreateForm(Form):
column_filters = [PartFilter(PartStudent.part_id, "Part", options=list(zip(partLabels, partLabels)))] def studentQueryFactory():
return Student.query
form_excluded_columns = ["experiment_marks"] def groupQueryFactory():
return Group.query.filter(Group.part_id.in_([part.id for part in userActiveSemester().parts]))
student = QuerySelectField(
"Student", query_factory=studentQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-"
)
part = QuerySelectField(
"Part", query_factory=partQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-"
)
group = QuerySelectField("Group", query_factory=groupQueryFactory, allow_blank=True, blank_text="-")
class EditForm(CreateForm):
student = None
part = None
final_part_mark = FloatField("Final Part Mark", validators=[Optional()])
form = EditForm
column_filters = ["part", "student", "group"]
partGroupPartMismatchException = "Part and groups part don't match!"
def create_form(self, obj=None):
form = self.CreateForm
return form(get_form_data(), obj=obj)
def on_model_change(self, form, model, is_created):
if model.group and model.part != model.group.part:
raise Exception(self.partGroupPartMismatchException)
def handle_view_exception(self, exc):
if exc.args[0] in (self.partGroupPartMismatchException):
pass
else:
return super().handle_view_exception(exc)
def get_query(self): def get_query(self):
return super().get_query().filter(PartStudent.part_id.in_([part.id for part in userActiveSemester().parts])) return super().get_query().filter(PartStudent.part_id.in_([part.id for part in userActiveSemester().parts]))
@ -298,21 +261,62 @@ class PartStudentModelView(SecureModelView):
class GroupModelView(SecureModelView): class GroupModelView(SecureModelView):
partLabels = getConfig("partLabels") class CreateForm(Form):
column_filters = [PartFilter(Group.part_id, "Part", options=list(zip(partLabels, partLabels)))] def partStudentsQueryFactory():
return PartStudent.query.filter(PartStudent.part_id.in_([part.id for part in userActiveSemester().parts]))
def validate_form(self, form): part = QuerySelectField(
if request.method == "POST": label="Part", query_factory=partQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-"
if Group.query.filter(Group.number == form.number.data, Group.part == form.part.data).first(): )
lastTakenGroupNumber = ( part_students = QuerySelectMultipleField(label="Part Students", query_factory=partStudentsQueryFactory)
Group.query.filter(Group.part == form.part.data).order_by(Group.number)[-1].number
) class EditForm(CreateForm):
flash( part = None
f"Group number taken in this part! Last group number taken in this part is {lastTakenGroupNumber}.",
"warning", form = EditForm
)
return False column_list = ["number", "part", "part_students"]
return super().validate_form(form) column_filters = ["part"]
column_searchable_list = ["number"]
partStudentPartPartMismatchException = "Part and StudentParts part don't match!"
def create_model(self, form):
try:
orderedPartGroups = Group.query.filter(Group.part == form.part.data).order_by(Group.number)
lastTakenGroupNumber = orderedPartGroups[-1].number if orderedPartGroups.count() > 0 else 0
model = Group(
number=lastTakenGroupNumber + 1,
part_students=form.part_students.data,
part=form.part.data,
)
self.session.add(model)
self.on_model_change(form, model, True)
self.session.commit()
except Exception as ex:
flash(ex, "error")
self.session.rollback()
else:
self.after_model_change(form, model, True)
return model
def on_model_change(self, form, model, is_created):
for partStudent in model.part_students:
if model.part != partStudent.part:
raise Exception(self.partStudentPartPartMismatchException)
def handle_view_exception(self, exc):
if exc.args[0] in (self.partStudentPartPartMismatchException):
pass
else:
return super().handle_view_exception(exc)
def create_form(self, obj=None):
form = self.CreateForm
return form(get_form_data(), obj=obj)
def get_query(self): def get_query(self):
return super().get_query().filter(Group.part_id.in_([part.id for part in userActiveSemester().parts])) return super().get_query().filter(Group.part_id.in_([part.id for part in userActiveSemester().parts]))
@ -335,7 +339,7 @@ class PartExperimentModelView(SecureModelView):
column_list = ["experiment", "part", "assistants"] column_list = ["experiment", "part", "assistants"]
partLabels = getConfig("partLabels") partLabels = getConfig("partLabels")
column_filters = [PartFilter(PartExperiment.part_id, "Part", options=list(zip(partLabels, partLabels)))] column_filters = ["part"]
def get_query(self): def get_query(self):
return super().get_query().filter(PartExperiment.part_id.in_([part.id for part in userActiveSemester().parts])) return super().get_query().filter(PartExperiment.part_id.in_([part.id for part in userActiveSemester().parts]))