Skip to content

Commit f8f12ef

Browse files
reppuli92ihalaij1
authored andcommitted
Add questionnaire reset functionality
1 parent 7b6fccd commit f8f12ef

5 files changed

Lines changed: 60 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 must be refreshed
49+
# 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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from userprofile.models import UserProfile
3333
from aplus.celery import retry_submissions
3434
from . import exercise_models
35+
from .cache.exercise import ExerciseCache
3536
from .exercise_models import LearningObjectProto
3637

3738
logger = logging.getLogger('aplus.exercise')
@@ -255,6 +256,25 @@ 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+
# Use the current view URL name when it affects the exercise load URL (e.g. exercise vs exercise-plain).
263+
url_name = getattr(getattr(request, "resolver_match", None), "url_name", None)
264+
if url_name not in ("exercise", "exercise-plain", "lti-exercise"):
265+
url_name = "exercise"
266+
267+
exercise_cache = ExerciseCache(
268+
exercise,
269+
meta_data_dict['lang'],
270+
request,
271+
submitters,
272+
url_name,
273+
)
274+
version = exercise_cache.exercise_version()
275+
if version:
276+
meta_data_dict['exercise_version'] = version
277+
258278
try:
259279
if ('lti-launch-id' in request.session
260280
and has_lti_access_to_course(request, None, exercise.course_instance)):

exercise/views.py

Lines changed: 30 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,25 @@ def get_page(self, request: HttpRequest, students: List[UserProfile]) -> Exercis
338349
url_name=self.post_url_name,
339350
)
340351
if self.feedback_revealed:
352+
try:
353+
submission_version = submission.meta_data.get('exercise_version')
354+
except AttributeError:
355+
# Handle cases where database includes null or non dictionary json
356+
submission_version = None
357+
submission_language = submission.lang
358+
current_language = get_language().lower()
359+
current_version = self._get_current_exercise_version(request, students)
360+
if (
361+
(submission_language is not None
362+
and submission_language.lower() != current_language)
363+
or (current_version is not None
364+
and submission_version != current_version)
365+
):
366+
return self.exercise.load(
367+
request,
368+
students,
369+
url_name=self.post_url_name,
370+
)
341371
page = ExercisePage(self.exercise)
342372
page.content = submission.feedback
343373
page.is_loaded = True

0 commit comments

Comments
 (0)