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:
parent
5aa408df89
commit
d9eaafeb6d
1 changed files with 110 additions and 106 deletions
|
@ -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 = (
|
|
||||||
Group.query.filter(Group.part == form.part.data).order_by(Group.number)[-1].number
|
|
||||||
)
|
)
|
||||||
flash(
|
part_students = QuerySelectMultipleField(label="Part Students", query_factory=partStudentsQueryFactory)
|
||||||
f"Group number taken in this part! Last group number taken in this part is {lastTakenGroupNumber}.",
|
|
||||||
"warning",
|
class EditForm(CreateForm):
|
||||||
|
part = None
|
||||||
|
|
||||||
|
form = EditForm
|
||||||
|
|
||||||
|
column_list = ["number", "part", "part_students"]
|
||||||
|
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,
|
||||||
)
|
)
|
||||||
return False
|
|
||||||
return super().validate_form(form)
|
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]))
|
||||||
|
|
Loading…
Reference in a new issue