diff --git a/DB.drawio b/DB.drawio index f84c1d6..472e018 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/advlabdb/modelViews.py b/advlabdb/modelViews.py index ecf2f14..072772d 100644 --- a/advlabdb/modelViews.py +++ b/advlabdb/modelViews.py @@ -20,7 +20,7 @@ from advlabdb.models import ( Group, GroupExperiment, Part, - PartExperiment, + SemesterExperiment, PartStudent, Role, Semester, @@ -106,7 +106,7 @@ class SemesterView(SecureModelView): can_edit = False column_list = ["label", "parts"] - form_columns = ["semester_label", "year", "create_parts", "transfer_assistants"] + form_columns = ["semester_label", "year", "transfer_parts", "transfer_semester_experiments", "transfer_assistants"] semesterLabels = ["WS", "SS"] form_extra_fields = { @@ -114,13 +114,14 @@ class SemesterView(SecureModelView): "Semester", choices=list(zip(semesterLabels, semesterLabels)), validators=[DataRequired()] ), "year": TextField("Year", validators=[DataRequired()]), - "create_parts": BooleanField( - "Create parts:" - + ", ".join(getConfig("partLabels")) - + " and transfer part experiments from your current active semester:", + "transfer_parts": BooleanField( + "Transfer parts from your current semester", default=True, ), - "transfer_assistants": BooleanField("Transfer Assistants from your current active semester:", default=False), + "transfer_semester_experiments": BooleanField( + "Transfer Semester Experiemnts from your current semester", default=True + ), + "transfer_assistants": BooleanField("Transfer Assistants from your current active semester", default=True), } def create_model(self, form): @@ -147,22 +148,22 @@ class SemesterView(SecureModelView): ) ) - oldSemesterParts = userActiveSemester().parts + oldSemester = userActiveSemester() setUserActiveSemester(model.id) - if form.create_parts.data: - model.createParts() + if form.transfer_parts.data: + model.transferParts(oldSemester) + if form.transfer_semester_experiments.data: try: - for part in oldSemesterParts: - for partExperiment in part.part_experiments: - newPartExperiment = PartExperiment( - experiment=partExperiment.experiment, part=partFromLabelInUserActiveSemester(part.label) - ) - if form.transfer_assistants.data: - newPartExperiment.assistants = partExperiment.assistants - self.session.add(newPartExperiment) + for semesterExperiment in oldSemester.semester_experiments: + newSemesterExperiment = SemesterExperiment( + experiment=semesterExperiment.experiment, semester=userActiveSemester() + ) + if form.transfer_assistants.data: + newSemesterExperiment.assistants = semesterExperiment.assistants + self.session.add(newSemesterExperiment) self.session.commit() except Exception as ex: flash(ex, "error") @@ -172,12 +173,9 @@ class SemesterView(SecureModelView): class PartView(SecureModelView): can_view_details = True - column_details_list = ["label", "semester", "part_experiments", "part_students", "groups"] + column_details_list = ["label", "semester", "part_students", "groups"] form_columns = ["label", "semester"] - partLabels = getConfig("partLabels") - form_choices = {"label": list(zip(partLabels, partLabels))} - def get_query(self): return super().get_query().filter(Part.id.in_([part.id for part in userActiveSemester().parts])) @@ -337,26 +335,23 @@ class ExperimentView(SecureModelView): column_list = ["number", "name", "deprecated"] -class PartExperimentView(SecureModelView): - column_list = ["experiment", "part", "assistants"] - - partLabels = getConfig("partLabels") - column_filters = ["part"] +class SemesterExperimentView(SecureModelView): + column_list = ["experiment", "semester", "assistants"] 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(SemesterExperiment.semester == userActiveSemester()) def get_count_query(self): return ( self.session.query(func.count("*")) .select_from(self.model) - .filter(PartExperiment.part_id.in_([part.id for part in userActiveSemester().parts])) + .filter(SemesterExperiment.semester == userActiveSemester()) ) class AssistantView(SecureModelView): can_view_details = True - column_list = ["first_name", "last_name", "email", "user", "part_experiments"] + column_list = ["first_name", "last_name", "email", "user", "semester_experiments"] column_details_list = column_list + [ "phone_number", "mobile_phone_number", @@ -371,10 +366,8 @@ class AssistantView(SecureModelView): class GroupExperimentView(SecureModelView): class CreateForm(Form): - def partExperimentQueryFactory(): - return PartExperiment.query.filter( - PartExperiment.part_id.in_([part.id for part in userActiveSemester().parts]) - ) + def semesterExperimentQueryFactory(): + return SemesterExperiment.query.filter(SemesterExperiment.semester == userActiveSemester()) def assistantQueryFactory(): return Assistant.query.filter( @@ -384,9 +377,9 @@ class GroupExperimentView(SecureModelView): group = QuerySelectField( "Group", query_factory=groupQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-" ) - part_experiment = QuerySelectField( - "Part Experiment", - query_factory=partExperimentQueryFactory, + semester_experiment = QuerySelectField( + "Semester Experiment", + query_factory=semesterExperimentQueryFactory, validators=[DataRequired()], allow_blank=True, blank_text="-", @@ -414,8 +407,8 @@ class GroupExperimentView(SecureModelView): form = CreateForm - column_list = ["group", "part_experiment", "appointments", "experiment_marks"] - column_filters = ["group", "part_experiment.experiment", "appointments"] + column_list = ["group", "semester_experiment", "appointments", "experiment_marks"] + column_filters = ["group", "semester_experiment.experiment", "appointments"] def get_query(self): return ( @@ -455,7 +448,7 @@ admin.add_view(PartStudentView(PartStudent, db.session)) admin.add_view(GroupView(Group, db.session)) admin.add_view(GroupExperimentView(GroupExperiment, db.session)) admin.add_view(ExperimentView(Experiment, db.session)) -admin.add_view(PartExperimentView(PartExperiment, db.session)) +admin.add_view(SemesterExperimentView(SemesterExperiment, db.session)) admin.add_view(AssistantView(Assistant, db.session)) admin.add_view(AppointmentView(Appointment, db.session)) admin.add_view(PartView(Part, db.session)) @@ -465,7 +458,7 @@ admin.add_view(UserView(User, db.session)) admin.add_view(RoleView(Role, db.session)) with app.app_context(): - semesters = Semester.query.all()[::-1] + semesters = Semester.query.order_by(Semester.id) for semester in semesters: admin.add_link( MenuLink( diff --git a/advlabdb/models.py b/advlabdb/models.py index fb1d347..9c66913 100644 --- a/advlabdb/models.py +++ b/advlabdb/models.py @@ -58,7 +58,7 @@ class Group(db.Model): class GroupExperiment(db.Model): # An experiment specified to a group id = db.Column(db.Integer, primary_key=True) - part_experiment_id = db.Column(db.Integer, db.ForeignKey("part_experiment.id"), nullable=False) + semester_experiment_id = db.Column(db.Integer, db.ForeignKey("semester_experiment.id"), nullable=False) group_id = db.Column(db.Integer, db.ForeignKey("group.id"), nullable=False) appointments = db.relationship("Appointment", backref="group_experiment", lazy=True) experiment_marks = db.relationship("ExperimentMark", backref="group_experiment", lazy=True) @@ -78,29 +78,29 @@ class Experiment(db.Model): oral_weighting = db.Column(db.Float, nullable=False) protocol_weighting = db.Column(db.Float, nullable=False) final_weighting = db.Column(db.Float, nullable=False) - part_experiments = db.relationship("PartExperiment", backref="experiment", lazy=True) + semester_experiments = db.relationship("SemesterExperiment", backref="experiment", lazy=True) def __repr__(self): return f"" -# Helper table for the many to many relationship between Assistant and PartExperiment +# Helper table for the many to many relationship between Assistant and SemesterExperiment experiment_assistant = db.Table( "experiment_assistant", - db.Column("part_experiment_id", db.Integer, db.ForeignKey("part_experiment.id"), primary_key=True), + db.Column("semester_experiment_id", db.Integer, db.ForeignKey("semester_experiment.id"), primary_key=True), db.Column("assistant_id", db.Integer, db.ForeignKey("assistant.id"), primary_key=True), ) -class PartExperiment(db.Model): +class SemesterExperiment(db.Model): # An experiment in a specific part id = db.Column(db.Integer, primary_key=True) experiment_id = db.Column(db.Integer, db.ForeignKey("experiment.id"), nullable=False) - part_id = db.Column(db.Integer, db.ForeignKey("part.id"), nullable=False) + semester_id = db.Column(db.Integer, db.ForeignKey("semester.id"), nullable=False) assistants = db.relationship( - "Assistant", secondary=experiment_assistant, lazy=True, backref=db.backref("part_experiments", lazy=True) + "Assistant", secondary=experiment_assistant, lazy=True, backref=db.backref("semester_experiments", lazy=True) ) - group_experiments = db.relationship("GroupExperiment", backref="part_experiment", lazy=True) + group_experiments = db.relationship("GroupExperiment", backref="semester_experiment", lazy=True) class Assistant(db.Model): @@ -132,7 +132,6 @@ class Part(db.Model): id = db.Column(db.Integer, primary_key=True) label = db.Column(db.String(100), nullable=False) # A/1, A/2, B/1, B/2 semester_id = db.Column(db.Integer, db.ForeignKey("semester.id"), nullable=False) - part_experiments = db.relationship("PartExperiment", backref="part", lazy=True) part_students = db.relationship("PartStudent", backref="part", lazy=True) groups = db.relationship("Group", backref="part", lazy=True) @@ -144,14 +143,21 @@ class Semester(db.Model): id = db.Column(db.Integer, primary_key=True) label = db.Column(db.String(100), nullable=False, unique=True) # WS2122 for example parts = db.relationship("Part", backref="semester", lazy=True) + semester_experiments = db.relationship("SemesterExperiment", backref="semester", lazy=True) def __repr__(self): return f"<{self.label}>" - def createParts(self): + def transferParts(self, oldSemester): try: - for partLabel in getConfig("partLabels"): + if oldSemester: + partLabels = [part.label for part in oldSemester.parts] + else: + partLabels = getConfig("defaultPartLabels") + + for partLabel in partLabels: db.session.add(Part(label=partLabel, semester=self)) + db.session.commit() except Exception as ex: flash(ex, "error") diff --git a/config.json b/config.json index 2848710..a1f6b90 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "partLabels": [ + "defaultPartLabels": [ "A/1", "A/2", "A/m", diff --git a/testDB.py b/testDB.py index 2396553..2f8a295 100644 --- a/testDB.py +++ b/testDB.py @@ -15,8 +15,8 @@ with app.app_context(): db.session.add(sem1) db.session.add(sem2) - sem1.createParts() - sem2.createParts() + sem1.transferParts(None) + sem2.transferParts(None) parta1 = sem2.parts[0] partb2 = sem2.parts[1] @@ -70,14 +70,14 @@ with app.app_context(): db.session.add(ex1) db.session.add(ex2) - px1 = PartExperiment(experiment=ex1, part=parta1) - px2 = PartExperiment(experiment=ex2, part=partb2) + sx1 = SemesterExperiment(experiment=ex1, semester=sem2) + sx2 = SemesterExperiment(experiment=ex2, semester=sem2) - db.session.add(px1) - db.session.add(px2) + db.session.add(sx1) + db.session.add(sx2) - gx1 = GroupExperiment(part_experiment=px1, group=g1) - gx2 = GroupExperiment(part_experiment=px2, group=g2) + gx1 = GroupExperiment(semester_experiment=sx1, group=g1) + gx2 = GroupExperiment(semester_experiment=sx2, group=g2) db.session.add(gx1) db.session.add(gx2) @@ -100,8 +100,8 @@ with app.app_context(): ) as2 = Assistant(first_name="As2", last_name="l", email="test2@test.com", user=us2) - as1.part_experiments.append(px1) - as2.part_experiments.append(px2) + as1.semester_experiments.append(sx1) + as2.semester_experiments.append(sx2) db.session.add(as1) db.session.add(as2)