1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-09-19 18:31:16 +00:00
AdvLabDB/advlabdb/models.py

338 lines
13 KiB
Python
Raw Normal View History

2021-03-18 13:53:55 +00:00
"""
See the file DB.drawio for the design of the database. It can be opened in the internet browser with the website:
https://app.diagrams.net
For more information about the implementation, see the part to Models in the documentation of Flask-SQLAlchemy:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/
"""
2021-04-03 00:11:26 +00:00
# Imports
2021-07-01 17:43:59 +00:00
from flask import flash, has_request_context
2021-06-02 21:43:41 +00:00
from flask_security.models.fsqla_v2 import FsRoleMixin, FsUserMixin
2021-04-03 00:11:26 +00:00
2021-03-18 13:53:55 +00:00
# Importing the database instance
from advlabdb import db
2021-06-01 23:56:49 +00:00
from advlabdb.configUtils import getConfig
2021-03-18 13:53:55 +00:00
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
2021-07-11 12:27:00 +00:00
student_number = db.Column(db.Integer, db.CheckConstraint("student_number > -1"), nullable=False, unique=True)
2021-03-18 13:53:55 +00:00
first_name = db.Column(db.String(100), nullable=False)
last_name = db.Column(db.String(100), nullable=False)
uni_email = db.Column(db.String(200), nullable=False, unique=True)
contact_email = db.Column(db.String(200), unique=True)
2021-03-18 13:53:55 +00:00
bachelor_thesis = db.Column(db.String, nullable=True)
bachelor_thesis_work_group = db.Column(db.String, nullable=True)
note = db.Column(db.Text, nullable=True)
part_students = db.relationship("PartStudent", backref="student", lazy=True)
def repr(self):
return f"{self.first_name} {self.last_name}"
def __repr__(self):
return f"<STD {self.repr()}>"
2021-03-18 13:53:55 +00:00
class PartStudent(db.Model):
# A student doing a specific part
id = db.Column(db.Integer, primary_key=True)
2021-07-11 12:27:00 +00:00
final_part_mark = db.Column(
db.Integer,
db.CheckConstraint("final_part_mark > -1"),
db.CheckConstraint("final_part_mark < 16"),
nullable=True,
)
2021-03-18 13:53:55 +00:00
student_id = db.Column(db.Integer, db.ForeignKey("student.id"), nullable=False)
part_id = db.Column(db.Integer, db.ForeignKey("part.id"), nullable=False)
group_id = db.Column(db.Integer, db.ForeignKey("group.id"), nullable=True)
experiment_marks = db.relationship("ExperimentMark", backref="part_student", lazy=True)
2021-07-11 01:21:54 +00:00
__table_args__ = (
db.UniqueConstraint(student_id, part_id),
db.UniqueConstraint(student_id, group_id),
)
def repr(self):
return f"{self.student.repr()} {self.part.repr()}"
2021-06-30 18:21:44 +00:00
def __repr__(self):
return f"<PARTSTD {self.repr()}>"
2021-06-30 18:21:44 +00:00
2021-03-18 13:53:55 +00:00
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
2021-07-11 12:27:00 +00:00
number = db.Column(db.Integer, db.CheckConstraint("number > 0"), nullable=False)
part_id = db.Column(db.Integer, db.ForeignKey("part.id"), nullable=False)
2021-03-18 13:53:55 +00:00
part_students = db.relationship("PartStudent", backref="group", lazy=True)
group_experiments = db.relationship("GroupExperiment", backref="group", lazy=True)
2021-07-11 01:21:54 +00:00
__table_args__ = (db.UniqueConstraint(number, part_id),)
2021-07-12 12:48:29 +00:00
def customInit(part, partStudents):
orderedPartGroups = Group.query.filter(Group.part == part).order_by(Group.number)
lastTakenGroupNumber = orderedPartGroups[-1].number if orderedPartGroups.count() > 0 else 0
return Group(
number=lastTakenGroupNumber + 1,
part_students=partStudents,
part=part,
)
def repr(self):
return f"{self.number} {self.part.repr()}"
2021-04-24 11:38:03 +00:00
def __repr__(self):
return f"<GR {self.repr()}>"
2021-04-24 11:38:03 +00:00
2021-03-18 13:53:55 +00:00
class GroupExperiment(db.Model):
# An experiment specified to a group
id = db.Column(db.Integer, primary_key=True)
semester_experiment_id = db.Column(db.Integer, db.ForeignKey("semester_experiment.id"), nullable=False)
2021-03-18 13:53:55 +00:00
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)
2021-07-11 01:21:54 +00:00
__table_args__ = (db.UniqueConstraint(semester_experiment_id, group_id),)
2021-07-12 12:48:29 +00:00
def customInit(semester_experiment, group):
for partStudent in group.part_students:
student = partStudent.student
for partStudent in student.part_students:
for experimentMark in partStudent.experiment_marks:
2021-07-13 16:41:00 +00:00
if (
(experimentMark.oral_mark or experimentMark.protocol_mark)
and experimentMark.group_experiment.semester_experiment.experiment
== semester_experiment.experiment
):
raise Exception(
2021-07-11 01:21:54 +00:00
f"{student} has already done {semester_experiment.experiment} in {partStudent.part} and had {experimentMark}!"
)
2021-07-13 00:50:15 +00:00
groupExperiment = GroupExperiment(semester_experiment=semester_experiment, group=group)
for partStudent in group.part_students:
db.session.add(ExperimentMark(part_student=partStudent, group_experiment=groupExperiment))
return groupExperiment
2021-07-12 12:48:29 +00:00
def repr(self):
return f"SemExp {self.semester_experiment.repr()}; Gr {self.group.repr()}"
def __repr__(self):
return f"<GREXP {self.repr()}>"
2021-03-18 13:53:55 +00:00
class Experiment(db.Model):
id = db.Column(db.Integer, primary_key=True)
label = db.Column(db.String(20), nullable=False, unique=True)
title = db.Column(db.String(200), nullable=False)
2021-03-18 13:53:55 +00:00
description = db.Column(db.Text, nullable=True)
2021-06-21 16:07:18 +00:00
wiki_link = db.Column(db.String(200), nullable=True)
2021-03-18 13:53:55 +00:00
room = db.Column(db.String(100), nullable=False)
building = db.Column(db.String(100), nullable=False)
responsibility = db.Column(db.String(200), nullable=True)
2021-07-11 12:27:00 +00:00
duration_in_days = db.Column(db.Integer, db.CheckConstraint("duration_in_days > -1"), nullable=False)
2021-07-13 00:50:15 +00:00
active = db.Column(db.Boolean, default=True, nullable=False)
2021-07-13 15:22:15 +00:00
oral_weighting = db.Column(
db.Float, db.CheckConstraint("oral_weighting >= 0"), db.CheckConstraint("oral_weighting <= 1"), nullable=False
)
protocol_weighting = db.Column(
db.Float,
db.CheckConstraint("protocol_weighting >= 0"),
db.CheckConstraint("protocol_weighting <= 1"),
nullable=False,
)
final_weighting = db.Column(
db.Float, db.CheckConstraint("final_weighting >= 0"), db.CheckConstraint("final_weightin <= 1"), nullable=False
)
semester_experiments = db.relationship("SemesterExperiment", backref="experiment", lazy=True)
2021-03-18 13:53:55 +00:00
def repr(self):
return f"{self.label}"
def __repr__(self):
return f"<EXP {self.repr()}>"
2021-03-18 13:53:55 +00:00
# Helper table for the many to many relationship between Assistant and SemesterExperiment
2021-06-02 21:43:41 +00:00
experiment_assistant = db.Table(
"experiment_assistant",
db.Column("semester_experiment_id", db.Integer, db.ForeignKey("semester_experiment.id"), primary_key=True),
2021-06-02 21:43:41 +00:00
db.Column("assistant_id", db.Integer, db.ForeignKey("assistant.id"), primary_key=True),
)
2021-03-18 13:53:55 +00:00
class SemesterExperiment(db.Model):
2021-07-11 01:21:54 +00:00
# An experiment in a specific semester
2021-03-18 13:53:55 +00:00
id = db.Column(db.Integer, primary_key=True)
experiment_id = db.Column(db.Integer, db.ForeignKey("experiment.id"), nullable=False)
semester_id = db.Column(db.Integer, db.ForeignKey("semester.id"), nullable=False)
2021-06-02 21:43:41 +00:00
assistants = db.relationship(
"Assistant", secondary=experiment_assistant, lazy=True, backref=db.backref("semester_experiments", lazy=True)
2021-06-02 21:43:41 +00:00
)
group_experiments = db.relationship("GroupExperiment", backref="semester_experiment", lazy=True)
2021-03-18 13:53:55 +00:00
2021-07-11 01:21:54 +00:00
__table_args__ = (db.UniqueConstraint(experiment_id, semester_id),)
def repr(self):
return f"{self.experiment.repr()} {self.semester.repr()}"
def __repr__(self):
return f"<SEMEXP {self.repr()}>"
2021-03-18 13:53:55 +00:00
class Assistant(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(100), nullable=False)
last_name = db.Column(db.String(100), nullable=False)
phone_number = db.Column(db.String(50), nullable=True)
mobile_phone_number = db.Column(db.String(50), nullable=True)
room = db.Column(db.String(100), nullable=True)
building = db.Column(db.String(100), nullable=True)
2021-07-11 01:21:54 +00:00
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False, unique=True)
2021-03-18 13:53:55 +00:00
appointments = db.relationship("Appointment", backref="assistant", lazy=True)
experiment_marks = db.relationship("ExperimentMark", backref="assistant", lazy=True)
def repr(self):
return f"{self.first_name} {self.last_name}"
def __repr__(self):
return f"<ASST: {self.repr()}>"
2021-03-18 13:53:55 +00:00
class Appointment(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.Date, nullable=False) # To be specified with the python package "datetime"
2021-07-13 00:50:15 +00:00
special = db.Column(db.Boolean, default=False, nullable=False) # In the break or not
2021-03-18 13:53:55 +00:00
group_experiment_id = db.Column(db.Integer, db.ForeignKey("group_experiment.id"), nullable=False)
assistant_id = db.Column(db.Integer, db.ForeignKey("assistant.id"), nullable=False)
2021-07-12 14:42:11 +00:00
def checkAndGetAssistant(assistant, groupExperiment):
2021-07-12 12:48:29 +00:00
semesterExperiment = groupExperiment.semester_experiment
semesterExperimentAssistants = semesterExperiment.assistants
if not semesterExperimentAssistants:
raise Exception(f"{semesterExperiment} has no assistants yet!")
if assistant:
if assistant not in semesterExperimentAssistants:
raise Exception(f"{assistant} not responsible for {semesterExperiment}!")
else:
if len(semesterExperimentAssistants) == 1:
assistant = semesterExperimentAssistants[0]
else:
raise Exception(
f"Experiment {semesterExperiment} has more than one assistant. You have to assign one of these assistants: {semesterExperimentAssistants}"
)
2021-07-12 14:42:11 +00:00
return assistant
def customInit(date, special, assistant, groupExperiment):
assistant = Appointment.checkAndGetAssistant(assistant, groupExperiment)
2021-07-12 12:48:29 +00:00
return Appointment(
date=date,
special=special,
group_experiment=groupExperiment,
assistant=assistant,
)
2021-07-12 14:42:11 +00:00
def customUpdate(self, date, special, assistant, groupExperiment):
assistant = Appointment.checkAndGetAssistant(assistant, groupExperiment)
self.date = date
self.special = special
self.assistant = assistant
self.group_experiment = groupExperiment
def repr(self):
return f"{self.date} {self.group_experiment.repr()}"
def __repr__(self):
return f"<APPT {self.repr()}>"
2021-03-18 13:53:55 +00:00
class Part(db.Model):
id = db.Column(db.Integer, primary_key=True)
2021-06-02 21:43:41 +00:00
label = db.Column(db.String(100), nullable=False) # A/1, A/2, B/1, B/2
2021-03-18 13:53:55 +00:00
semester_id = db.Column(db.Integer, db.ForeignKey("semester.id"), nullable=False)
part_students = db.relationship("PartStudent", backref="part", lazy=True)
groups = db.relationship("Group", backref="part", lazy=True)
2021-03-18 13:53:55 +00:00
2021-07-11 01:21:54 +00:00
__table_args__ = (db.UniqueConstraint(label, semester_id),)
def repr(self):
return f"{self.label} {self.semester.repr()}"
2021-04-24 11:38:03 +00:00
def __repr__(self):
return f"<PART {self.repr()}>"
2021-04-24 11:38:03 +00:00
2021-03-18 13:53:55 +00:00
class Semester(db.Model):
id = db.Column(db.Integer, primary_key=True)
2021-06-02 21:43:41 +00:00
label = db.Column(db.String(100), nullable=False, unique=True) # WS2122 for example
2021-03-18 13:53:55 +00:00
parts = db.relationship("Part", backref="semester", lazy=True)
semester_experiments = db.relationship("SemesterExperiment", backref="semester", lazy=True)
2021-07-11 12:27:00 +00:00
active_users = db.relationship("User", backref="active_semester", lazy=True)
2021-03-18 13:53:55 +00:00
def repr(self):
return f"{self.label}"
def __repr__(self):
return f"<SEM {self.repr()}>"
2021-07-01 17:43:59 +00:00
def transferPartsFrom(self, oldSemester):
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))
2021-06-01 23:56:49 +00:00
2021-03-18 13:53:55 +00:00
class ExperimentMark(db.Model):
# A mark for a student after a specific experiment
id = db.Column(db.Integer, primary_key=True)
2021-07-11 12:27:00 +00:00
oral_mark = db.Column(
db.Integer, db.CheckConstraint("oral_mark > -1"), db.CheckConstraint("oral_mark < 16"), nullable=True
)
protocol_mark = db.Column(
db.Integer, db.CheckConstraint("protocol_mark > -1"), db.CheckConstraint("protocol_mark < 16"), nullable=True
)
2021-07-13 00:50:15 +00:00
edited_by_admin = db.Column(db.Boolean, default=False, nullable=True)
2021-03-18 13:53:55 +00:00
part_student_id = db.Column(db.Integer, db.ForeignKey("part_student.id"), nullable=False)
group_experiment_id = db.Column(db.Integer, db.ForeignKey("group_experiment.id"), nullable=False)
2021-06-02 21:43:41 +00:00
assistant_id = db.Column(
db.Integer, db.ForeignKey("assistant.id"), nullable=True
) # The assistant who gave the mark
2021-07-11 01:21:54 +00:00
__table_args__ = (db.UniqueConstraint(part_student_id, group_experiment_id),)
def repr(self):
2021-07-12 17:49:10 +00:00
return f"Oral {self.oral_mark}; Prot {self.protocol_mark}"
def __repr__(self):
return f"<EXPMARL {self.repr()}>"
2021-03-18 13:53:55 +00:00
2021-04-03 00:11:26 +00:00
class User(db.Model, FsUserMixin):
2021-03-18 13:53:55 +00:00
assistant = db.relationship("Assistant", backref="user", lazy=True, uselist=False)
2021-07-11 12:27:00 +00:00
active_semester_id = db.Column(db.Integer, db.ForeignKey("semester.id"), nullable=True)
2021-04-03 00:11:26 +00:00
def repr(self):
return f"{self.email}"
def __repr__(self):
return f"<USR {self.repr()}>"
2021-04-03 00:11:26 +00:00
class Role(db.Model, FsRoleMixin):
def repr(self):
return f"{self.name}"
def __repr__(self):
return f"<ROLE {self.repr()}>"