org.opentestsystem.authoring.testauth.service.impl.PublishingRecordServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opentestsystem.authoring.testauth.service.impl.PublishingRecordServiceImpl.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System
 * Copyright (c) 2013 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 org.opentestsystem.authoring.testauth.service.impl;

import static org.opentestsystem.authoring.testauth.config.TestAuthUtil.DEFAULT_VERSION;
import static org.opentestsystem.authoring.testauth.config.TestAuthUtil.paramArray;
import static org.opentestsystem.shared.search.domain.AbstractSearchRequest.SORT_DIR;

import java.math.BigDecimal;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.opentestsystem.authoring.testauth.domain.Assessment;
import org.opentestsystem.authoring.testauth.domain.PsychometricRecord;
import org.opentestsystem.authoring.testauth.domain.PublishingRecord;
import org.opentestsystem.authoring.testauth.domain.PublishingStatus;
import org.opentestsystem.authoring.testauth.domain.search.PublishingRecordSearchRequest;
import org.opentestsystem.authoring.testauth.persistence.AssessmentRepository;
import org.opentestsystem.authoring.testauth.persistence.PsychometricRecordRepository;
import org.opentestsystem.authoring.testauth.persistence.PublishingRecordRepository;
import org.opentestsystem.authoring.testauth.publish.PublisherRunner;
import org.opentestsystem.authoring.testauth.publish.domain.Purpose;
import org.opentestsystem.authoring.testauth.publish.domain.PurposeBaseContent;
import org.opentestsystem.authoring.testauth.publish.domain.TestSpecification;
import org.opentestsystem.authoring.testauth.service.ApprovalService;
import org.opentestsystem.authoring.testauth.service.AssessmentService;
import org.opentestsystem.authoring.testauth.service.PublishingRecordService;
import org.opentestsystem.authoring.testauth.validation.PublishingRecordValidationHelper;
import org.opentestsystem.shared.exception.LocalizedException;
import org.opentestsystem.shared.exception.RestException;
import org.opentestsystem.shared.search.domain.SearchResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;

@Service
public class PublishingRecordServiceImpl implements PublishingRecordService {

    @Autowired
    private transient AssessmentRepository assessmentRepository;

    @Autowired
    private transient AssessmentService assessmentService;

    @Autowired
    private transient ApprovalService approvalService;

    @Autowired
    private transient PublishingRecordRepository publishingRecordRepository;

    @Autowired
    private transient PsychometricRecordRepository psychometricRecordRepository;

    @Autowired
    private PublishingRecordValidationHelper publishingRecordValidationHelper;

    @Autowired
    private MessageSource messageSource;

    @Autowired
    private PublisherRunner publishRunner;

    // used for publishing display
    @Override
    public PublishingRecord retrievePublishingRecordWithAllowablePurposes(final String assessmentId) {
        final PublishingRecord publishingRecord = retrieveCurrentPublishingRecord(assessmentId);
        if (publishingRecord != null) {
            publishingRecord.setPurpose(Iterables.toArray(
                    this.publishingRecordValidationHelper.determineAvailableSpecTypesForAssessment(assessmentId),
                    Purpose.class));
            try {
                this.publishingRecordValidationHelper.verifyCanPublish(publishingRecord, false);
            } catch (final LocalizedException e) {
                publishingRecord.setErrorMessageText(
                        this.messageSource.getMessage(e.getMessageCode(), e.getMessageArgs(), Locale.US));
            } finally {
                this.publishingRecordRepository.save(publishingRecord);
            }
        }
        return publishingRecord;
    }

    // used for assessment display
    @Override
    public PublishingRecord retrieveCurrentPublishingRecord(final String assessmentId) {
        if (assessmentId == null) {
            return null;
        }
        final Map<String, String[]> parameterMap = ImmutableMap.of("assessmentId", paramArray(assessmentId),
                "sortKey", paramArray("lastUpdatedDate"), SORT_DIR, paramArray("DESC"));
        final SearchResponse<PublishingRecord> searchResponse = searchPublishingRecords(parameterMap);
        PublishingRecord publishingRecord = null;
        if (searchResponse.getSearchResults().size() > 0) {
            publishingRecord = searchResponse.getSearchResults().get(0);
        }
        return publishingRecord;
    }

    @Override
    public PublishingRecord getPublishingRecord(final String publishingRecordId) {
        return this.publishingRecordRepository.findOne(publishingRecordId);
    }

    // used for publishing record display
    @Override
    public SearchResponse<PublishingRecord> searchPublishingRecords(final Map<String, String[]> parameterMap) {
        final PublishingRecordSearchRequest searchRequest = new PublishingRecordSearchRequest(parameterMap);
        if (searchRequest.isValid()) {
            return this.publishingRecordRepository.search(searchRequest);
        }
        throw new RestException("publishingRecord.search.invalidSearchCriteria");
    }

    @Override
    public PublishingRecord savePublishingRecord(final PublishingRecord publishingRecord,
            final boolean incrementNextMajor) {

        final PublishingRecord latestPublishingRecord = retrievePublishingRecordWithAllowablePurposes(
                publishingRecord.getAssessmentId());

        if (latestPublishingRecord != null && publishingRecord.getPublishingStatus() == null) {
            throw new LocalizedException("publishingRecord.publishingStatus.required",
                    paramArray(latestPublishingRecord.getPublishingStatus().toString()));
        }

        boolean createPsychometricRecord = false;

        if (publishingRecord.getPublishingStatus() != null) {
            // cannot spoof publishing status, only deviation from existing is to reject
            if (!latestPublishingRecord.getPublishingStatus().equals(publishingRecord.getPublishingStatus())) {
                if (latestPublishingRecord.getPublishingStatus().equals(PublishingStatus.AWAITING_APPROVAL)
                        && !publishingRecord.getPublishingStatus().equals(PublishingStatus.REJECT)) {
                    throw new LocalizedException("publishingRecord.publishingStatus.mismatch",
                            paramArray(publishingRecord.getPublishingStatus().toString(),
                                    latestPublishingRecord.getPublishingStatus().toString()));
                }
            }
            // cannot spoof version
            if (!latestPublishingRecord.getVersion().equals(publishingRecord.getVersion())) {
                throw new LocalizedException("publishingRecord.version.mismatch",
                        paramArray(publishingRecord.getVersion(), latestPublishingRecord.getVersion()));
            }

            final Assessment assessment = this.assessmentService.getAssessment(publishingRecord.getAssessmentId());

            switch (publishingRecord.getPublishingStatus()) {
            case PUBLISHED:
                processNextVersion(assessment, publishingRecord, incrementNextMajor);
                createPsychometricRecord = true;
                break;
            case AWAITING_APPROVAL:
                approveAndPublish(assessment, publishingRecord);
                break;
            case INPROGRESS:
                setupForApproval(assessment, publishingRecord);
                break;
            case REJECT:
                processRejection(assessment, publishingRecord);
                break;
            default:
                break;
            }
        } else {
            processInitial(publishingRecord);
            createPsychometricRecord = true;
        }

        publishingRecord.setLastUpdatedDate(new DateTime());

        final PublishingRecord savedPublishingRecord = this.publishingRecordRepository.save(publishingRecord);

        if (createPsychometricRecord) {
            final PsychometricRecord psychometricRecord = new PsychometricRecord();
            psychometricRecord.setPublishingRecordId(savedPublishingRecord.getId());
            psychometricRecord.setVersion(savedPublishingRecord.getVersion());
            this.psychometricRecordRepository.save(psychometricRecord);
        }

        return savedPublishingRecord;
    }

    /**
     * save new INPROGRESS status row w/ same assessment ID and w/ incremented version # of PUBLISHED row
     */
    private void processNextVersion(final Assessment assessment, final PublishingRecord publishingRecord,
            final boolean incrementNextMajor) {
        if (!this.approvalService.isAdminUser()) {
            throw new LocalizedException("publishingRecord.invalid.user");
        }
        publishingRecord.setId(null);
        publishingRecord.setPublishingStatus(PublishingStatus.INPROGRESS);
        assessment.setStatus(PublishingStatus.INPROGRESS);
        if (incrementNextMajor) {
            final BigDecimal major = new BigDecimal(
                    StringUtils.substringBefore(publishingRecord.getVersion(), "."));
            publishingRecord.setVersion(major.add(BigDecimal.ONE).toString() + ".0");
        } else {
            publishingRecord.setVersion(
                    new BigDecimal(publishingRecord.getVersion()).add(new BigDecimal("0.1")).toString());
        }

        assessment.setVersion(publishingRecord.getVersion());
        assessment.setLocked(false);
        this.assessmentRepository.save(assessment);
    }

    /**
     * run validation to ensure assessment is complete enough for selected Purpose
     */
    private void setupForApproval(final Assessment assessment, final PublishingRecord publishingRecord) {
        this.publishingRecordValidationHelper.verifyCanPublish(publishingRecord, false);
        if (CollectionUtils.isEmpty(publishingRecord.getValidationResultList())
                || !CollectionUtils.isEmpty(Arrays.asList(publishingRecord.getPurpose()))) {
            publishingRecord.setPublishingStatus(PublishingStatus.AWAITING_APPROVAL);
            assessment.setStatus(PublishingStatus.AWAITING_APPROVAL);

            this.approvalService.createInitialApprovals(publishingRecord.getId());

            assessment.setLocked(true);
            this.assessmentRepository.save(assessment);
        }
    }

    private void processRejection(final Assessment assessment, final PublishingRecord publishingRecord) {
        publishingRecord.setPublishingStatus(PublishingStatus.INPROGRESS);
        assessment.setStatus(PublishingStatus.INPROGRESS);
        assessment.setLocked(false);
        this.assessmentRepository.save(assessment);
    }

    private void approveAndPublish(final Assessment assessment, final PublishingRecord publishingRecord) {
        if (this.approvalService.canPublishToSpecBank(publishingRecord.getId())) {
            publishingRecord.setPublishingStatus(PublishingStatus.PUBLISHING);
            publishingRecord.setLastUpdatedDate(new DateTime());
            this.publishingRecordRepository.save(publishingRecord);

            assessment.setStatus(PublishingStatus.PUBLISHING);
            this.assessmentRepository.save(assessment);

            this.publishRunner.publishAndSend(assessment, publishingRecord);
        }
    }

    private void processInitial(final PublishingRecord publishingRecord) {
        publishingRecord.setPurpose(new Purpose[] { Purpose.REGISTRATION });
        publishingRecord.setPublishingStatus(PublishingStatus.INPROGRESS);
        publishingRecord.setVersion(DEFAULT_VERSION);
    }

    @Override
    public void removeByAssessmentId(final String assessmentId) {
        this.publishingRecordRepository.delete(this.publishingRecordRepository.findAllByAssessmentId(assessmentId));
    }

    @Override
    public Entry<String, byte[]> simulate(final String publishingRecordId, final String purposeString) {
        final PublishingRecord publishingRecord = getPublishingRecord(publishingRecordId);
        final Purpose purpose = Purpose.valueOf(purposeString);
        publishingRecord.setPurpose(new Purpose[] { purpose });
        this.publishingRecordValidationHelper.verifyCanPublish(publishingRecord, true);
        if (CollectionUtils.isEmpty(publishingRecord.getValidationResultList())
                || !CollectionUtils.isEmpty(Arrays.asList(publishingRecord.getPurpose()))) {

        }
        final Assessment assessment = this.assessmentService.getAssessment(publishingRecord.getAssessmentId());
        final TestSpecification<? extends PurposeBaseContent> testSpec = this.publishRunner
                .createTestSpec(assessment, publishingRecord, purpose, true, null);
        return new AbstractMap.SimpleEntry<String, byte[]>(simulationFilename(testSpec, purposeString),
                this.publishRunner.generateAndValidateXml(testSpec));
    }

    private String simulationFilename(final TestSpecification<? extends PurposeBaseContent> testSpec,
            final String purposeString) {
        final StringBuilder sb = new StringBuilder();
        sb.append(testSpec.getIdentifier().getName());
        sb.append("_");
        sb.append(purposeString);
        sb.append("_");
        sb.append(testSpec.getPurpose());
        sb.append("_");
        sb.append(testSpec.getPublishDate());
        sb.append(".xml");
        return sb.toString();
    }
}