tds.student.web.handlers.TestShellHandler.java Source code

Java tutorial

Introduction

Here is the source code for tds.student.web.handlers.TestShellHandler.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System Copyright (c) 2014 American
 * Institutes for Research
 *
 * Distributed under the AIR Open Source License, Version 1.0 See accompanying
 * file AIR-License-1_0.txt or at http://www.smarterapp.org/documents/
 * American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
package tds.student.web.handlers;

import AIR.Common.TDSLogger.ITDSLogger;
import AIR.Common.Web.Session.HttpContext;
import AIR.Common.Web.TDSReplyCode;
import AIR.Common.data.ResponseData;
import TDS.Shared.Data.ReturnStatus;
import TDS.Shared.Exceptions.ReturnStatusException;
import TDS.Shared.Exceptions.TDSSecurityException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.HtmlUtils;
import tds.blackbox.web.handlers.TDSHandler;
import tds.exam.ExamStatusCode;
import tds.itemrenderer.data.AccLookup;
import tds.itemrenderer.data.AccProperties;
import tds.student.services.ExamCompletionService;
import tds.student.services.abstractions.IOpportunityService;
import tds.student.services.abstractions.IResponseService;
import tds.student.services.abstractions.PrintService;
import tds.student.services.data.ApprovalInfo;
import tds.student.services.data.ApprovalInfo.OpportunityApprovalStatus;
import tds.student.services.data.ItemResponse;
import tds.student.services.data.PageGroup;
import tds.student.services.data.TestOpportunity;
import tds.student.services.remote.RemoteExamineeNoteService;
import tds.student.sql.abstractions.IOpportunityRepository;
import tds.student.sql.abstractions.IResponseRepository;
import tds.student.sql.data.ClientLatency;
import tds.student.sql.data.OpportunityInstance;
import tds.student.sql.data.OpportunityStatus;
import tds.student.sql.data.OpportunityStatusChange;
import tds.student.sql.data.OpportunityStatusType;
import tds.student.sql.data.TestSegment.TestSegmentApproval;
import tds.student.sql.data.Testee;
import tds.student.sql.data.ToolUsed;
import tds.student.web.StudentContext;
import tds.student.web.TestManager;
import tds.student.web.data.TestShellAudit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * @author mpatel
 *
 */
@Controller
@Scope("prototype")
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class TestShellHandler extends TDSHandler {

    private static final Logger _logger = LoggerFactory.getLogger(TestShellHandler.class);
    @Autowired
    private IOpportunityRepository _oppRepository;
    @Autowired
    @Qualifier("integrationOpportunityService")
    private IOpportunityService _oppService;
    @Autowired
    @Qualifier("integrationResponseService")
    private IResponseService _responseService;
    @Autowired
    private IResponseRepository _responseRepository;
    @Autowired
    @Qualifier("integrationPrintService")
    private PrintService _printService;
    @Autowired
    private ITDSLogger _tdsLogger;

    private final RemoteExamineeNoteService remoteExamineeNoteService;
    private final ExamCompletionService examCompletionService;

    @Autowired
    public TestShellHandler(final RemoteExamineeNoteService remoteExamineeNoteService,
            final ExamCompletionService examCompletionService) {
        this.remoteExamineeNoteService = remoteExamineeNoteService;
        this.examCompletionService = examCompletionService;
    }

    @RequestMapping(value = "TestShell.axd/logAuditTrail")
    @ResponseBody
    public ResponseData<String> logAuditTrail(HttpServletRequest request, HttpServletResponse response) {
        try {
            // TODO Shiva: Do we have this functionality? This needs to be
            // implemented.
            // There is parseOutLatencies() originally used in completeTest().

        } catch (Exception exp) {
            exp.printStackTrace();
            _logger.error(String.format("Problem in logAuditTrail. Message: %s", exp.getMessage()));
        }
        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", "Logged");
    }

    @RequestMapping(value = "TestShell.axd/print")
    @ResponseBody
    public void Print(@RequestParam(value = "type", required = false) String type,
            @RequestParam(value = "page", required = false) int page,
            @RequestParam(value = "pageKey", required = false) UUID pageKey,
            @RequestParam(value = "params", required = false) String requestParams,
            @RequestParam(value = "position", required = false) final Integer position)
            throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();

        // get opportunity
        TestOpportunity testOpp = StudentContext.getTestOpportunity();
        AccLookup accLookup = StudentContext.getAccLookup();
        AccProperties accProps = new AccProperties(accLookup);

        // get item group for this page
        PageGroup pageToPrint = _responseService.getItemGroup(testOpp.getOppInstance(), page, null, null, true);

        if (type.equals("passage")) {
            if (accProps.isBrailleEnabled()) {
                _printService.printPassageBraille(testOpp, pageToPrint, accLookup);
            } else {
                _printService.printPassage(testOpp.getOppInstance(), pageToPrint, requestParams);
            }

        } else if (type.equals("page")) {
            if (accProps.isBrailleEnabled()) {
                _printService.printPageBraille(testOpp, pageToPrint, accLookup);
            } else {
                _printService.printPage(testOpp.getOppInstance(), pageToPrint, requestParams);
            }

        } else if (type.equals("item")) {
            // find the item
            ItemResponse itemToPrint = (ItemResponse) CollectionUtils.find(pageToPrint, new Predicate() {
                @Override
                public boolean evaluate(Object object) {
                    ItemResponse response = (ItemResponse) object;
                    return position == response.getPosition();
                }
            });

            if (accProps.isBrailleEnabled()) {
                _printService.printItemBraille(testOpp, itemToPrint, accLookup);
            } else {
                _printService.printItem(testOpp.getOppInstance(), itemToPrint, requestParams);
            }
        }
    }

    private String parseOutLatencies(Map<String, String> formParams) {
        Set<String> keys = formParams.keySet();
        String latencies = null;
        for (String key : keys) {

            if (key.startsWith("{\"latencies\":")) {
                latencies = key;
                break;
            }
        }
        return latencies;
    }

    @RequestMapping(value = "TestShell.axd/pauseTest")
    @ResponseBody
    public ResponseData<String> pauseTestNew(@RequestParam Map<String, String> formParams,
            @RequestParam(value = "reason", required = false) String reason, HttpServletRequest request)
            throws TDSSecurityException, ReturnStatusException {
        return PauseTest(formParams, reason, request);
    }

    @RequestMapping(value = "TestShell.axd/pause")
    @ResponseBody
    public ResponseData<String> PauseTest(@RequestParam Map<String, String> formParams,
            @RequestParam(value = "reason", required = false) String reason, HttpServletRequest request)
            throws TDSSecurityException, ReturnStatusException {
        // check if authenticated
        if (isAuthenticated()) {
            TestOpportunity testOpp = StudentContext.getTestOpportunity();

            // only pause test if test opp exists (otherwise it doesn't matter)
            if (testOpp != null) {
                String latencies = parseOutLatencies(formParams);
                if (latencies != null) {
                    TestShellAudit testShellAudit = null;
                    try {
                        ObjectMapper mapper = new ObjectMapper();
                        testShellAudit = mapper.readValue(latencies, TestShellAudit.class);
                    } catch (IOException e) {
                        _logger.error(String.format("Problem mapping pause request to TestShellAudit: %s",
                                e.getMessage()));
                    }
                    PerformTestShellAudit(testOpp, testShellAudit, request);
                }
                // change status of opp to paused
                OpportunityStatusChange statusChange = new OpportunityStatusChange(OpportunityStatusType.Paused,
                        true, reason);
                _oppService.setStatus(testOpp.getOppInstance(), statusChange);
            }
        }

        // success
        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", null);
    }

    @RequestMapping(value = "TestShell.axd/completeTest")
    @ResponseBody
    public ResponseData<String> reviewTestNew(@RequestParam Map<String, String> formParams,
            HttpServletRequest request) throws IOException, TDSSecurityException, ReturnStatusException {
        return reviewTest(formParams, request);
    }

    @RequestMapping(value = "TestShell.axd/complete")
    @ResponseBody
    public ResponseData<String> reviewTest(@RequestParam Map<String, String> formParams, HttpServletRequest request)
            throws IOException, TDSSecurityException, ReturnStatusException {
        checkAuthenticated();

        TestOpportunity testOpp = StudentContext.getTestOpportunity();

        ResponseData<String> responseData = examCompletionService.updateStatusWithValidation(testOpp,
                new TestManager(testOpp), ExamStatusCode.STATUS_REVIEW);

        if (responseData.getReplyCode() != TDSReplyCode.OK.getCode()) {
            _tdsLogger.applicationError(responseData.getReplyText(), "updateStatusWithValidation", request, null);
            HttpContext.getCurrentContext().getResponse().sendError(HttpStatus.SC_FORBIDDEN,
                    "Cannot end the test.");

            return null;
        }

        return responseData;
    }

    /**
     * Gets the test shell audit json and logs data to the DB.
     *
     * @param testOpp
     * @throws ReturnStatusException
     */
    private void PerformTestShellAudit(TestOpportunity testOpp, TestShellAudit testShellAudit,
            HttpServletRequest request) throws TDSSecurityException, ReturnStatusException {
        try {

            if (testShellAudit == null)
                return;
            // check if have latencies to log
            if (testShellAudit.getLatencies() != null && testShellAudit.getLatencies().size() > 0) {
                LogClientLatencies(testOpp, testShellAudit.getLatencies(), request);
            }

            // check if have tool usage to log
            if (testShellAudit.getToolsUsed() != null && testShellAudit.getToolsUsed().size() > 0) {
                LogToolsUsed(testOpp, testShellAudit.getToolsUsed());
            }
        } catch (Exception ex) {
            _tdsLogger.applicationError(ex.getMessage(), "PerformTestShellAudit", request, ex);
        }

    }

    private void LogClientLatencies(TestOpportunity testOpp, List<ClientLatency> clientLatencies,
            HttpServletRequest request) {
        StringBuilder errorBuilder = new StringBuilder();

        // look for errors
        for (ClientLatency clientLatency : clientLatencies) {
            List<String> latencyErrors = clientLatency.getErrors();

            // log any latency validation errors
            if (latencyErrors != null && latencyErrors.size() > 0) {
                errorBuilder.append("PAGE ").append(clientLatency.getItemPage()).append(" ERRORS:");

                for (String error : latencyErrors) {
                    errorBuilder.append("* ");
                    errorBuilder.append(error);
                    errorBuilder.append(System.lineSeparator());
                }
                errorBuilder.append(System.lineSeparator());
            }
        }

        // write latency to DB
        try {
            _oppRepository.recordClientLatencies(testOpp.getOppInstance(), clientLatencies);
        } catch (Exception ex) {
            // log any exceptions
            errorBuilder.append("EXCEPTION: ").append(ex);
            errorBuilder.append(System.lineSeparator());
            errorBuilder.append(System.lineSeparator());
        }

        // write error to DB
        if (errorBuilder.length() > 0) {
            String message = String.format("Client latency exception/errors have occured: %s",
                    errorBuilder.toString());
            _tdsLogger.applicationError(message, "LogClientLatencies", request, null);
        }
    }

    private void LogToolsUsed(TestOpportunity testOpp, List<ToolUsed> toolsUsed) {
        try {
            _oppRepository.recordToolsUsed(testOpp.getOppInstance().getKey(), toolsUsed);
        } catch (Exception e) {
            _logger.error(String.format("Tools Used exception occured %s", e.getMessage()));
        }
    }

    @RequestMapping(value = "TestShell.axd/waitForSegmentApproval")
    @ResponseBody
    private ResponseData<String> WaitForSegmentApproval(
            @RequestParam(value = "position", required = false) int segmentPosition,
            @RequestParam(value = "approval", required = false) String segmentApproval)
            throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();
        OpportunityInstance oppInstance = StudentContext.getOppInstance();

        if (segmentApproval.equals("entry")) {
            _oppService.waitForSegment(oppInstance, segmentPosition, TestSegmentApproval.Entry);
        } else if (segmentApproval.equals("exit")) {
            _oppService.waitForSegment(oppInstance, segmentPosition, TestSegmentApproval.Exit);
        }

        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", null);
    }

    @RequestMapping(value = "TestShell.axd/checkForSegmentApproval")
    @ResponseBody
    private ResponseData<ApprovalInfo> CheckSegmentApproval() throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();
        OpportunityInstance oppInstance = StudentContext.getOppInstance();

        ApprovalInfo approvalInfo = _oppService.checkSegmentApproval(oppInstance);
        if (approvalInfo.getComment() != null)
            approvalInfo.setComment(HtmlUtils.htmlEscape(approvalInfo.getComment()));

        /*
         * if (approvalInfo.Status == OpportunityApprovalStatus.Denied) { throw new
         * ReturnStatusException(new
         * ReturnStatus(OpportunityApprovalStatus.Denied.ToString(),
         * approvalInfo.Comment)); }
         */

        if (approvalInfo.getStatus().equals(OpportunityApprovalStatus.Logout)) {
            throw new ReturnStatusException(
                    new ReturnStatus(OpportunityApprovalStatus.Logout.toString(), "Proctor logged out"));
        }

        return new ResponseData<ApprovalInfo>(TDSReplyCode.OK.getCode(), "OK", approvalInfo);
    }

    @RequestMapping(value = "TestShell.axd/exitSegment")
    @ResponseBody
    private void ExitSegment(@RequestParam(value = "position", required = false) int segmentPosition)
            throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();

        OpportunityInstance oppInstance = StudentContext.getOppInstance();
        _oppService.exitSegment(oppInstance, segmentPosition);
    }

    @RequestMapping(value = "TestShell.axd/recordItemComment")
    @ResponseBody
    private ResponseData<String> RecordItemComment(@RequestParam(value = "position", required = false) int position,
            @RequestParam(value = "comment", required = false) String comment)
            throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();
        OpportunityInstance oppInstance = StudentContext.getOppInstance();
        Testee testee = StudentContext.getTestee();

        // encode and trim comment
        if (comment != null) {
            // TODO mpatel - Talk to Shiva and confirm we can replace following line
            // of code with UrlEncoderDecoderUtils
            // comment = AntiXss.HtmlEncode(comment, 2000);
            comment = HtmlUtils.htmlEscape(comment);
        }

        //_responseRepository.recordComment (oppInstance.getSessionKey (), testee.getKey (), oppInstance.getKey (), position, comment);
        remoteExamineeNoteService.saveItemNote(oppInstance, testee.getKey(), position, comment);

        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", null);
    }

    @RequestMapping(value = "TestShell.axd/recordOppComment")
    @ResponseBody
    private ResponseData<String> RecordOppComment(@RequestParam(value = "comment", required = false) String comment)
            throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();
        OpportunityInstance oppInstance = StudentContext.getOppInstance();
        Testee testee = StudentContext.getTestee();

        // Following code commented in 2013 Dotnet version
        /*
         * //encode and trim comment if (comment != null) { // comment =
         * AntiXss.HtmlEncode(comment, 2000); comment =
         * UrlEncoderDecoderUtils.encode (comment); }
         */

        remoteExamineeNoteService.saveExamNote(oppInstance, testee.getKey(), comment);

        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", null);
    }

    @RequestMapping(value = "TestShell.axd/getOppComment")
    @ResponseBody
    private ResponseData<String> GetOppComment() throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();
        OpportunityInstance oppInstance = StudentContext.getOppInstance();

        final String comment = remoteExamineeNoteService.findExamNote(oppInstance);

        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", comment);
    }

    @RequestMapping(value = "TestShell.axd/markForReview")
    @ResponseBody
    private ResponseData<String> MarkForReview(@RequestParam(value = "position", required = false) int position,
            @RequestParam(value = "mark", required = false) boolean mark)
            throws TDSSecurityException, ReturnStatusException {
        checkAuthenticated();

        OpportunityInstance oppInstance = StudentContext.getOppInstance();

        _responseService.markItemForReview(oppInstance, position, mark);

        return new ResponseData<>(TDSReplyCode.OK.getCode(), "OK", null);
    }

    @RequestMapping(value = "TestShell.axd/removeResponse")
    @ResponseBody
    private ResponseData<String> RemoveResponse(@RequestParam(value = "position", required = false) int position,
            @RequestParam(value = "itemID", required = false) String itemID,
            @RequestParam(value = "dateCreated", required = false) String dateCreated)
            throws TDSSecurityException, ReturnStatusException {
        OpportunityInstance oppInstance = StudentContext.getOppInstance();

        _responseService.removeResponse(oppInstance, position, itemID, dateCreated);

        // success
        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", null);
    }

    // / <summary>
    // / Returns the opp status code.
    // / </summary>
    @RequestMapping(value = "TestShell.axd/getStatus")
    @ResponseBody
    private ResponseData<OpportunityStatusType> GetStatus() throws TDSSecurityException, ReturnStatusException {
        OpportunityStatus oppStatus = _oppService.getStatus(StudentContext.getOppInstance());
        return new ResponseData<OpportunityStatusType>(TDSReplyCode.OK.getCode(), "OK", oppStatus.getStatus());
    }

    @RequestMapping(value = "TestShell.axd/timer")
    @ResponseBody
    private void timerBatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

    }

    @RequestMapping(value = "TestShell.axd/audit")
    @ResponseBody
    private ResponseData<String> audit(TestShellAudit testShellAudit)
            throws TDSSecurityException, ReturnStatusException {

        return new ResponseData<String>(TDSReplyCode.OK.getCode(), "OK", "Logged");
    }

}