diff --git a/exercise/cache/points.py b/exercise/cache/points.py
index dfe1dc469..1d7639b9d 100644
--- a/exercise/cache/points.py
+++ b/exercise/cache/points.py
@@ -24,7 +24,7 @@
from django.db.models.signals import post_save, post_delete, pre_delete, m2m_changed
from django.utils import timezone
-from course.models import CourseInstance, CourseModule, StudentGroup
+from course.models import CourseInstance, CourseModule, StudentGroup, StudentModuleGoal
from deviations.models import DeadlineRuleDeviation, MaxSubmissionsRuleDeviation
from lib.cache.cached import DBDataManager, Dependencies, ProxyManager, resolve_proxies
from lib.helpers import format_points
@@ -865,6 +865,7 @@ class ModulePoints(DifficultyStats, ModuleEntryBase[LearningObjectPoints]):
_children_unconfirmed: bool
is_model_answer_revealed: bool
confirmable_children: bool
+ personalized_points_module_goal: Optional[int]
children_unconfirmed = RevealableAttribute[bool]()
@@ -919,7 +920,7 @@ def _generate_data(
self._points_by_difficulty = {}
self._true_unconfirmed_points_by_difficulty = {}
self._unconfirmed_points_by_difficulty = {}
-
+ self.personalized_points_module_goal = None
self.instance = precreated.get_or_create_proxy(
CachedPointsData, *self.instance._params, user_id, modifiers=self._modifiers
)
@@ -940,6 +941,15 @@ def _generate_data(
elif entry.submission_count > 0:
self.confirmable_children = True
+ def update_personalized_points():
+ print("Called update_personalized_points")
+ try:
+ self.personalized_points_module_goal = StudentModuleGoal.objects.get(module_id=module_id, student_id=user_id).personalized_points_goal
+ print("Module Goal: " + str(self.personalized_points_module_goal))
+ except StudentModuleGoal.DoesNotExist:
+ print("Module Goal: None")
+ self.personalized_points_module_goal = None
+
def add_points(children):
for entry in children:
if not entry.confirm_the_level and isinstance(entry, ExercisePoints) and entry.is_visible():
@@ -948,7 +958,8 @@ def add_points(children):
add_points(entry.children)
add_points(self.children)
-
+ print("Called add_points")
+ update_personalized_points()
self._true_passed = self._true_passed and self._true_points >= self.points_to_pass
self._passed = self._passed and self._points >= self.points_to_pass
@@ -966,6 +977,9 @@ def add_points(children):
ModuleEntryBase: [self._params[:1]],
LearningObjectPoints: [proxy._params for proxy in self.children],
}
+
+ def update_personalized_points(self):
+ self._generate_data()
CachedPointsDataType = TypeVar("CachedPointsDataType", bound="CachedPointsData")
diff --git a/exercise/static/exercise/css/goal_points.css b/exercise/static/exercise/css/goal_points.css
new file mode 100644
index 000000000..bd97741ce
--- /dev/null
+++ b/exercise/static/exercise/css/goal_points.css
@@ -0,0 +1,5 @@
+.progress .goal-points {
+ position: absolute;
+ height: 20px;
+ border-left: 1px solid blue;
+}
diff --git a/exercise/templates/exercise/_points_progress.html b/exercise/templates/exercise/_points_progress.html
index 77121b29b..24f6eea32 100644
--- a/exercise/templates/exercise/_points_progress.html
+++ b/exercise/templates/exercise/_points_progress.html
@@ -1,18 +1,21 @@
{% load i18n %}
+{% load static %}
{% if not feedback_revealed %}
{% translate "RESULTS_OF_SOME_ASSIGNMENTS_ARE_CURRENTLY_HIDDEN" %}
{% endif %}
-Personalized {{personalized_points_goal}}
-
-
{% if required_percentage %}
{% endif %}
+ {% if personalized_points_module_goal %}
+
+
+ {% endif %}
diff --git a/exercise/templates/exercise/_user_results.html b/exercise/templates/exercise/_user_results.html
index 8085d1051..eab75d0b7 100644
--- a/exercise/templates/exercise/_user_results.html
+++ b/exercise/templates/exercise/_user_results.html
@@ -2,7 +2,7 @@
{% load static %}
{% load course %}
{% load exercise %}
-
+{% include 'exercise/points_goal_modal.html' %}
{% if categories|len_listed > 1 %}
@@ -45,11 +45,6 @@
aria-expanded="{% if accessible and not after_open or open %}true{% else %}false{% endif %}" aria-controls="#module{{ module.id }}">
{% points_badge module "pull-right" %}
- {% for goal in personalized_points_goal %}
- {% if goal.module.id == module.id %}
- PERSONALIZED POINTS GOAL {{ goal.personalized_points_goal }}%
- {% endif %}
- {% endfor %}
{% if not accessible %}
{% translate "OPENS ON" %} {{ module.opening_time }}
@@ -109,8 +104,15 @@
{% endif %}
- Goals {{goal.personalized_points_goal}}
- {% points_progress module 50 %}
+ {% with module_id=module.id student_id=user.id %}
+ {% get_goal module_id student_id as goal %}
+
+
+ {% points_progress module %}
+ {% endwith %}
+
{{ module.introduction|safe }}
{% if student %}
{% adddeviationsbutton instance module submitters=student %}
@@ -282,3 +284,16 @@
startListen("{{course|safe}}");
}, false);
+
+
\ No newline at end of file
diff --git a/exercise/templates/exercise/points_goal_modal.html b/exercise/templates/exercise/points_goal_modal.html
new file mode 100644
index 000000000..8b87ab9be
--- /dev/null
+++ b/exercise/templates/exercise/points_goal_modal.html
@@ -0,0 +1,28 @@
+
+
\ No newline at end of file
diff --git a/exercise/templatetags/exercise.py b/exercise/templatetags/exercise.py
index 7158e0587..1def0e3fc 100644
--- a/exercise/templatetags/exercise.py
+++ b/exercise/templatetags/exercise.py
@@ -53,11 +53,15 @@ def _prepare_context(context: Context, student: Optional[User] = None) -> Cached
def _get_toc(context, student=None):
points = _prepare_context(context, student)
- # Add personalized_points_goal to the context
+
+ # Add personalized_points_goal to the context
if 'personalized_points_goal' not in context:
- context["personalized_points_goal"] = StudentModuleGoal.objects.all
- print(StudentModuleGoal.objects.all())
-
+ course_module = context.get('course_module')
+ if course_module:
+ context["personalized_points_goal"] = StudentModuleGoal.objects.filter(module=course_module)
+ else:
+ context["personalized_points_goal"] = StudentModuleGoal.objects.none()
+
context = context.flatten()
context.update({
'modules': points.modules_flatted(),
@@ -69,6 +73,14 @@ def _get_toc(context, student=None):
return context
+@register.simple_tag
+def get_goal(module, student):
+ try:
+ goal = StudentModuleGoal.objects.get(module_id=module, student_id=student)
+ return goal
+ except StudentModuleGoal.DoesNotExist:
+ return None
+
def _is_accessible(context, entry, t):
if t and t > _prepare_now(context):
return False
@@ -115,7 +127,7 @@ def user_results(context: Context, student: Optional[User] = None) -> Dict[str,
.order_by()
)
values['exercise_submitter_counts'] = {row['submissions__exercise_id']: row['count'] for row in counts}
- print("StudentModuleGoalObjects", StudentModuleGoal.objects.all())
+ #print("StudentModuleGoalObjects", StudentModuleGoal.objects.all())
return values
@@ -221,6 +233,7 @@ def _points_data(
'unofficial_submission_type': getattr(obj, 'unofficial_submission_type', None),
'confirmable_points': getattr(obj, 'confirmable_points', False),
'feedback_revealed': getattr(obj, 'feedback_revealed', True),
+ 'personalized_points_module_goal': getattr(obj, 'personalized_points_module_goal', None),
}
reveal_time = getattr(obj, 'feedback_reveal_time', None)
@@ -259,10 +272,8 @@ def _points_data(
@register.inclusion_tag("exercise/_points_progress.html")
def points_progress(
obj: Union[CachedPointsData, ModulePoints, CategoryPoints],
- personalized_points_goal: int = 100,
) -> Dict[str, Any]:
data = _points_data(obj, None)
- data['personalized_points_goal'] = personalized_points_goal
return data
diff --git a/exercise/urls.py b/exercise/urls.py
index 8cd74f05b..75b7e7471 100644
--- a/exercise/urls.py
+++ b/exercise/urls.py
@@ -85,6 +85,9 @@
re_path(MODULE_URL_PREFIX + r'student_module_goal/$',
views.StudentModuleGoalView.as_view(),
name="student_module_goal"),
+ re_path(r'save_points_goal/$',
+ views.StudentModuleGoalView.save_points_goal,
+ name="save_points_goal"),
re_path(EDIT_URL_PREFIX + r'analytics/$',
staff_views.AnalyticsView.as_view(),
name="analytics"),
diff --git a/exercise/views.py b/exercise/views.py
index afb97a319..b88e5dfd5 100644
--- a/exercise/views.py
+++ b/exercise/views.py
@@ -21,7 +21,7 @@
from lib.remote_page import RemotePageNotFound, request_for_response
from lib.viewbase import BaseFormView, BaseRedirectMixin, BaseView
from userprofile.models import UserProfile
-from .cache.points import ExercisePoints
+from .cache.points import CachedPoints, ExercisePoints, ModulePoints
from .models import BaseExercise, LearningObject, LearningObjectDisplay
from .protocol.exercise_page import ExercisePage
from .submission_models import SubmittedFile, Submission, SubmissionTagging
@@ -614,6 +614,38 @@ class StudentModuleGoalView(CourseModuleBaseView, BaseFormView):
template_name = "exercise/student_module_goal.html"
form_class = StudentModuleGoalForm
+ @csrf_exempt
+ def save_points_goal(request):
+ if request.method == 'POST':
+ points_goal = request.POST.get('points_goal')
+ user_id = request.POST.get('student')
+ module_id = request.POST.get('module-id')
+
+ student = UserProfile.objects.get(id=user_id)
+ module = CourseModule.objects.get(id=module_id)
+
+ cached_points = CachedPoints(module.course_instance, student, True)
+ cached_module, _, _, _ = cached_points.find(module)
+ print(cached_module.max_points)
+ # Points goal can either be an integer or percentage. If the the points goal is given as a percentage, give it to the points goal directly as an int
+ # Otherwise calculate the points goal by taking the integer and calculating how much that is of the module's max points
+ if '%' in points_goal:
+ points_goal = int(points_goal.replace('%', ''))
+ elif cached_module.max_points > 0:
+ points_goal = float(float(points_goal) / float(cached_module.max_points)) * 100
+ elif cached_module.max_points == 0:
+ points_goal = 0
+
+ if points_goal > 100:
+ points_goal = 100
+ elif points_goal < 0:
+ points_goal = 0
+ StudentModuleGoal.objects.update_or_create(student=student, module=module, defaults={'personalized_points_goal': points_goal})
+ print(StudentModuleGoal.objects.get(student=user_id, module=module_id).personalized_points_goal)
+ cached_points.invalidate(module.course_instance, student)
+ return redirect(request.META.get('HTTP_REFERER', '/'))
+ return HttpResponse('Invalid request', status=400)
+
def get_common_objects(self):
super().get_common_objects()