com.pearson.pdn.learningstudio.exams.ExamService.java Source code

Java tutorial

Introduction

Here is the source code for com.pearson.pdn.learningstudio.exams.ExamService.java

Source

/*
 * LearningStudio RESTful API Libraries 
 * These libraries make it easier to use the LearningStudio Course APIs.
 * Full Documentation is provided with the library. 
 * 
 * Need Help or Have Questions? 
 * Please use the PDN Developer Community at https://community.pdn.pearson.com
 *
 * @category   LearningStudio Course APIs
 * @author     Wes Williams <wes.williams@pearson.com>
 * @author     Pearson Developer Services Team <apisupport@pearson.com>
 * @copyright  2014 Pearson Education Inc.
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache 2.0
 * @version    1.0
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.pearson.pdn.learningstudio.exams;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.pearson.pdn.learningstudio.core.AbstractService;
import com.pearson.pdn.learningstudio.core.Response;
import com.pearson.pdn.learningstudio.core.ResponseStatus;
import com.pearson.pdn.learningstudio.oauth.OAuthServiceFactory;

/**
 * Service for interacting with the exams module of LearningStudio
 *
 */
public class ExamService extends AbstractService {

    // RELATION CONSTANTS
    private final static String RELS_USER_COURSE_EXAM = "https://api.learningstudio.com/rels/user/course/exam";

    // PATH CONSTANTS
    private final static String PATH_USERS_COURSES_ITEMS = "/users/%s/courses/%s/items";
    private final static String PATH_USERS_COURSES_EXAMS = "/users/%s/courses/%s/exams";
    private final static String PATH_USERS_COURSES_EXAMS_ = "/users/%s/courses/%s/exams/%s";
    private final static String PATH_USERS_COURSES_EXAMS_ATTEMPTS = "/users/%s/courses/%s/exams/%s/attempts";
    private final static String PATH_USERS_COURSES_EXAMS_ATTEMPTS_ = "/users/%s/courses/%s/exams/%s/attempts/%s";
    private final static String PATH_USERS_COURSES_EXAMS_ATTEMPTS_SUMMARY = "/users/%s/courses/%s/exams/%s/attempts/%s/summary";
    private final static String PATH_USERS_COURSES_EXAMDETAILS = "/users/%s/courses/%s/examDetails";
    private final static String PATH_USERS_COURSES_EXAMDETAILS_ = "/users/%s/courses/%s/examDetails/%s";
    private final static String PATH_COURSES_EXAMSCHEDULES = "/courses/%s/examSchedules";

    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS = "/users/%s/courses/%s/exams/%s/sections";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_QUESTIONS = "/users/%s/courses/%s/exams/%s/sections/%s/questions";

    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_TRUEFALSE_ = "/users/%s/courses/%s/exams/%s/sections/%s/trueFalseQuestions/%s";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_TRUEFALSE_CHOICES = "/users/%s/courses/%s/exams/%s/sections/%s/trueFalseQuestions/%s/choices";

    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MULTIPLECHOICE_ = "/users/%s/courses/%s/exams/%s/sections/%s/multipleChoiceQuestions/%s";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MULTIPLECHOICE_CHOICES = "/users/%s/courses/%s/exams/%s/sections/%s/multipleChoiceQuestions/%s/choices";

    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MANYMULTIPLECHOICE_ = "/users/%s/courses/%s/exams/%s/sections/%s/manyMultipleChoiceQuestions/%s";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MANYMULTIPLECHOICE_CHOICES = "/users/%s/courses/%s/exams/%s/sections/%s/manyMultipleChoiceQuestions/%s/choices";

    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MATCHING_ = "/users/%s/courses/%s/exams/%s/sections/%s/matchingQuestions/%s";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MATCHING_PREMISES = "/users/%s/courses/%s/exams/%s/sections/%s/matchingQuestions/%s/premises";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_MATCHING_CHOICES = "/users/%s/courses/%s/exams/%s/sections/%s/matchingQuestions/%s/choices";

    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_SHORT_ = "/users/%s/courses/%s/exams/%s/sections/%s/shortQuestions/%s";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_ESSAY_ = "/users/%s/courses/%s/exams/%s/sections/%s/essayQuestions/%s";
    private final static String PATH_USERS_COURSES_EXAMS_SECTIONS_FILLINTHEBLANK_ = "/users/%s/courses/%s/exams/%s/sections/%s/fillintheblankQuestions/%s";

    private final static String PATH_USERS_COURSES_EXAMS_ATTEMPTS_ANSWERS = "/users/%s/courses/%s/exams/%s/attempts/%s/answers";
    private final static String PATH_USERS_COURSES_EXAMS_ATTEMPTS_ANSWERS_ = "/users/%s/courses/%s/exams/%s/attempts/%s/answers/%s";

    // EXAM Constants
    private final static String PEARSON_EXAM_TOKEN = "Pearson-Exam-Token";
    private final static String PEARSON_EXAM_PASSWORD = "Pearson-Exam-Password";

    private enum QuestionType {
        TRUE_FALSE("trueFalse"), MULTIPLE_CHOICE("multipleChoice"), MANY_MULTIPLE_CHOICE(
                "manyMultipleChoice"), MATCHING(
                        "matching"), SHORT_ANSWER("short"), ESSAY("essay"), FILL_IN_THE_BLANK("fillInTheBlank");

        private String value;

        private QuestionType(String value) {
            this.value = value;
        }

        public String value() {
            return this.value;
        }

        public static QuestionType matchesValue(String value) {
            for (QuestionType qt : QuestionType.values()) {
                if (qt.value.equalsIgnoreCase(value)) {
                    return qt;
                }
            }

            return null;
        }
    }

    /**
     * Constructs a new ExamService
     * 
     * @param oauthServiceFactory Service provider for OAuth operations
     */
    public ExamService(OAuthServiceFactory oauthServiceFactory) {
        super(oauthServiceFactory);
    }

    /**
     * Provides name of service for identification purposes
     * 
     * @return Unique identifier for service
     */
    protected String getServiceIdentifier() {
        return "LS-Library-Exam-Java-V1";
    }

    /**
     * Retrieve all of a user's exams for a course with
     * GET /users/{userId}/courses/{courses}/items
     * using OAuth1 or OAuth2 as a student or teacher
     * 
     * @param userId   ID of the user
     * @param courseId   ID of course
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getAllExamItems(String userId, String courseId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_ITEMS, userId, courseId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        if (response.isError()) {
            return response;
        }

        String courseItemsJson = response.getContent();
        JsonObject json = this.jsonParser.parse(courseItemsJson).getAsJsonObject();
        JsonArray items = json.get("items").getAsJsonArray();
        JsonArray exams = new JsonArray();

        for (Iterator<JsonElement> itemIter = items.iterator(); itemIter.hasNext();) {
            JsonObject item = itemIter.next().getAsJsonObject();
            JsonArray links = item.get("links").getAsJsonArray();

            for (Iterator<JsonElement> linkIter = links.iterator(); linkIter.hasNext();) {
                JsonObject link = linkIter.next().getAsJsonObject();
                if (RELS_USER_COURSE_EXAM.equals(link.get("rel").getAsString())) {
                    exams.add(item);
                    break;
                }
            }
        }

        JsonObject examItems = new JsonObject();
        examItems.add("items", exams);

        response.setContent(this.gson.toJson(examItems));
        return response;
    }

    /**
     * Retrieve all of a user's existing exams for a course with 
     * GET /users/{userId}/courses/{courseId}/exams
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExistingExams(String userId, String courseId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS, userId, courseId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve details for all exams for a course with 
     * GET /users/{userId}/courses/{courseId}/exams
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamDetails(String userId, String courseId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMDETAILS, userId, courseId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve details for all exams for a course with 
     * GET /users/{userId}/courses/{courseId}/exams
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamDetails(String userId, String courseId, String examId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMDETAILS_, userId, courseId, examId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve exam schedules for a course with 
     * GET /courses/{courseId}/examschedules
     * using OAuth1 or OAuth2 as a teacher, teaching assistant or administrator
     * 
     * @param courseId   ID of the course
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamSchedules(String courseId) throws IOException {
        String relativeUrl = String.format(PATH_COURSES_EXAMSCHEDULES, courseId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve exam schedules for a course with 
     * POST /courses/{courseId}/examschedules
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param courseId   ID of the course
     * @return Response object with details of status and content
     * @throws IOException
     */
    /*   public Response createExamSchedules(String courseId, String examSchedules) throws IOException {
          String relativeUrl = String.format(PATH_COURSES_EXAMSCHEDULES, courseId);
          Response response = doMethod(HttpMethod.POST,relativeUrl,examSchedules);
          return response;
       }
    */

    /**
     * Retrieve exam schedules for a course with 
     * POST /courses/{courseId}/examschedules
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param courseId   ID of the course
     * @return Response object with details of status and content
     * @throws IOException
     */
    /*   public Response updateExamSchedules(String courseId, String examSchedules) throws IOException {
          String relativeUrl = String.format(PATH_COURSES_EXAMSCHEDULES, courseId);
          Response response = doMethod(HttpMethod.PUT,relativeUrl,examSchedules);
          return response;
       }
    */

    /**
     * Retrieve a user's exam for a course with
     * GET /users/{userId}/courses/{courseId}/exams/{examId}
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course   
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExistingExam(String userId, String courseId, String examId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_, userId, courseId, examId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Creates an exam for a user in a course with 
     * POST /users/userId/courses/{courseId}/exams/{examId}
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response createUserExam(String userId, String courseId, String examId) throws IOException {
        Response response = getExistingExam(userId, courseId, examId);
        if (response.getStatusCode() == ResponseStatus.NOT_FOUND.code()) {
            String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_, userId, courseId, examId);
            response = doMethod(HttpMethod.POST, relativeUrl, NO_CONTENT);
        }
        return response;
    }

    /**
     * Updates an exam for a user in a course with 
     * PUT /users/userId/courses/{courseId}/exams/{examId}
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    /*   public Response updateUserExam(String userId, String courseId, String examId) throws IOException {
          Response response = getExistingExam(userId,courseId,examId);
          if(response.getStatusCode() == ResponseStatus.OK.code()) {
     String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_,userId,courseId,examId);
     **FIX** response = doMethod(HttpMethod.PUT,relativeUrl,"{\"exam\":{\"id\":53967560,\"title\":\"test\",\"description\":\"\" }");
          }
          return response;
       }
    */

    /**
     * Delete a users's exam in a course with 
     * DELETE /users/{userId}/courses/{courseId}/exams/{examId}
     * using OAuth1 or OAuth2 as a teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course   
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response deleteUserExam(String userId, String courseId, String examId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_, userId, courseId, examId);
        Response response = doMethod(HttpMethod.DELETE, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Create an exam attempt for a user in a course with 
     * POST /users/{userId}/courses/{courseId}/exams/{examId}/attempts
     * using OAuth1 or OAuth2 as student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response createExamAttempt(String userId, String courseId, String examId) throws IOException {

        return createExamAttempt(userId, courseId, examId, null);
    }

    /**
     * Create an exam attempt for a user in a course with 
     * POST /users/{userId}/courses/{courseId}/exams/{examId}/attempts
     * using OAuth1 or OAuth2
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @param examPassword   Optional password from instructor
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response createExamAttempt(String userId, String courseId, String examId, String examPassword)
            throws IOException {

        Map<String, String> examHeaders = null;
        if (examPassword != null) {
            examHeaders = new HashMap<String, String>();
            examHeaders.put(PEARSON_EXAM_PASSWORD, examPassword);
        }

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS, userId, courseId, examId);
        Response response = doMethod(examHeaders, HttpMethod.POST, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve a users's attempt of an exam in a course with 
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/attempts
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamAttempts(String userId, String courseId, String examId) throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS, userId, courseId, examId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve a user's attempt of an exam in a course with 
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attemptId}
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @param attemptId   ID of the exam attempt
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamAttempt(String userId, String courseId, String examId, String attemptId)
            throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_, userId, courseId, examId, attemptId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     *  Retrieves and filters a user's current attempt of an exam in a course with 
     *  GET /users/{userId}/courses/{courseId}/exams/{examId}/attempts
     *  using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getCurrentExamAttempt(String userId, String courseId, String examId) throws IOException {
        Response response = getExamAttempts(userId, courseId, examId);
        if (response.isError()) {
            return response;
        }
        String attemptsJson = response.getContent();
        JsonElement json = jsonParser.parse(attemptsJson);
        JsonArray attempts = json.getAsJsonObject().get("attempts").getAsJsonArray();
        JsonObject currentAttempt = null;

        for (Iterator<JsonElement> attemptsIterator = attempts.iterator(); attemptsIterator.hasNext();) {
            JsonObject attempt = attemptsIterator.next().getAsJsonObject();
            if (!attempt.get("isCompleted").getAsBoolean()) {
                currentAttempt = attempt;
                break;
            }
        }

        if (currentAttempt != null) {
            JsonObject attempt = new JsonObject();
            attempt.add("attempt", currentAttempt);

            response.setContent(gson.toJson(attempt));
        } else {
            response.setStatusCode(ResponseStatus.NOT_FOUND.code());
            response.setContent(null);
        }

        return response;
    }

    /**
     * Retrieve a summary of a user's attempt of an exam in a course with 
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attempdId}/summary
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param attemptId   ID of the attempt
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamAttemptSummary(String userId, String courseId, String examId, String attemptId)
            throws IOException {
        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_SUMMARY, userId, courseId, examId,
                attemptId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve a user's current attempt or create new attempt of an exam in a course with
     * getCurrentExamAttempt and createExamAttempt
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response startExamAttempt(String userId, String courseId, String examId) throws IOException {
        return startExamAttempt(userId, courseId, examId, null);
    }

    /**
     * Retrieve a user's current attempt or create new attempt of an exam in a course with
     * getCurrentExamAttempt and createExamAttempt
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @param examPassword   Optional password from instructor
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response startExamAttempt(String userId, String courseId, String examId, String examPassword)
            throws IOException {
        Response response = getCurrentExamAttempt(userId, courseId, examId);
        if (response.getStatusCode() == ResponseStatus.NOT_FOUND.code()) {
            response = createExamAttempt(userId, courseId, examId, examPassword);
        }
        return response;
    }

    /**
     * Retrieve sections of an user's exam in a course with 
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/sections
     * using OAuth1 or OAuth2 as a student, teacher, teaching assistant or administrator
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamSections(String userId, String courseId, String examId) throws IOException {

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS, userId, courseId, examId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        return response;
    }

    /**
     * Retrieve details of questions for a section of a user's exam in a course with 
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/sections/{sectionId}/questions
     * and getExamSectionQuestion
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @param sectionId   ID of the section on the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamSectionQuestions(String userId, String courseId, String examId, String sectionId)
            throws IOException {

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_QUESTIONS, userId, courseId, examId,
                sectionId);
        Response response = doMethod(HttpMethod.GET, relativeUrl, NO_CONTENT);
        if (response.isError()) {
            return response;
        }

        String sectionQuestionJson = response.getContent();
        JsonObject json = this.jsonParser.parse(sectionQuestionJson).getAsJsonObject();
        JsonArray questions = json.get("questions").getAsJsonArray();
        JsonArray sectionQuestions = new JsonArray();

        for (Iterator<JsonElement> questionIter = questions.iterator(); questionIter.hasNext();) {
            JsonObject question = questionIter.next().getAsJsonObject();
            String questionType = question.get("type").getAsString();
            String questionId = question.get("id").getAsString();
            Response questionResponse = getExamSectionQuestion(userId, courseId, examId, sectionId, questionType,
                    questionId);
            if (questionResponse.isError()) {
                return questionResponse;
            }

            JsonObject sectionQuestion = this.jsonParser.parse(questionResponse.getContent()).getAsJsonObject();
            sectionQuestion.addProperty("id", questionId);
            sectionQuestion.addProperty("type", questionType);
            sectionQuestion.addProperty("pointsPossible", question.get("pointsPossible").getAsString());
            sectionQuestions.add(sectionQuestion);
        }

        JsonObject sectionQuestionsWrapper = new JsonObject();
        sectionQuestionsWrapper.add("questions", sectionQuestions);

        response.setContent(this.gson.toJson(sectionQuestionsWrapper));
        return response;
    }

    /**
     * Retrieve details of a question for a section of a user's exam in a course with
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/sections/{sectionId}/{questionType}Questions/{questionId}
     * and GET /users/{userId}/courses/{courseId}/exams/{examId}/sections/{sectionId}/{questionType}Questions/{questionId}/choices
     * and GET /users/{userId}/courses/{courseId}/exams/{examId}/sections/{sectionId}/{questionType}Questions/{questionId}/premises
     * using OAuth2 as a student
     *   
     * @param userId   ID of the user
     * @param examId   ID of the exam
     * @param sectionId   ID of the section
     * @param questionType   Type of question
     * @param questionId   ID of the question
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getExamSectionQuestion(String userId, String courseId, String examId, String sectionId,
            String questionType, String questionId) throws IOException {

        QuestionType type = QuestionType.matchesValue(questionType);
        if (type == null) {
            throw new RuntimeException("Invalid Question Type");
        }

        Response response = getCurrentExamAttempt(userId, courseId, examId);
        if (response.isError()) {
            return response;
        }
        Map<String, String> extraHeaders = getExamHeaders(response);

        String questionRelativeUrl = null;
        String choicesRelativeUrl = null;
        String premisesRelativeUrl = null;

        switch (type) {
        case TRUE_FALSE:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_TRUEFALSE_, userId, courseId,
                    examId, sectionId, questionId);
            choicesRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_TRUEFALSE_CHOICES, userId,
                    courseId, examId, sectionId, questionId);
            break;
        case MULTIPLE_CHOICE:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MULTIPLECHOICE_, userId, courseId,
                    examId, sectionId, questionId);
            choicesRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MULTIPLECHOICE_CHOICES, userId,
                    courseId, examId, sectionId, questionId);
            break;
        case MANY_MULTIPLE_CHOICE:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MANYMULTIPLECHOICE_, userId,
                    courseId, examId, sectionId, questionId);
            choicesRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MANYMULTIPLECHOICE_CHOICES, userId,
                    courseId, examId, sectionId, questionId);
            break;
        case MATCHING:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MATCHING_, userId, courseId,
                    examId, sectionId, questionId);
            premisesRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MATCHING_PREMISES, userId,
                    courseId, examId, sectionId, questionId);
            choicesRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_MATCHING_CHOICES, userId, courseId,
                    examId, sectionId, questionId);
            break;
        case SHORT_ANSWER:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_SHORT_, userId, courseId, examId,
                    sectionId, questionId);
            break;
        case ESSAY:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_ESSAY_, userId, courseId, examId,
                    sectionId, questionId);
            break;
        case FILL_IN_THE_BLANK:
            questionRelativeUrl = String.format(PATH_USERS_COURSES_EXAMS_SECTIONS_FILLINTHEBLANK_, userId, courseId,
                    examId, sectionId, questionId);
            break;
        }

        JsonObject details = new JsonObject();

        response = doMethod(extraHeaders, HttpMethod.GET, questionRelativeUrl, NO_CONTENT);
        if (response.isError()) {
            return response;
        }

        JsonObject question = this.jsonParser.parse(response.getContent()).getAsJsonObject();
        details.add("question", question.get(type.value() + "Question").getAsJsonObject());

        if (choicesRelativeUrl != null) {
            response = doMethod(extraHeaders, HttpMethod.GET, choicesRelativeUrl, NO_CONTENT);
            if (response.isError()) {
                return response;
            }

            JsonObject choices = this.jsonParser.parse(response.getContent()).getAsJsonObject();
            details.add("choices", choices.get("choices").getAsJsonArray());
        }

        if (premisesRelativeUrl != null) {
            response = doMethod(extraHeaders, HttpMethod.GET, premisesRelativeUrl, NO_CONTENT);
            if (response.isError()) {
                return response;
            }

            JsonObject choices = this.jsonParser.parse(response.getContent()).getAsJsonObject();
            details.add("premises", choices.get("premises").getAsJsonArray());
        }

        response.setContent(this.gson.toJson(details));
        return response;
    }

    /**
     * Updates a user's answer for a question on a specific attempt of an exam in a course with
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attemptId}/answers/{answerId}
     * POST /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attemptId}/answers
     * PUT /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attemptId}/answers/{answerId}
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam   
     * @param attemptId   ID of the attempt on the exam
     * @param questionId   ID of the question on the exam
     * @param answer   Answer to the question on the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response answerQuestion(String userId, String courseId, String examId, String attemptId,
            String questionId, String answer) throws IOException {
        Response response = getExamAttempt(userId, courseId, examId, attemptId);
        if (response.isError()) {
            return response;
        }
        Map<String, String> extraHeaders = getExamHeaders(response);

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_ANSWERS_, userId, courseId, examId,
                attemptId, questionId);
        response = doMethod(extraHeaders, HttpMethod.GET, relativeUrl, NO_CONTENT);
        if (response.isError()) {
            if (response.getStatusCode() == ResponseStatus.NOT_FOUND.code()) {
                relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_ANSWERS, userId, courseId, examId,
                        attemptId);
                response = doMethod(extraHeaders, HttpMethod.POST, relativeUrl, answer);
            }
        } else {
            response = doMethod(extraHeaders, HttpMethod.PUT, relativeUrl, answer);
        }

        return response;
    }

    /**
     * Retrieve a user's answer for a question on a specific attempt of an exam in a course with
     * GET /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attemptId}/answers/{answerId}
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam   
     * @param attemptId   ID of the attempt on the exam
     * @param questionId   ID of the question on the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response getQuestionAnswer(String userId, String courseId, String examId, String attemptId,
            String questionId) throws IOException {
        Response response = getExamAttempt(userId, courseId, examId, attemptId);
        if (response.isError()) {
            return response;
        }
        Map<String, String> extraHeaders = getExamHeaders(response);

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_ANSWERS_, userId, courseId, examId,
                attemptId, questionId);
        response = doMethod(extraHeaders, HttpMethod.GET, relativeUrl, NO_CONTENT);

        return response;
    }

    /**
     * Delete a user's answer for a question on a specific attempt of an exam in a course with
     * DELETE /users/{userId}/courses/{courseId}/exams/{examId}/attempts/{attemptId}/answers/{answerId}
     * using OAuth2 as a student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam   
     * @param attemptId   ID of the attempt on the exam
     * @param questionId   ID of the question on the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response deleteQuestionAnswer(String userId, String courseId, String examId, String attemptId,
            String questionId) throws IOException {
        Response response = getExamAttempt(userId, courseId, examId, attemptId);
        if (response.isError()) {
            return response;
        }
        Map<String, String> extraHeaders = getExamHeaders(response);

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_ANSWERS_, userId, courseId, examId,
                attemptId, questionId);
        response = doMethod(extraHeaders, HttpMethod.DELETE, relativeUrl, NO_CONTENT);

        return response;
    }

    /**
     * Updates a user's attempt of an exam in a course to complete with 
     * PUT /users/{userId}/courses/{examId}/exams/{examId}/attempts/{attemptId}
     * using OAuth2 as student
     * 
     * @param userId   ID of the user
     * @param courseId   ID of the course
     * @param examId   ID of the exam
     * @param attemptId   ID of the attempt on the exam
     * @return Response object with details of status and content
     * @throws IOException
     */
    public Response completeExamAttempt(String userId, String courseId, String examId, String attemptId)
            throws IOException {
        Response response = getExamAttempt(userId, courseId, examId, attemptId);
        if (response.isError()) {
            return response;
        }
        Map<String, String> extraHeaders = getExamHeaders(response);

        String relativeUrl = String.format(PATH_USERS_COURSES_EXAMS_ATTEMPTS_, userId, courseId, examId, attemptId);
        String json = "{ \"attempts\" : { \"isCompleted\" : true } }";
        response = doMethod(extraHeaders, HttpMethod.PUT, relativeUrl, json);
        return response;
    }

    private Map<String, String> getExamHeaders(Response response) throws IOException {
        JsonObject attempt = this.jsonParser.parse(response.getContent()).getAsJsonObject();
        attempt = attempt.get("attempt").getAsJsonObject();
        Map<String, String> extraHeaders = new HashMap<String, String>();
        extraHeaders.put(PEARSON_EXAM_TOKEN, attempt.get("pearsonExamToken").getAsString());
        return extraHeaders;
    }

}