Submit a request
Submit a request

Tutorial 202 - Displaying Distractor Rationale

Learn how to show feedback at the Question and/or response level. 

 

Introduction 

Each Learnosity Question type can include distractor rationale—an explanation for why a student response is incorrect—as part of its Question data. In both the Author site and embedded Question Editor, this information can be entered in the metadata section, as shown in Figure 1. A general field is available for cases where only one Rationale needs to be displayed (typically for Questions where a single answer is possible), and per-response Rationale can be used in cases where Authors would want to display a different Rationale for each incorrect answer (typically for Questions where more than one answer choice is provided).

How optional information like feedback, hints, or sample answers is displayed can vary significantly from developer to developer, and even from case to case. As such, Learnosity provides the data infrastructure and APIs needed to parse this information at runtime, without dictating how it should be shown to the student.

This tutorial covers one possible approach to this task. The finished code will show no additional explanation if the answer is correct, a rationale for each incorrect response when the Question contains Distractor Rationale at Response Level, or a single rationale when the Question contains a global Distractor Rationale. In addition, in cases where more than one answer is required, but only a single correct answer has been supplied, the student will be prompted to review the response for completeness. 

This code is created in a context where Authors always fill either the global Distractor Rationale or the Distractor Rationale at Response Level.

Note: This tutorial assumes you are familiar with the Learnosity Items APIPHP, and jQuery.
 
QE-Distractor_Rationale.png

Figure 1

 

Using the Learnosity SDK

Security and authentication required to use Learnosity APIs is best handled by our SDK. In Tutorial 102, we created a simple config file that includes your customer key, customer secret, and a whitelisted domain. It also includes the SDK autoloader to simplify using dependencies and reduce related errors. The autoloader will try to load undefined classes or interfaces, before throwing an error. The SDK is available in PHPJava, and C# .NET, and this tutorial uses the PHP version.

The authentication information in the config file is combined with the assessment request data below and signed by the SDK using an SHA256 hashing algorithm. Learnosity servers sign the incoming request the same way, and matching signatures ensure that the data has not been tampered with during transmission.

Note This tutorial uses demo values for the consumer key and secret. In production, you must use your own consumer key and secret.
 

Signing the Assessment Request

The signing code, shown below, first includes the aforementioned config file that contains the customer authentication data, and creates aliases for the Init and Uuid classes to simplify their use.

 1  <?php
 2
 3  include_once 'config.php';
 4
 5  use LearnositySdkRequestInit;
 6  use LearnositySdkUtilsUuid;
 7
 8  $request = [
 9    'user_id'        => 'student_1234',
10    'session_id'     => Uuid::generate(),
11    'items'          => [
12        'act1','act2','act3','act4','act5','act6'
13    ],
14    'rendering_type' => 'inline',
15    'type'           => 'submit_practice',
16    'activity_id'    => 'tutorial_activity',
17    'name'           => 'Distractor Rationale Example',
18    'config'         => [
19        'renderSubmitButton'  => true
20    ]
21  ];
22
23  $Init = new Init('items', $security, $consumer_secret,
        $request);
24  $signedRequest = $Init->generate();
25  ?>

The Request Object then includes the necessary information to create the assessment:

  • user id: unique student id
  • session id: unique id for each new session (generated for this tutorial by the SDK’s UUID class), or existing id (retrieved from your own system) when resuming a session
  • items: array of Items included in the assessment
  • rendering type: assess (uses Learnosity’s Assess UI to render assessment) or inline (rendering each item in its own DOM element giving developer full control over layout)
  • type: local practice (validated locally) or submit practice (submitted to Learnosity servers to support additional features such as reporting)
  • activity id: reference used to group all sessions of this assessment together to support additional features such as reporting
  • name: display name for assessment
  • config: object used for optional config settings. In this case, we will be rendering the submit button. This is handled automatically by the Assess rendering type, but is optional for inline to allow developers to use their own submit button, if desired.

This information is then combined with the authentication data and the requested API (line 23) to create a signed request (line 24).

 

Creating the Host Page 

The HTML used in this tutorial to render the assessment is very basic. Shown below, it starts with a few simple styles to clearly separate each item from the page background, and each distractor rationale from its corresponding Item. (As the distractor rationale will only be displayed when responses are incorrect or incomplete, a red alert/incorrect theme is used.) The body then includes a header, a DOM element for each Item in the assessment, and a submit button.

 26  <!--php goes here-->
 27
 28  <!DOCTYPE html>
 29  <html lang="en">
 30    <head>
 31      <meta charset="utf-8">
 32      <title>Distractor Rationale</title>
 33      <style>
 34        body {
 35          background-color:#F2F2F2;
 36        }
 37        .learnosity-item {
 38          background-color:#FFFFFF;
 39          padding:20px;
 40          margin:20px 0px;
 41        }
 42        .distractor {
 43          margin-top:5px;
 44          padding:10px;
 45          background-color:#EBCCD1;
 46          color:#b94a48;
 47        }
 48      </style>
 49    </head>
 50    <body>
 51      <h1>Distractor Rationale Tutorial</h1>
 52      <div style="width:750px;">
 53        <span class="learnosity-item" data-reference="act1"></span>
 54        <span class="learnosity-item" data-reference="act2"></span>
 55        <span class="learnosity-item" data-reference="act3"></span>
 56        <span class="learnosity-item" data-reference="act4"></span>
 57        <span class="learnosity-item" data-reference="act5"></span>
 58        <span class="learnosity-item" data-reference="act6"></span>
 59        <span class="learnosity-submit-button"></span>
 60      </div>
 61      <!--scripts go here-->
115    </body>
116  </html>

 

Initializing the API

The last thing we need to do is initialize the Items API. For this tutorial, we’ll be using Question-level events and public methods to display the distractor rationale. We’ll also use jQuery, added in line 62, to help along the way.

When the Items API is initialized, we pass to it the aforementioned signed request and an events object. This object contains a listener for the ready event fired when the Items API is ready, as well as additional event listeners, if desired. Our ready listener is defined in lines 67 through 108. When the API is ready, the loop begun in line 68 walks through every Question in the assessment. For each, the API provides a reference to the Question and its response ID.

62      <script src="js/vendor/jquery.min.js"></script>
63      <script src="//items.learnosity.com/"></script>
64      <script>
65
66        var eventOptions = {
67          readyListener: function () {
68            $.each(itemsApp.questions(), function(responseID, question) {
69              question.on("validated", function() {
70                if (question.isValid()) { return; }
71
72                var outputHTML = "";
73
74                if (question.mapValidationMetadata('distractor_rationale_response_level') != false) {
75                  map = question.mapValidationMetadata('distractor_rationale_response_level');
76 $.each(map.incorrect, function (i, data){
77 /* Each item in the `map.incorrect` array is an object that contains a `value` property that
78 holds the response value; an `index` property that refers to the shared index of both the
79 response area and the metadata; and a `metadata` property containing the metadata value */
80
81 var distractor = data.metadata;
82
83 outputHTML = '<li>' + distractor + '</li>';
84 });
85 if(outputHTML) {
86 outputHTML = '<ul>' + outputHTML + '</ul>';
87 }
88 } else if (question.getMetadata().distractor_rationale) {
89                 outputHTML = question.getMetadata().distractor_rationale;
90              }
91
92              if(!outputHTML) {
93                 outputHTML = 'Have you answered all possible responses?';
94               }
95

 

Parsing Rationale When a Question is Validated

The first thing we do is bind a validated event to each Question using the on() public method in line 69. This will trigger each time a Question is validated. In the case of this tutorial, this will occur when the student clicks on the Check Answers button. When the validated event fires, we Immediately check the isValid() public method in line 70 and exit the function if the response is correct. If the response is not valid, we begin to create the outputHTML display string in line 72.

Next, in line 74, we call question.mapValidationMetadata('distractor_rationale_response_level'), which will return false if the question does not contain Distractor Rationale at Response Level.

If not false, we use it to populate the map variable with the Question’s per response distractor rationale. The mapValidationMetadata() method will create an object for all correct, incorrect, and unattempted responses. The resulting object contains the value of the response, an index that matches the distractor with its corresponding rationale, and the metadata string of that rationale. In our case, we only want the incorrect answers, so we iterate through the map.incorrect array in lines 76 to 84, wrapping each rationale in a list item. If any rationales are found, we wrap the list Item(s) in an unordered list tag in line 86.

If, in the opposite, the mapValidationMetadata() method returned false for'distractor_rationale_response_level', we know that this question does not contain Distractor Rationale at Response Level, and therefore we’ll use the getMetadata() method in line 89 to get the unique Distractor Rationale for this question.

We’ve already left the event handler in line 70 if the response is correct, and we’ve only worked through the incorrect responses in the preceding loop. Therefore, if the student only selected one correct answer from 2 or more expected, the outputHTML variable will still be empty, and we can create a prompt string in line 93 to tell the student that more answers are expected.

Displaying the Rationale

All that remains is handling the display of the rationale. Because the Items API provided us with the responseID for each question, we can use that to target or create a container for the rationale inside each response parent. Line 96 checks to see if the container has already been created. If so, line 97 populates the container and fades it in. If not, line 99 appends a div with the appropriate class and content and appends it to the response.

 

 96                if ($("#" + responseID + "_distractor").length) {
 97                  $("#" + responseID + "_distractor").html(outputHTML).fadeIn();
 98                } else {
 99                  $("#" + responseID).append('<div id="' + responseID +
                       '_distractor" class="distractor">' + outputHTML + '</div>');
100                }
101
102 //renderMath() Renders all Latex or MathML elements on the page with MathJax.
103 itemsApp.questionsApp().renderMath();
104
105 question.on("changed", function(responseID, question)){
106 $("#" + responseID + "_distractor").fadeout();
107 });
108
109 });
110 });
111 }
112 }
113 var itemsApp = LearnosityItems.init(<?php echo $signedRequest; ?>, eventOptions);
114 </script>
115 </body>
116 </html>

As a bonus, to account for any possible LaTeX or MathML that may have been added to the distractor rationale, line 103 uses the renderMath() function.

Finally, we bind another event to each Question in lines 105 through 107 to account for edits to student responses. Each time the student alters a response, the changed event will fire and the distractor rationale container will fade out and await a new validation.

Now that the eventOptions object is complete, we can send it, along with the signed request object, to initialize the Items API in line 113.

 

The Resulting Assessment

The following figures show portions of the finished report. Questions containing a single global Distractor Rationale show a simple string, and Questions containing Distractor Rationales at Response Level show a list of Item for each incorrect response. Partially correct responses prompt for additional information, and correct answers show no distractor rationale at all.

Single-Global-Distractor-Rationale.png

Figure 2: Single Distractor Rationale

Multiple-Distractor-Rationale-Response-Level.png

Figure 3: Multiple Distractor Rationale per Response Level

 

partially-correct-distractor-rationale.png

Figure 4: Partially Correct Answer Rationale

 

correct-answer-no-distractor.png

Figure 5: Correct Answer (No Rationale)

 

What you learned

In this tutorial you learned how to use Question-level events and public methods to show distractor rationale for incorrect and partial responses. In any Question, distractor rationale can be added at the per-Question and per response-level. Upon validation, you can map distractor rationale to correct, incorrect, and unattempted responses.

 

Was this article helpful?
1 out of 3 found this helpful

Did you arrive here by accident? If so, learn more about Learnosity by clicking here.