Skip to content

Commit

Permalink
Include numbas.review_allowed SCORM element, to determine if full r…
Browse files Browse the repository at this point in the history
…eview is allowed.

See numbas/editor#795

If review mode is not allowed yet, a student can still enter an exam,
but in the "just completed" mode, so they might not see all feedback,
depending on the exam's feedback settings.

The attempts listing page shows when each kind of feedback is available.
  • Loading branch information
christianp committed Jul 12, 2024
1 parent aac37b7 commit 109d3bc
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 16 deletions.
1 change: 1 addition & 0 deletions numbas_lti/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def save(self, commit=True):

class CreateExamForm(ModelForm):
package = forms.FileField(required=False)

class Meta:
model = Exam
fields = ['package','retrieve_url','rest_url']
Expand Down
28 changes: 28 additions & 0 deletions numbas_lti/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,34 @@ def duration(self):

return self._duration

def get_feedback_settings(self, completed, review_allowed):
content = self.source()

def get(node, attr, default=None):
return node.get(attr, node.get(attr.lower(), default))

feedback = get(content, 'feedback', {})

def resolve_feedback_setting(setting):
return {
'always': True,
'oncompletion': completed,
'inreview': review_allowed,
'never': False,
}[setting]

info = [
(_('Maximum available score'), get(feedback,'showTotalMark', 'always')),
(_('Whether answers are correct'), get(feedback,'showAnswerState', 'always')),
(_('Awarded scores'), get(feedback,'showActualMark', 'always')),
(_('Feedback messages for each question part'), get(feedback, 'showPartFeedbackMessages', 'always')),
(_('Expected answers to each part'), get(feedback, 'revealExpectedAnswers', 'inreview')),
(_('Advice for each question'), get(feedback, 'revealAdvice', 'inreview')),
]

return info


GRADING_METHODS = [
('highest',_('Highest score')),
('last',_('Last attempt')),
Expand Down
1 change: 0 additions & 1 deletion numbas_lti/templates/numbas_lti/review_not_allowed.html

This file was deleted.

38 changes: 30 additions & 8 deletions numbas_lti/templates/numbas_lti/show_attempts.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,9 @@ <h1>
</td>
<td>
{% if attempt.completed %}
{% if attempt.review_allowed %}
<a title="{% blocktranslate with start_time=attempt.start_time %}Review attempt started at {{start_time}}{% endblocktranslate %}" class="button info" href="{% url_with_lti 'run_attempt' pk=attempt.pk %}">
{% icon 'play' %} {% translate "Review this attempt" %}
</a>
{% else %}
{% if attempt.resource.allow_review_from %}
{% include "numbas_lti/review_not_allowed.html" with allow_review_from=attempt.resource.allow_review_from %}
{% endif %}
{% endif %}
{% icon 'play' %} {% translate "Review this attempt" %}
</a>
{% else %}
{% if attempt.resume_allowed %}
<a title="{% blocktranslate with start_time=attempt.start_time %}Continue attempt started at {{start_time}}{% endblocktranslate %}" class="button {% if attempt.completed %}info{% else %}primary{% endif %}" href="{% url_with_lti 'run_attempt' pk=attempt.pk %}">
Expand All @@ -92,6 +86,34 @@ <h1>
{% endfor %}
</tbody>
</table>

{% if resource.show_marks_when == 'review' and resource.allow_review_from %}
<p class="warning">{% blocktranslate with time_iso=resource.allow_review_from|date:"c" time=resource.allow_review_from %}Full review will be available from <time datetime="{{time_iso}}">{{time}}</time>.{% endblocktranslate %}</p>
{% endif %}

<p>{% blocktranslate %}These are the feedback settings for this activity:{% endblocktranslate %}</p>

<table class="settings-table">
<thead>
<tr>
<th scope="col">{% translate "Feedback" %}</th>
<th scope="col">{% translate "Available from" %}</th>
</tr>
</thead>
<tbody>
{% for label, when in exam_info %}
<tr>
<td scope="row">{{label}}</td>
<td>
{% if when == 'always' %}{% translate "The start of the attempt" %}{% endif %}
{% if when == 'oncompletion' %}{% translate "Immediately after finishing" %}{% endif %}
{% if when == 'inreview' %}{% translate "When full review is allowed" %}{% endif %}
{% if when == 'never' %}{% translate "Never" %}{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</main>

{% include "numbas_lti/footer.html" %}
Expand Down
12 changes: 5 additions & 7 deletions numbas_lti/views/attempt.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,11 @@ def dispatch(self,request,*args,**kwargs):
def get_context_data(self,*args,**kwargs):
context = super(ShowAttemptsView,self).get_context_data(*args,**kwargs)

context['resource'] = self.request.resource
resource = self.request.resource

context['resource'] = resource
context['can_start_new_attempt'] = self.request.resource.can_start_new_attempt(self.request.user)
context['exam_info'] = resource.exam.get_feedback_settings(completed=False,review_allowed=False)

return context

Expand Down Expand Up @@ -251,12 +254,6 @@ def get_mode(self):
raise PermissionDenied(gettext("You're not allowed to review this attempt."))

if attempt.completed():
if not attempt.review_allowed():
if attempt.resource.allow_review_from:
template = get_template('numbas_lti/review_not_allowed.html')
raise PermissionDenied(template.render({'allow_review_from': attempt.resource.allow_review_from}))
else:
raise PermissionDenied(_("You're not allowed to review this attempt."))
return 'review'
else:
return 'normal'
Expand Down Expand Up @@ -371,6 +368,7 @@ def get_data(self):
'numbas.user_role': 'instructor' if request_is_instructor(self.request) else 'student',
'numbas.duration_extension.amount': duration_extension_amount,
'numbas.duration_extension.units': duration_extension_units,
'numbas.review_allowed': attempt.review_allowed(),
}

now = datetime.datetime.now().timestamp()
Expand Down

0 comments on commit 109d3bc

Please sign in to comment.