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)