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",
"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="+",
)
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_passed = models.BooleanField(null=True, blank=True)
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 = models.ForeignKey(Assignment, 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,
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(
"--create-edoniq-test-results",
type=(int, int),
default=(None, None),
metavar="USER_POINTS MAX_POINTS",
type=(int, int, float),
default=(None, None, 0),
metavar="USER_POINTS MAX_POINTS POINTS_DEDUCTED",
help="Create edoniq result data for test-student1@example.com with user points and max points",
)
@click.option(
@ -112,6 +117,7 @@ def command(
create_assignment_completion,
create_assignment_evaluation,
assignment_evaluation_scores,
assignment_points_deducted,
create_edoniq_test_results,
create_feedback_responses,
create_course_completion_performance_criteria,
@ -171,9 +177,10 @@ def command(
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID),
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:
print(
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),
user_points=user_points,
max_points=max_points,
evaluation_points_deducted=points_deducted,
)
if create_feedback_responses:

View File

@ -58,3 +58,10 @@ def pretty_print_json(json_string):
# pylint: disable=broad-except
except Exception:
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")
if 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"] = (
request.data.get("create_feedback_responses") == "true"
@ -159,10 +162,12 @@ def cypress_reset_view(request):
# edoniq test results
edoniq_test_user_points = request.data.get("edoniq_test_user_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):
options["create_edoniq_test_results"] = (
int(edoniq_test_user_points),
int(edoniq_test_max_points),
float(edoniq_points_deducted),
)
options["create_course_completion_performance_criteria"] = (

View File

@ -1,3 +1,4 @@
from collections import deque
from datetime import datetime
from dateutil.relativedelta import MO, relativedelta, TH, TU, WE
@ -42,6 +43,7 @@ from vbv_lernwelt.core.constants import (
TEST_TRAINER1_USER_ID,
)
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.factories import CoursePageFactory
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(
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:
subtasks = assignment.get_evaluation_tasks()
evaluation_points = 0
input_scores_deque = deque(input_scores) if input_scores else deque()
for index, evaluation_task in enumerate(subtasks):
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]
evaluation_points += task_score
update_assignment_completion(
ac, _ = update_assignment_completion(
assignment_user=assignment_user,
assignment=assignment,
course_session=course_session,
@ -380,7 +389,7 @@ def create_test_assignment_evaluation_data(
evaluation_user=evaluation_user,
)
update_assignment_completion(
ac, _ = update_assignment_completion(
assignment_user=assignment_user,
assignment=assignment,
course_session=course_session,
@ -391,16 +400,26 @@ def create_test_assignment_evaluation_data(
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(
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"][
"max_points"
] = max_points
assignment.assignment.save()
if assignment and course_session and assignment_user:
update_assignment_completion(
ac, _ = update_assignment_completion(
assignment_user=assignment_user,
assignment=assignment,
course_session=course_session,
@ -412,6 +431,10 @@ def create_edoniq_test_result_data(
evaluation_max_points=max_points,
)
if evaluation_points_deducted > 0:
ac.evaluation_points_deducted = evaluation_points_deducted
ac.save()
def create_feedback_response_data(
course_session,

View File

@ -73,6 +73,10 @@
<label>
evaluation score:
<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>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
@ -84,6 +88,10 @@
<label>
max points:
<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>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>