Skip to content

Commit 083c930

Browse files
committed
Add questionnaire reset functionality
1 parent 7b6fccd commit 083c930

5 files changed

Lines changed: 51 additions & 0 deletions

File tree

exercise/cache/exercise.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ def __init__( # pylint: disable=too-many-arguments
4444
super().__init__(exercise, modifiers=[language])
4545

4646
def _needs_generation(self, data: Dict[str, Any]) -> bool:
47+
if data and 'exercise_version' not in data:
48+
# Cache entries created before exercise version stamping was added
49+
# must be refreshed so update detection can work.
50+
return True
4751
expires = data['expires'] if data else None
4852
return not expires or time.time() > expires
4953
# pylint: disable-next=arguments-differ
@@ -60,6 +64,7 @@ def _generate_data(self, exercise: 'BaseExercise', data: Optional[Dict[str, Any]
6064
'head': page.head,
6165
'content': content,
6266
'last_modified': page.last_modified,
67+
'exercise_version': page.exercise_version,
6368
'expires': page.expires if page.is_loaded else 0,
6469
}
6570
except RemotePageNotModified as e:
@@ -74,6 +79,9 @@ def content(self) -> str:
7479
content = decompress(self.data['content']).decode('utf-8')
7580
return content
7681

82+
def exercise_version(self) -> str:
83+
return self.data.get('exercise_version') or ''
84+
7785

7886
def invalidate_instance(instance: 'CourseInstance') -> None:
7987
for module in instance.course_modules.all():

exercise/protocol/aplus.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,5 @@ def parse_page_content(
181181
id_attrs_to_remove = ('exercise', 'chapter', 'aplus')
182182
page.content, page.clean_content = remote_page.element_or_body(element_selectors, id_attrs_to_remove)
183183
page.last_modified = remote_page.last_modified()
184+
page.exercise_version = remote_page.meta("x-aplus-exercise-version") or ""
184185
page.expires = remote_page.expires()

exercise/protocol/exercise_page.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(self, exercise):
2323
self.content = ""
2424
self.clean_content = ""
2525
self.last_modified = ""
26+
self.exercise_version = "" # content hash supplied by mooc-grader
2627
self.expires = 0
2728
self.meta = {
2829
"title": exercise.name,

exercise/submission_models.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from lti_tool.utils import has_lti_access_to_course
3232
from userprofile.models import UserProfile
3333
from aplus.celery import retry_submissions
34+
from .cache.exercise import ExerciseCache
3435
from . import exercise_models
3536
from .exercise_models import LearningObjectProto
3637

@@ -255,6 +256,20 @@ def create_from_post(self, exercise, submitters, request):
255256
if 'lang' not in meta_data_dict:
256257
meta_data_dict['lang'] = get_language()
257258

259+
# Store the current exercise page version for later compatibility checks
260+
# when deciding whether old feedback can be shown.
261+
262+
exercise_cache = ExerciseCache(
263+
exercise,
264+
meta_data_dict['lang'],
265+
request,
266+
submitters,
267+
'exercise',
268+
)
269+
version = exercise_cache.exercise_version()
270+
if version:
271+
meta_data_dict['exercise_version'] = version
272+
258273
try:
259274
if ('lti-launch-id' in request.session
260275
and has_lti_access_to_course(request, None, exercise.course_instance)):

exercise/views.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from lib.remote_page import RemotePageNotFound, request_for_response
2626
from lib.viewbase import BaseFormView, BaseRedirectMixin, BaseView
2727
from userprofile.models import UserProfile
28+
from .cache.exercise import ExerciseCache
2829
from .cache.points import CachedPoints, ModulePoints, ExercisePoints
2930
from .models import BaseExercise, LearningObject, LearningObjectDisplay
3031
from .protocol.exercise_page import ExercisePage
@@ -296,6 +297,16 @@ def submission_check(self, request=None):
296297
)
297298
return submission_status, submission_allowed, issues, students
298299

300+
def _get_current_exercise_version(self, request: HttpRequest, students: List[UserProfile]) -> Optional[str]:
301+
cache = ExerciseCache(
302+
self.exercise,
303+
get_language(),
304+
request,
305+
students,
306+
self.post_url_name,
307+
)
308+
return cache.exercise_version() or None
309+
299310
def get_page(self, request: HttpRequest, students: List[UserProfile]) -> ExercisePage:
300311
"""
301312
Determines which page should be displayed for this exercise:
@@ -338,6 +349,21 @@ def get_page(self, request: HttpRequest, students: List[UserProfile]) -> Exercis
338349
url_name=self.post_url_name,
339350
)
340351
if self.feedback_revealed:
352+
submission_version = (submission.meta_data or {}).get('exercise_version')
353+
submission_language = submission.lang
354+
current_language = get_language()
355+
current_version = self._get_current_exercise_version(request, students)
356+
if (
357+
(submission_language is not None
358+
and submission_language != current_language)
359+
or (current_version is not None
360+
and submission_version != current_version)
361+
):
362+
return self.exercise.load(
363+
request,
364+
students,
365+
url_name=self.post_url_name,
366+
)
341367
page = ExercisePage(self.exercise)
342368
page.content = submission.feedback
343369
page.is_loaded = True

0 commit comments

Comments
 (0)