mirror of
https://codeberg.org/Mo8it/AdvLabDB.git
synced 2024-12-20 23:41:20 +00:00
Move weightings from Experiment to SemesterExperiment
This commit is contained in:
parent
4e6c2ad708
commit
de4ef9d6f4
6 changed files with 114 additions and 79 deletions
File diff suppressed because one or more lines are too long
|
@ -12,8 +12,16 @@ from flask_wtf import FlaskForm
|
|||
from flask_wtf.file import FileAllowed, FileField, FileRequired
|
||||
from sqlalchemy import and_, func, or_
|
||||
from werkzeug.utils import secure_filename
|
||||
from wtforms import BooleanField, Form, RadioField, SelectField, StringField
|
||||
from wtforms.fields import DateField, IntegerField
|
||||
from wtforms import Form
|
||||
from wtforms.fields import (
|
||||
BooleanField,
|
||||
DateField,
|
||||
DecimalField,
|
||||
IntegerField,
|
||||
RadioField,
|
||||
SelectField,
|
||||
StringField,
|
||||
)
|
||||
from wtforms.validators import URL, DataRequired, Email, NumberRange, Optional
|
||||
|
||||
from advlabdb import adminSpace, app, assistantSpace, db, user_datastore
|
||||
|
@ -523,9 +531,6 @@ class ExperimentView(SecureAdminModelView):
|
|||
"room",
|
||||
"responsibility",
|
||||
"duration_in_days",
|
||||
"oral_weighting",
|
||||
"protocol_weighting",
|
||||
"final_weighting",
|
||||
"semester_experiments",
|
||||
]
|
||||
|
||||
|
@ -538,9 +543,6 @@ class ExperimentView(SecureAdminModelView):
|
|||
|
||||
form_args = {
|
||||
"wiki_link": {"validators": [URL()]},
|
||||
"oral_weighting": {
|
||||
"description": "Oral and protocol weighting have to add to 1! Both are rounded to 2 decimal digits."
|
||||
},
|
||||
}
|
||||
form_extra_fields = {
|
||||
"program": QuerySelectField(
|
||||
|
@ -548,9 +550,6 @@ class ExperimentView(SecureAdminModelView):
|
|||
)
|
||||
}
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
model.checkWeightings(roundWeightings=True)
|
||||
|
||||
|
||||
def assistantQueryFactory():
|
||||
return Assistant.query.filter(Assistant.user.has(User.active == True))
|
||||
|
@ -569,6 +568,28 @@ class SemesterExperimentView(SecureAdminModelView):
|
|||
blank_text="-",
|
||||
)
|
||||
|
||||
oral_weighting = DecimalField(
|
||||
"Oral weighting",
|
||||
validators=[DataRequired(), NumberRange(min=0, max=1)],
|
||||
default=0.5,
|
||||
description="Between 0 and 1",
|
||||
places=2,
|
||||
)
|
||||
protocol_weighting = DecimalField(
|
||||
"Protocol weighting",
|
||||
validators=[DataRequired(), NumberRange(min=0, max=1)],
|
||||
default=0.5,
|
||||
description="Between 0 and 1. Oral and protocol weightings have to add to 1! Both are rounded to 2 decimal digits.",
|
||||
places=2,
|
||||
)
|
||||
final_weighting = DecimalField(
|
||||
"Final weighting",
|
||||
validators=[DataRequired(), NumberRange(min=0, max=1)],
|
||||
default=1.0,
|
||||
description="Between 0 and 1",
|
||||
places=2,
|
||||
)
|
||||
|
||||
assistants = QuerySelectMultipleField("Assistants", query_factory=assistantQueryFactory)
|
||||
|
||||
form = CreateForm
|
||||
|
@ -581,6 +602,9 @@ class SemesterExperimentView(SecureAdminModelView):
|
|||
"semester",
|
||||
]
|
||||
column_details_list = column_list + [
|
||||
"oral_weighting",
|
||||
"protocol_weighting",
|
||||
"final_weighting",
|
||||
"group_experiments",
|
||||
]
|
||||
column_filters = [
|
||||
|
@ -592,9 +616,17 @@ class SemesterExperimentView(SecureAdminModelView):
|
|||
|
||||
def customCreateModel(self, form):
|
||||
return SemesterExperiment(
|
||||
semester=userActiveSemester(), experiment=form.experiment.data, assistants=form.assistants.data
|
||||
semester=userActiveSemester(),
|
||||
oral_weighting=form.oral_weighting.data,
|
||||
protocol_weighting=form.protocol_weighting.data,
|
||||
final_weighting=form.final_weighting.data,
|
||||
experiment=form.experiment.data,
|
||||
assistants=form.assistants.data,
|
||||
)
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
model.checkAndRoundWeightings()
|
||||
|
||||
|
||||
class AssistantView(SecureAdminModelView):
|
||||
def assistantUserQueryFactory():
|
||||
|
|
|
@ -5,7 +5,14 @@ from flask_security import current_user
|
|||
from sqlalchemy import and_
|
||||
|
||||
from advlabdb.exceptions import DataBaseException, ModelViewException
|
||||
from advlabdb.models import ExperimentMark, Part, PartStudent, GroupExperiment, SemesterExperiment, Assistant
|
||||
from advlabdb.models import (
|
||||
Assistant,
|
||||
ExperimentMark,
|
||||
GroupExperiment,
|
||||
Part,
|
||||
PartStudent,
|
||||
SemesterExperiment,
|
||||
)
|
||||
from advlabdb.utils import reportBadAttempt, userActiveSemester
|
||||
|
||||
|
||||
|
|
|
@ -85,21 +85,19 @@ class PartStudent(db.Model):
|
|||
groupExperiments = []
|
||||
|
||||
for experimentMark in self.experiment_marks:
|
||||
if not (experimentMark.oral_mark and experimentMark.protocol_mark):
|
||||
if None in (experimentMark.oral_mark, experimentMark.protocol_mark):
|
||||
# Not all marks are set!
|
||||
return
|
||||
|
||||
groupExperiment = experimentMark.group_experiment
|
||||
groupExperiments.append(groupExperiment)
|
||||
|
||||
experiment = groupExperiment.semester_experiment.experiment
|
||||
semesterExperiment = groupExperiment.semester_experiment
|
||||
|
||||
finalWeighting = experiment.final_weighting
|
||||
finalWeighting = semesterExperiment.final_weighting
|
||||
finalWeightingSum += finalWeighting
|
||||
|
||||
finalMark += finalWeighting * (
|
||||
experiment.oral_weighting * experimentMark.oral_mark
|
||||
+ experiment.protocol_weighting * experimentMark.protocol_mark
|
||||
)
|
||||
finalMark += finalWeighting * experimentMark.final_experiment_mark
|
||||
|
||||
if set(groupExperiments) != set(self.group.group_experiments):
|
||||
flash(f"{self} does not have an experiment mark for every group experiment in his group!", "warning")
|
||||
|
@ -114,7 +112,7 @@ class PartStudent(db.Model):
|
|||
except Exception as ex:
|
||||
flash(str(ex), "error")
|
||||
|
||||
self.session.rollback()
|
||||
db.session.rollback()
|
||||
else:
|
||||
if current_user.has_role("admin"):
|
||||
if oldFinalPartMark != self.final_part_mark:
|
||||
|
@ -205,11 +203,7 @@ class GroupExperiment(db.Model):
|
|||
student = partStudent.student
|
||||
for partStudent in student.part_students:
|
||||
for experimentMark in partStudent.experiment_marks:
|
||||
if (
|
||||
(experimentMark.oral_mark or experimentMark.protocol_mark)
|
||||
and experimentMark.group_experiment.semester_experiment.experiment
|
||||
== semester_experiment.experiment
|
||||
):
|
||||
if experimentMark.group_experiment.semester_experiment.experiment == semester_experiment.experiment:
|
||||
raise Exception(
|
||||
f"{student} has already done {semester_experiment.experiment} in {partStudent.part} and had {experimentMark}!"
|
||||
)
|
||||
|
@ -240,45 +234,12 @@ class Experiment(db.Model):
|
|||
duration_in_days = db.Column(db.Integer, db.CheckConstraint("duration_in_days > 0"), nullable=False)
|
||||
active = db.Column(db.Boolean, default=True, nullable=False)
|
||||
|
||||
oral_weighting = db.Column(
|
||||
db.Float,
|
||||
db.CheckConstraint("oral_weighting >= 0"),
|
||||
db.CheckConstraint("oral_weighting <= 1"),
|
||||
default=0.5,
|
||||
nullable=False,
|
||||
)
|
||||
protocol_weighting = db.Column(
|
||||
db.Float,
|
||||
db.CheckConstraint("protocol_weighting >= 0"),
|
||||
db.CheckConstraint("protocol_weighting <= 1"),
|
||||
default=0.5,
|
||||
nullable=False,
|
||||
)
|
||||
final_weighting = db.Column(
|
||||
db.Float, db.CheckConstraint("final_weighting >= 0"), db.CheckConstraint("final_weighting <= 1"), nullable=False
|
||||
)
|
||||
|
||||
program_id = db.Column(db.Integer, db.ForeignKey("program.id"), nullable=False)
|
||||
|
||||
semester_experiments = db.relationship("SemesterExperiment", backref="experiment", lazy=True)
|
||||
|
||||
__table_args__ = (db.UniqueConstraint(number, program_id),)
|
||||
|
||||
def checkWeightings(self, roundWeightings=False):
|
||||
roundedOralWeighting = round(self.oral_weighting, 2)
|
||||
roundedProtocolWeighting = round(self.protocol_weighting, 2)
|
||||
|
||||
weightingSum = round(roundedOralWeighting + roundedProtocolWeighting, 2)
|
||||
|
||||
if weightingSum != 1:
|
||||
raise DataBaseException(
|
||||
f"Oral and protocol weighting (rounded to 2 decimal digits) sum to {weightingSum} and not 1.00!"
|
||||
)
|
||||
|
||||
if roundWeightings:
|
||||
self.oral_weighting = roundedOralWeighting
|
||||
self.protocol_weighting = roundedProtocolWeighting
|
||||
|
||||
def repr(self):
|
||||
return f"{self.number} {self.program.repr()}"
|
||||
|
||||
|
@ -297,6 +258,27 @@ experiment_assistant = db.Table(
|
|||
class SemesterExperiment(db.Model):
|
||||
# An experiment in a specific semester
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
oral_weighting = db.Column(
|
||||
db.Float,
|
||||
db.CheckConstraint("oral_weighting >= 0"),
|
||||
db.CheckConstraint("oral_weighting <= 1"),
|
||||
default=0.5,
|
||||
nullable=False,
|
||||
)
|
||||
protocol_weighting = db.Column(
|
||||
db.Float,
|
||||
db.CheckConstraint("protocol_weighting >= 0"),
|
||||
db.CheckConstraint("protocol_weighting <= 1"),
|
||||
default=0.5,
|
||||
nullable=False,
|
||||
)
|
||||
final_weighting = db.Column(
|
||||
db.Float,
|
||||
db.CheckConstraint("final_weighting >= 0"),
|
||||
db.CheckConstraint("final_weighting <= 1"),
|
||||
default=1.0,
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
experiment_id = db.Column(db.Integer, db.ForeignKey("experiment.id"), nullable=False)
|
||||
semester_id = db.Column(db.Integer, db.ForeignKey("semester.id"), nullable=False)
|
||||
|
@ -308,6 +290,20 @@ class SemesterExperiment(db.Model):
|
|||
|
||||
__table_args__ = (db.UniqueConstraint(experiment_id, semester_id),)
|
||||
|
||||
def checkAndRoundWeightings(self):
|
||||
roundedOralWeighting = round(self.oral_weighting, 2)
|
||||
roundedProtocolWeighting = round(self.protocol_weighting, 2)
|
||||
|
||||
weightingSum = round(roundedOralWeighting + roundedProtocolWeighting, 2)
|
||||
|
||||
if weightingSum != 1:
|
||||
raise DataBaseException(
|
||||
f"Oral and protocol weightings (rounded to 2 decimal digits) sum to {weightingSum} and not 1.00!"
|
||||
)
|
||||
|
||||
self.oral_weighting = roundedOralWeighting
|
||||
self.protocol_weighting = roundedProtocolWeighting
|
||||
|
||||
def repr(self):
|
||||
return f"{self.experiment.repr()} {self.semester.repr()}"
|
||||
|
||||
|
@ -456,16 +452,22 @@ class ExperimentMark(db.Model):
|
|||
|
||||
experimentMark = ExperimentMark.query.get(params["experiment_mark_id"])
|
||||
|
||||
experiment = experimentMark.group_experiment.semester_experiment.experiment
|
||||
|
||||
oral_mark = params.get("oral_mark") or experimentMark.oral_mark
|
||||
protocol_mark = params.get("protocol_mark") or experimentMark.protocol_mark
|
||||
|
||||
if not (oral_mark and protocol_mark):
|
||||
oral_mark = params.get("oral_mark")
|
||||
if oral_mark is None:
|
||||
oral_mark = experimentMark.oral_mark
|
||||
if oral_mark is None:
|
||||
return
|
||||
else:
|
||||
|
||||
protocol_mark = params.get("protocol_mark")
|
||||
if protocol_mark is None:
|
||||
protocol_mark = experimentMark.protocol_mark
|
||||
if protocol_mark is None:
|
||||
return
|
||||
|
||||
semesterExperiment = experimentMark.group_experiment.semester_experiment
|
||||
|
||||
return roundHalfUpToInt(
|
||||
experiment.oral_weighting * oral_mark + experiment.protocol_weighting * protocol_mark
|
||||
semesterExperiment.oral_weighting * oral_mark + semesterExperiment.protocol_weighting * protocol_mark
|
||||
)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
@ -491,7 +493,7 @@ class ExperimentMark(db.Model):
|
|||
nullable=True,
|
||||
)
|
||||
|
||||
edited_by_admin = db.Column(db.Boolean, default=False, nullable=False)
|
||||
edited_by_admin = db.Column(db.Boolean, default=False, nullable=False) # TODO: Admin id
|
||||
|
||||
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)
|
||||
|
|
6
poetry.lock
generated
6
poetry.lock
generated
|
@ -327,7 +327,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|||
|
||||
[[package]]
|
||||
name = "rope"
|
||||
version = "0.22.0"
|
||||
version = "0.23.0"
|
||||
description = "a python refactoring library..."
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -625,8 +625,8 @@ pyflakes = [
|
|||
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
|
||||
]
|
||||
rope = [
|
||||
{file = "rope-0.22.0-py3-none-any.whl", hash = "sha256:2847220bf72ead09b5abe72b1edc9cacff90ab93663ece06913fc97324167870"},
|
||||
{file = "rope-0.22.0.tar.gz", hash = "sha256:b00fbc064a26fc62d7220578a27fd639b2fad57213663cc396c137e92d73f10f"},
|
||||
{file = "rope-0.23.0-py3-none-any.whl", hash = "sha256:edf2ed3c9b35a8814752ffd3ea55b293c791e5087e252461de898e953cf9c146"},
|
||||
{file = "rope-0.23.0.tar.gz", hash = "sha256:f87662c565086d660fc855cc07f37820267876634c3e9e51bddb32ff51547268"},
|
||||
]
|
||||
sqlalchemy = [
|
||||
{file = "SQLAlchemy-1.4.31-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c3abc34fed19fdeaead0ced8cf56dd121f08198008c033596aa6aae7cc58f59f"},
|
||||
|
|
|
@ -68,9 +68,6 @@ with app.app_context():
|
|||
building="phy",
|
||||
responsibility="none",
|
||||
duration_in_days=2,
|
||||
oral_weighting=0.5,
|
||||
protocol_weighting=0.5,
|
||||
final_weighting=1,
|
||||
)
|
||||
|
||||
ex2 = Experiment(
|
||||
|
@ -81,9 +78,6 @@ with app.app_context():
|
|||
building="phy",
|
||||
responsibility="none",
|
||||
duration_in_days=2,
|
||||
oral_weighting=0.5,
|
||||
protocol_weighting=0.5,
|
||||
final_weighting=1,
|
||||
)
|
||||
|
||||
db.session.add(ex1)
|
||||
|
|
Loading…
Reference in a new issue