Displaying Distractor Rationale

Show feedback at the question and/or response level. Developer


Figure 1

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 a single answer is possible, and per-response rationale can be used for question types 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 single rationale when a single answer choice is provided, and a rationale for each incorrect response when the question contains multiple possible answers. 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.

Note This tutorial assumes you are familiar with the Learnosity Items API, PHP, and jQuery.

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 PHP, Java, 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
 3  include_once 'config.php';
 5  use LearnositySdkRequestInit;
 6  use LearnositySdkUtilsUuid;
 8  $request = [
 9    'user_id'        => 'tutorial_student',
10    'session_id'     => Uuid::generate(),
11    'items'          => [
12        'act1','act2','act3','act4','act5','act6'
13    ],
14    'rendering_type' => 'inline',
15    'state'          => 'initial',
16    'type'           => 'submit_practice',
17    'activity_id'    => 'tutorial_activity',
18    'name'           => 'Distractor Rationale Example',
19    'course_id'      => 'tutorial_course',
20    'config'         => [
21        'renderSubmitButton'  => true
22    ]
23  ];
25  $Init = new Init('items', $security, $consumer_secret,
26  $signedRequest = $Init->generate();
28  ?>

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)
  • state: typically initial for first use, or resume when saving and continuing prior to submission.
  • 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
  • course id: name of course in which the assessment is used (required for submit practice)
  • 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-->
 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>
 62      <!--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 68 through 109. When the API is ready, the loop begun in line 69 walks through every question in the assessment. For each, the API provides a reference to the question and it’s response ID.

62  <script src="js/vendor/jquery.min.js"></script>
63  <script src="//items.learnosity.com/"></script>
64  <script>
66    var eventOptions = {
68      readyListener: function () {
69        $.each(itemsApp.questions(), function(responseID, question) {
71          question.on("validated", function() {
72            if (question.isValid()) { return; }
74            var outputHTML = "";
76            if (question.getResponse().type != "array") {
77              outputHTML = "<span>" + question.getMetadata().distractor_rationale + "</span>";
78            } else {
79              var map = question.mapValidationMetadata("distractor_rationale_response_level");
81              $.each(map.incorrect, function (i, data) {
82                outputHTML += "<li>" + data.metadata + "</li>";
83              });
84              if (outputHTML != "") { outputHTML = "<ul>" + outputHTML + "</ul>"; }
85            }
87            if (outputHTML == "") { outputHTML = "Have you answered all possible responses?"; }

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 71. 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 72 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 74.

Next, in line 76, we get the student response using the getResponse() method, and check the response type to see if it’s an array. If not, we know the question has a single correct answer, and we use the getMetadata() method in line 77 to place the question’s general distractor rationale value into a span for later display.

If the response type is an array, we use the mapValidationMetadata() method, in line 79, to populate the map variable with the question’s per response distractor rationale. This 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 81 through 83, 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 84

We’ve already left the event handler in line 72 if the response is correct, and we’ve only worked through the incorrect responses in the preceding loop. Therefore, if only unattempted responses remain, the outputHTML variable will be empty and we can create a prompt string in line 87 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 response ID for each question, we can use that to target or create a container for the rationale inside each response parent. Line 89 checks to see if the container has already been created. If so, line 90 populates the container and fades it in. If not, line 92 appends a div with the appropriate class and content and appends it to the response.

 89            if ($("#" + responseID + "_distractor").length) {
 90                $("#" + responseID + "_distractor").html(outputHTML).fadeIn();
 91            } else {
 92                $("#" + responseID).append('<div id="' + responseID +
 93                  '_distractor" class="distractor">' + outputHTML + '</div>');
 94            }
 95            MathJax.Hub.Queue([
                   "Typeset", MathJax.Hub, responseID
 97          });
 99          question.on("changed", function (responseID, question) {
100            $("#" + this.getQuestion().response_id + "_distractor").fadeOut();
101          });
103        });
105      }
106    }
108    var itemsApp = LearnosityItems.init(<?php echo $signedRequest; ?>,
109        eventOptions);
110  </script>

As a bonus, to account for any possible LaTeX or MathML that may have been added to the distractor rationale, line 94 uses the MathJax JavaScript library (delivered with the Learnosity APIs) to render the final output.

Finally, we bind another event to each question in lines 98 through 100 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 107.

The Resulting Assessment

The following figures show portions of the finished report. Single-answer questions show a simple string, multiple choice answers show a list item for each incorrect response, partially correct responses prompt for additional information, and correct answers show no distractor rationale at all.

Figure 2: Single Answer Rationale

Figure 3: Multiple Choice Answer Rationale

Figure 4: Partially Correct Answer Rationale

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.