Add `evaluation_points_deducted` field

This commit is contained in:
Daniel Egger 2024-05-21 11:17:24 +02:00
parent fe0fb55bc9
commit 7c4300f40c
8 changed files with 95 additions and 11 deletions

View File

@ -18,3 +18,10 @@ class AssignmentCompletionAdmin(admin.ModelAdmin):
"assignment_user", "assignment_user",
"course_session", "course_session",
] ]
list_filter = [
"completion_status",
"assignment__assignment_type",
"course_session__course",
"course_session",
]
search_fields = ["assignment_user__email"]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2024-05-21 13:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assignment', '0013_assignment_competence_certificate_weight'),
]
operations = [
migrations.AddField(
model_name='assignmentcompletion',
name='evaluation_points_deducted',
field=models.FloatField(default=0.0, verbose_name='Punkteabzug'),
),
]

View File

@ -340,10 +340,18 @@ class AssignmentCompletion(models.Model):
related_name="+", related_name="+",
) )
evaluation_points = models.FloatField(null=True, blank=True) evaluation_points = models.FloatField(null=True, blank=True)
evaluation_points_deducted = models.FloatField(
default=0.0, verbose_name="Punkteabzug"
)
evaluation_max_points = models.FloatField(null=True, blank=True) evaluation_max_points = models.FloatField(null=True, blank=True)
evaluation_passed = models.BooleanField(null=True, blank=True) evaluation_passed = models.BooleanField(null=True, blank=True)
edoniq_extended_time_flag = models.BooleanField(default=False) edoniq_extended_time_flag = models.BooleanField(default=False)
@property
def evaluation_points_final(self):
# with Django>=5 it can be replaced with a `GeneratedField`
return (self.evaluation_points or 0.0) - self.evaluation_points_deducted
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE) assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE) assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE) course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)

View File

@ -66,11 +66,16 @@ from vbv_lernwelt.self_evaluation_feedback.models import (
default=None, default=None,
help="Provide assignment evaluation scores in the format: 6,6,6,3,3", help="Provide assignment evaluation scores in the format: 6,6,6,3,3",
) )
@click.option(
"--assignment-points-deducted",
default=0,
help="Provide assignment points deducted",
)
@click.option( @click.option(
"--create-edoniq-test-results", "--create-edoniq-test-results",
type=(int, int), type=(int, int, float),
default=(None, None), default=(None, None, 0),
metavar="USER_POINTS MAX_POINTS", metavar="USER_POINTS MAX_POINTS POINTS_DEDUCTED",
help="Create edoniq result data for test-student1@example.com with user points and max points", help="Create edoniq result data for test-student1@example.com with user points and max points",
) )
@click.option( @click.option(
@ -112,6 +117,7 @@ def command(
create_assignment_completion, create_assignment_completion,
create_assignment_evaluation, create_assignment_evaluation,
assignment_evaluation_scores, assignment_evaluation_scores,
assignment_points_deducted,
create_edoniq_test_results, create_edoniq_test_results,
create_feedback_responses, create_feedback_responses,
create_course_completion_performance_criteria, create_course_completion_performance_criteria,
@ -171,9 +177,10 @@ def command(
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID), assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID), evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID),
input_scores=assignment_evaluation_scores, input_scores=assignment_evaluation_scores,
points_deducted=assignment_points_deducted,
) )
user_points, max_points = create_edoniq_test_results user_points, max_points, points_deducted = create_edoniq_test_results
if user_points is not None and max_points is not None: if user_points is not None and max_points is not None:
print( print(
f"Create edoniq test results: User Points: {user_points}, Max Points: {max_points}" f"Create edoniq test results: User Points: {user_points}, Max Points: {max_points}"
@ -186,6 +193,7 @@ def command(
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID), assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
user_points=user_points, user_points=user_points,
max_points=max_points, max_points=max_points,
evaluation_points_deducted=points_deducted,
) )
if create_feedback_responses: if create_feedback_responses:

View File

@ -58,3 +58,10 @@ def pretty_print_json(json_string):
# pylint: disable=broad-except # pylint: disable=broad-except
except Exception: except Exception:
return json_string return json_string
def safe_deque_popleft(deq, default=None):
try:
return deq.popleft()
except IndexError:
return default

View File

@ -151,6 +151,9 @@ def cypress_reset_view(request):
assignment_evaluation_scores = request.data.get("assignment_evaluation_scores") assignment_evaluation_scores = request.data.get("assignment_evaluation_scores")
if assignment_evaluation_scores: if assignment_evaluation_scores:
options["assignment_evaluation_scores"] = assignment_evaluation_scores options["assignment_evaluation_scores"] = assignment_evaluation_scores
options["assignment_points_deducted"] = float(
request.data.get("assignment_points_deducted") or 0
)
options["create_feedback_responses"] = ( options["create_feedback_responses"] = (
request.data.get("create_feedback_responses") == "true" request.data.get("create_feedback_responses") == "true"
@ -159,10 +162,12 @@ def cypress_reset_view(request):
# edoniq test results # edoniq test results
edoniq_test_user_points = request.data.get("edoniq_test_user_points") edoniq_test_user_points = request.data.get("edoniq_test_user_points")
edoniq_test_max_points = request.data.get("edoniq_test_max_points") edoniq_test_max_points = request.data.get("edoniq_test_max_points")
edoniq_points_deducted = request.data.get("edoniq_test_points_deducted") or 0
if bool(edoniq_test_user_points and edoniq_test_max_points): if bool(edoniq_test_user_points and edoniq_test_max_points):
options["create_edoniq_test_results"] = ( options["create_edoniq_test_results"] = (
int(edoniq_test_user_points), int(edoniq_test_user_points),
int(edoniq_test_max_points), int(edoniq_test_max_points),
float(edoniq_points_deducted),
) )
options["create_course_completion_performance_criteria"] = ( options["create_course_completion_performance_criteria"] = (

View File

@ -1,3 +1,4 @@
from collections import deque
from datetime import datetime from datetime import datetime
from dateutil.relativedelta import MO, relativedelta, TH, TU, WE from dateutil.relativedelta import MO, relativedelta, TH, TU, WE
@ -42,6 +43,7 @@ from vbv_lernwelt.core.constants import (
TEST_TRAINER1_USER_ID, TEST_TRAINER1_USER_ID,
) )
from vbv_lernwelt.core.models import User from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.utils import safe_deque_popleft
from vbv_lernwelt.course.consts import COURSE_TEST_ID from vbv_lernwelt.course.consts import COURSE_TEST_ID
from vbv_lernwelt.course.factories import CoursePageFactory from vbv_lernwelt.course.factories import CoursePageFactory
from vbv_lernwelt.course.models import ( from vbv_lernwelt.course.models import (
@ -350,20 +352,27 @@ def create_test_assignment_submitted_data(assignment, course_session, user):
def create_test_assignment_evaluation_data( def create_test_assignment_evaluation_data(
assignment, course_session, assignment_user, evaluation_user, input_scores=None assignment,
course_session,
assignment_user,
evaluation_user,
input_scores=None,
points_deducted=0,
): ):
if assignment and course_session and assignment_user and evaluation_user: if assignment and course_session and assignment_user and evaluation_user:
subtasks = assignment.get_evaluation_tasks() subtasks = assignment.get_evaluation_tasks()
evaluation_points = 0 evaluation_points = 0
input_scores_deque = deque(input_scores) if input_scores else deque()
for index, evaluation_task in enumerate(subtasks): for index, evaluation_task in enumerate(subtasks):
task_score = evaluation_task["value"]["max_points"] task_score = evaluation_task["value"]["max_points"]
if input_scores[index] < len(input_scores): input_score = safe_deque_popleft(input_scores_deque)
if input_score is not None:
task_score = input_scores[index] task_score = input_scores[index]
evaluation_points += task_score evaluation_points += task_score
update_assignment_completion( ac, _ = update_assignment_completion(
assignment_user=assignment_user, assignment_user=assignment_user,
assignment=assignment, assignment=assignment,
course_session=course_session, course_session=course_session,
@ -380,7 +389,7 @@ def create_test_assignment_evaluation_data(
evaluation_user=evaluation_user, evaluation_user=evaluation_user,
) )
update_assignment_completion( ac, _ = update_assignment_completion(
assignment_user=assignment_user, assignment_user=assignment_user,
assignment=assignment, assignment=assignment,
course_session=course_session, course_session=course_session,
@ -391,16 +400,26 @@ def create_test_assignment_evaluation_data(
evaluation_points=evaluation_points, evaluation_points=evaluation_points,
) )
# take the last input score as deduction if there is one left...
if points_deducted > 0:
ac.evaluation_points_deducted = points_deducted
ac.save()
def create_edoniq_test_result_data( def create_edoniq_test_result_data(
assignment, course_session, assignment_user, user_points=19, max_points=24 assignment,
course_session,
assignment_user,
user_points=19,
max_points=24,
evaluation_points_deducted=0,
): ):
assignment.assignment.evaluation_tasks.raw_data[0]["value"][ assignment.assignment.evaluation_tasks.raw_data[0]["value"][
"max_points" "max_points"
] = max_points ] = max_points
assignment.assignment.save() assignment.assignment.save()
if assignment and course_session and assignment_user: if assignment and course_session and assignment_user:
update_assignment_completion( ac, _ = update_assignment_completion(
assignment_user=assignment_user, assignment_user=assignment_user,
assignment=assignment, assignment=assignment,
course_session=course_session, course_session=course_session,
@ -412,6 +431,10 @@ def create_edoniq_test_result_data(
evaluation_max_points=max_points, evaluation_max_points=max_points,
) )
if evaluation_points_deducted > 0:
ac.evaluation_points_deducted = evaluation_points_deducted
ac.save()
def create_feedback_response_data( def create_feedback_response_data(
course_session, course_session,

View File

@ -73,6 +73,10 @@
<label> <label>
evaluation score: evaluation score:
<input type="text" name="assignment_evaluation_scores" placeholder="6,6,6,3,3"> <input type="text" name="assignment_evaluation_scores" placeholder="6,6,6,3,3">
</label><br>
<label>
points deducted:
<input type="number" name="assignment_points_deducted" min="0">
</label> </label>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div> <div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
@ -84,6 +88,10 @@
<label> <label>
max points: max points:
<input type="number" name="edoniq_test_max_points" min="0"> <input type="number" name="edoniq_test_max_points" min="0">
</label><br>
<label>
points deducted:
<input type="number" name="edoniq_test_points_deducted" min="0">
</label> </label>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div> <div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>