diff --git a/DB.drawio b/DB.drawio index f61c87b..f84c1d6 100644 --- a/DB.drawio +++ b/DB.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/README.md b/README.md index 86221ab..3f64bbd 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ This URL leads to the home page where you can login with this testing admin acco # To-Do: - Rest of admin model views - - Validators - Experiments history for students - Assistants space +- Homepage text changeable - Email integration? - 2FA? - Students code for getting information? diff --git a/advlabdb/customClasses.py b/advlabdb/customClasses.py index d30f235..1201834 100644 --- a/advlabdb/customClasses.py +++ b/advlabdb/customClasses.py @@ -16,6 +16,10 @@ class SecureModelView(ModelView): can_export = True can_set_page_size = True + create_modal = True + edit_modal = True + details_modal = True + list_template = "admin_list.html" create_template = "admin_create.html" edit_template = "admin_edit.html" diff --git a/advlabdb/modelViews.py b/advlabdb/modelViews.py index 4ed84b8..ecf2f14 100644 --- a/advlabdb/modelViews.py +++ b/advlabdb/modelViews.py @@ -7,6 +7,7 @@ from wtforms import Form, BooleanField, SelectField, TextField, RadioField, Floa 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 wtforms.fields.html5 import DateField from advlabdb import admin, app, db, user_datastore from advlabdb.configUtils import getConfig @@ -39,6 +40,7 @@ class UserView(SecureModelView): column_searchable_list = ["email"] column_filters = ["active"] form_columns = ["email", "active", "roles"] + column_editable_list = ["active"] form_args = { "email": {"validators": [Email()]}, @@ -208,14 +210,15 @@ def partQueryFactory(): return Part.query.filter(Part.id.in_([part.id for part in userActiveSemester().parts])) +def groupQueryFactory(): + return Group.query.filter(Group.part_id.in_([part.id for part in userActiveSemester().parts])) + + class PartStudentView(SecureModelView): class CreateForm(Form): def studentQueryFactory(): return Student.query - 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="-" ) @@ -266,9 +269,9 @@ class GroupView(SecureModelView): return PartStudent.query.filter(PartStudent.part_id.in_([part.id for part in userActiveSemester().parts])) part = QuerySelectField( - label="Part", query_factory=partQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-" + "Part", query_factory=partQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-" ) - part_students = QuerySelectMultipleField(label="Part Students", query_factory=partStudentsQueryFactory) + part_students = QuerySelectMultipleField("Part Students", query_factory=partStudentsQueryFactory) class EditForm(CreateForm): part = None @@ -276,8 +279,7 @@ class GroupView(SecureModelView): form = EditForm column_list = ["number", "part", "part_students"] - column_filters = ["part"] - column_searchable_list = ["number"] + column_filters = ["number", "part"] partStudentPartPartMismatchException = "Part and StudentParts part don't match!" @@ -355,13 +357,65 @@ class PartExperimentView(SecureModelView): class AssistantView(SecureModelView): can_view_details = True column_list = ["first_name", "last_name", "email", "user", "part_experiments"] - column_details_list = column_list + ["phone_number", "mobile_phone_number", "room", "building", "experiment_marks"] + column_details_list = column_list + [ + "phone_number", + "mobile_phone_number", + "room", + "building", + "appointments", + "experiment_marks", + ] column_filters = ["user.active"] form_excluded_columns = ["experiment_marks"] class GroupExperimentView(SecureModelView): + class CreateForm(Form): + def partExperimentQueryFactory(): + return PartExperiment.query.filter( + PartExperiment.part_id.in_([part.id for part in userActiveSemester().parts]) + ) + + def assistantQueryFactory(): + return Assistant.query.filter( + Assistant.user_id.in_([user.id for user in User.query.filter(User.active == True)]) + ) + + group = QuerySelectField( + "Group", query_factory=groupQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-" + ) + part_experiment = QuerySelectField( + "Part Experiment", + query_factory=partExperimentQueryFactory, + validators=[DataRequired()], + allow_blank=True, + blank_text="-", + ) + + assistantBlankText = "Auto assign if experiment has only one assistant" + + appointment1 = DateField("Appointment 1") + appointment1_special = BooleanField("Appointment 1 special", default=False) + appointment1_assistant = QuerySelectField( + "Appointment 1 Assistant", + query_factory=assistantQueryFactory, + allow_blank=True, + blank_text=assistantBlankText, + ) + + appointment2 = DateField("Appointment 2") + appointment2_special = BooleanField("Appointment 2 special", default=False) + appointment2_assistant = QuerySelectField( + "Appointment 2 Assistant", + query_factory=assistantQueryFactory, + allow_blank=True, + blank_text=assistantBlankText, + ) + + form = CreateForm + column_list = ["group", "part_experiment", "appointments", "experiment_marks"] + column_filters = ["group", "part_experiment.experiment", "appointments"] def get_query(self): return ( @@ -392,6 +446,10 @@ class GroupExperimentView(SecureModelView): ) +class AppointmentView(SecureModelView): + column_list = ["date", "special", "group_experiment", "assistant"] + + admin.add_view(StudentView(Student, db.session)) admin.add_view(PartStudentView(PartStudent, db.session)) admin.add_view(GroupView(Group, db.session)) @@ -399,7 +457,7 @@ admin.add_view(GroupExperimentView(GroupExperiment, db.session)) admin.add_view(ExperimentView(Experiment, db.session)) admin.add_view(PartExperimentView(PartExperiment, db.session)) admin.add_view(AssistantView(Assistant, db.session)) -admin.add_view(SecureModelView(Appointment, db.session)) +admin.add_view(AppointmentView(Appointment, db.session)) admin.add_view(PartView(Part, db.session)) admin.add_view(SemesterView(Semester, db.session)) admin.add_view(SecureModelView(ExperimentMark, db.session))