Java tutorial
/******************************************************************************* * 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.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_EQUIVALENCE; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_FILTER_REMOVE; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_GRADE_FUNCTION; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_REMOVE_TRANSFORMER; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_UNIQUE_KEY_FUNCTION; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_VALUE_DIFF_TRANS; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BP_ELEMENT_ID_TRANSFORMER; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.CORE_STANDARDS_INVALID_BLUEPRINT_FILTER; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.CORE_STANDARD_BLUEPRINT_ELEMENT_TRANSFORMER; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.opentestsystem.authoring.testauth.domain.Assessment; import org.opentestsystem.authoring.testauth.domain.BlueprintElement; import org.opentestsystem.authoring.testauth.domain.CoreStandardPublicationPayloadElement; import org.opentestsystem.authoring.testauth.domain.CoreStandardPublicationResponse; import org.opentestsystem.authoring.testauth.domain.Segment; import org.opentestsystem.authoring.testauth.domain.ValidationResult; import org.opentestsystem.authoring.testauth.domain.search.BlueprintElementSearchRequest; import org.opentestsystem.authoring.testauth.persistence.BlueprintElementRepository; import org.opentestsystem.authoring.testauth.service.AssessmentService; import org.opentestsystem.authoring.testauth.service.BlueprintElementService; import org.opentestsystem.authoring.testauth.service.CoreStandardsService; import org.opentestsystem.authoring.testauth.service.PerformanceLevelService; import org.opentestsystem.authoring.testauth.service.ScoringRuleService; import org.opentestsystem.authoring.testauth.service.SegmentService; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_ADD_DEFAULT_SEGMENT_VALUE_TRANSFORMER; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_CLEAR_SEGMENT_VALUE_TRANSFORMER; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_FILTER_BY_CHOSEN_GRADES; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_INJECT_DEFAULT_VALUES_TRANSFORMER; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_REMOVE_SEGMENT_VALUE_TRANSFORMER; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_UPDATE_ISA_PARAMETERS_TRANSFORMER; import org.opentestsystem.authoring.testauth.validation.BlueprintElementValidator; import org.opentestsystem.authoring.testauth.validation.ValidationHelper; import org.opentestsystem.shared.exception.LocalizedException; import org.opentestsystem.shared.exception.RestException; import org.opentestsystem.shared.search.domain.SearchResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BindingResult; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.MapDifference.ValueDifference; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @Service public class BlueprintElementServiceImpl extends AssessmentChildHelper implements BlueprintElementService { private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintElementServiceImpl.class); @Autowired private transient BlueprintElementRepository blueprintElementRepository; @Autowired private AssessmentService assessmentService; @Autowired private SegmentService segmentService; @Autowired private CoreStandardsService coreStandardsService; @Autowired private BlueprintElementValidator blueprintElementValidator; @Autowired private ScoringRuleService scoringRuleService; @Autowired private PerformanceLevelService performanceLevelService; @Override public BlueprintElement getBlueprintElement(final String blueprintElementId) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Finding blueprintElement for Id: " + blueprintElementId); } return this.blueprintElementRepository.findOne(blueprintElementId); } @Override public List<BlueprintElement> getBlueprintElementsByAssessmentId(final String assessmentId) { return this.blueprintElementRepository.findAllByAssessmentId(assessmentId); } @Override public SearchResponse<BlueprintElement> searchBlueprintElements(final Map<String, String[]> parameterMap) { final BlueprintElementSearchRequest searchRequest = new BlueprintElementSearchRequest(parameterMap); if (searchRequest.isValid()) { return this.blueprintElementRepository.search(searchRequest); } throw new RestException("blueprintElement.search.invalidsearchcriteria"); } @Override public BlueprintElement saveBlueprintElement(final String blueprintElementId, final BlueprintElement blueprintElement) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Saving blueprintElement"); } if (blueprintElementId != null && (blueprintElement == null || StringUtils.isEmpty(blueprintElement.getId()) || !blueprintElementId.equals(blueprintElement.getId()))) { throw new LocalizedException("blueprintElement.invalid.id"); } checkForLockedAssessment(blueprintElement.getAssessmentId()); BlueprintElement savedBlueprintElement = null; try { validateBlueprintElement(blueprintElement); savedBlueprintElement = this.blueprintElementRepository.save(blueprintElement); } catch (final DuplicateKeyException dke) { throw new LocalizedException("blueprintElement.already.exists", new String[] { blueprintElement.getId() }, dke); } return savedBlueprintElement; } @Override public void removeByAssessmentId(final String assessmentId) { checkForLockedAssessment(assessmentId); this.blueprintElementRepository.delete(this.blueprintElementRepository.findAllByAssessmentId(assessmentId)); } @Override public List<BlueprintElement> saveBlueprintElementList(final List<BlueprintElement> blueprintElements) { final List<BlueprintElement> savedBlueprintElements = Lists.newArrayList(); if (!CollectionUtils.isEmpty(blueprintElements)) { final String assessmentId = blueprintElements.get(0).getAssessmentId(); checkForLockedAssessment(assessmentId); final List<BlueprintElement> blueprintElementsToRemove = Lists .newArrayList(Iterables.filter(blueprintElements, BLUEPRINT_ELEMENT_FILTER_REMOVE)); final List<BlueprintElement> blueprintElementsToSave = Lists.newArrayList( Iterables.filter(blueprintElements, Predicates.not(BLUEPRINT_ELEMENT_FILTER_REMOVE))); try { if (!CollectionUtils.isEmpty(blueprintElementsToRemove)) { this.blueprintElementRepository.delete(blueprintElementsToRemove); final List<String> bpReferenceIdsToDelete = Lists.transform(blueprintElementsToRemove, BP_ELEMENT_ID_TRANSFORMER); cleanupForRelatedData(assessmentId, bpReferenceIdsToDelete); } if (!CollectionUtils.isEmpty(blueprintElementsToSave)) { final List<String> bpReferenceIdsToDelete = Lists .transform( Lists.newArrayList(Iterables.filter(blueprintElementsToSave, BlueprintHelper.BP_ELEMENT_ID_INACTIVE_FILTER)), BP_ELEMENT_ID_TRANSFORMER); savedBlueprintElements.addAll(this.blueprintElementRepository.save(blueprintElementsToSave)); cleanupForRelatedData(assessmentId, bpReferenceIdsToDelete); } } catch (final DuplicateKeyException dke) { throw new LocalizedException("blueprintElement.listhasduplicates", dke); } } return savedBlueprintElements; } @Override public int synchronizeWithCoreStandards(final String assessmentId) { checkForLockedAssessment(assessmentId); final Assessment assessment = this.assessmentService.getAssessment(assessmentId); String client = assessment.getClient(); List<BlueprintElement> blueprintElementListToSave = Lists.newArrayList(); final List<BlueprintElement> blueprintElementList = this.blueprintElementRepository .findAllByAssessmentId(assessmentId); final List<Segment> segmentList = this.segmentService.findSegmentListByAssessmentId(assessmentId); this.segmentService.loadReferenceData(segmentList); final Map<String, Segment> segmentMap = SegmentHelper.createSegmentIdKeyMap(segmentList); final CoreStandardPublicationResponse csResponse = this.coreStandardsService .getCoreStandardsForPublication(assessment.getPublication().getCoreStandardsPublicationKey()); final List<CoreStandardPublicationPayloadElement> coreStandardsList = csResponse != null ? csResponse.getPayload() : null; // coreStandards are empty, wipe out blueprint elements (should never happen) if (CollectionUtils.isEmpty(coreStandardsList)) { blueprintElementListToSave = Lists.transform(blueprintElementList, BLUEPRINT_ELEMENT_REMOVE_TRANSFORMER); } else { final List<BlueprintElement> coreStandardsElementList = Lists.newArrayList(Iterables.filter( Iterables.transform(coreStandardsList, CORE_STANDARD_BLUEPRINT_ELEMENT_TRANSFORMER.getInstance(client)), CORE_STANDARDS_INVALID_BLUEPRINT_FILTER)); // first time around, default active booleans to correspond to the grades in the assessment if (CollectionUtils.isEmpty(blueprintElementList)) { blueprintElementListToSave = Lists.transform(coreStandardsElementList, BLUEPRINT_ELEMENT_INJECT_DEFAULT_VALUES_TRANSFORMER.getInstance(assessmentId, assessment.getGrade(), segmentMap)); } else { // subsequent synch blueprintElementListToSave = buildListOfDifferences(assessmentId, coreStandardsElementList, blueprintElementList, segmentMap, assessment.getGrade()); } } saveBlueprintElementList(blueprintElementListToSave); assessment.setBlueprintSynced(true); this.assessmentService.updateAssessment(assessment); return Lists.newArrayList(Iterables.filter(blueprintElementListToSave, BLUEPRINT_ELEMENT_FILTER_BY_CHOSEN_GRADES.getInstance(assessment.getGrade()))).size(); } @Override public Map<String, Integer> getElementTotalCounts(final String assessmentId) { final Map<String, Integer> elementCountsMap = Maps.newHashMap(); elementCountsMap.put("opMin", 0); elementCountsMap.put("opMax", 0); elementCountsMap.put("ftMin", 0); elementCountsMap.put("ftMax", 0); final Map<String, String[]> searchParams = Maps.newHashMap(); searchParams.put("assessmentId", new String[] { assessmentId }); searchParams.put("level", new String[] { "1" }); searchParams.put("pageSize", new String[] { "1000" }); searchParams.put("active", new String[] { "true" }); final List<BlueprintElement> blueprintElements = searchBlueprintElements(searchParams).getSearchResults(); for (final BlueprintElement blueprintElement : blueprintElements) { elementCountsMap.put("opMin", elementCountsMap.get("opMin") + blueprintElement.getMasterValue().getOperationalItemMinValue()); elementCountsMap.put("opMax", elementCountsMap.get("opMax") + blueprintElement.getMasterValue().getOperationalItemMaxValue()); elementCountsMap.put("ftMin", elementCountsMap.get("ftMin") + blueprintElement.getMasterValue().getFieldTestItemMinValue()); elementCountsMap.put("ftMax", elementCountsMap.get("ftMax") + blueprintElement.getMasterValue().getFieldTestItemMaxValue()); } return elementCountsMap; } private List<BlueprintElement> buildListOfDifferences(final String blueprintId, final List<BlueprintElement> coreStandardsElementList, final List<BlueprintElement> blueprintElementList, final Map<String, Segment> segmentMap, final String[] assessmentGrades) { List<BlueprintElement> blueprintElementListToSave = Lists.newArrayList(); final Map<String, BlueprintElement> blueprintElementMap = Maps.uniqueIndex(blueprintElementList, BLUEPRINT_ELEMENT_UNIQUE_KEY_FUNCTION); final Map<String, BlueprintElement> coreStandardsElementMap = Maps.uniqueIndex(coreStandardsElementList, BLUEPRINT_ELEMENT_UNIQUE_KEY_FUNCTION); final Map<String, ValueDifference<BlueprintElement>> entriesDiffering = Maps .difference(blueprintElementMap, coreStandardsElementMap, BLUEPRINT_ELEMENT_EQUIVALENCE) .entriesDiffering(); final Map<String, BlueprintElement> entriesOnlyOnLeft = Maps .difference(blueprintElementMap, coreStandardsElementMap).entriesOnlyOnLeft(); final Map<String, BlueprintElement> entriesOnlyOnRight = Maps .difference(blueprintElementMap, coreStandardsElementMap).entriesOnlyOnRight(); blueprintElementListToSave = Lists .newArrayList(Iterables.transform(entriesDiffering.values(), BLUEPRINT_ELEMENT_VALUE_DIFF_TRANS)); // subsequent time around, default active booleans for the additions from core standards only to correspond to the grades already in use Iterables.addAll(blueprintElementListToSave, Iterables.transform(entriesOnlyOnRight.values(), BLUEPRINT_ELEMENT_INJECT_DEFAULT_VALUES_TRANSFORMER.getInstance(blueprintId, convertExistingBpElementGradesIntoDistinctGradeArray(blueprintElementList, assessmentGrades), segmentMap))); Iterables.addAll(blueprintElementListToSave, Iterables.transform(entriesOnlyOnLeft.values(), BLUEPRINT_ELEMENT_REMOVE_TRANSFORMER)); return blueprintElementListToSave; } private String[] convertExistingBpElementGradesIntoDistinctGradeArray( final List<BlueprintElement> blueprintElementList, final String[] assessmentGrades) { final List<String> existingGrades = Lists.transform(blueprintElementList, BLUEPRINT_ELEMENT_GRADE_FUNCTION); final Set<String> uniqueGrades = Sets.newHashSet(existingGrades); // ensure that at the very least the assessment grades are included as active uniqueGrades.addAll(Arrays.asList(assessmentGrades)); return Iterables.toArray(uniqueGrades, String.class); } @Override public List<BlueprintElement> addBlueprintElementValueGroup(final String assessmentId, final String segmentId, final String segmentIdToClone) { checkForLockedAssessment(assessmentId); final Segment segment = this.segmentService.getSegment(segmentId); final List<BlueprintElement> blueprintElementList = this.blueprintElementRepository .findAllByAssessmentId(assessmentId); final List<BlueprintElement> updatedBlueprintElementList = Lists.transform(blueprintElementList, BLUEPRINT_ELEMENT_ADD_DEFAULT_SEGMENT_VALUE_TRANSFORMER.getInstance(segmentId, segmentIdToClone, segment == null ? null : segment.getItemSelectionAlgorithm())); return this.blueprintElementRepository.save(updatedBlueprintElementList); } @Override public List<BlueprintElement> removeBlueprintElementValueGroup(final String assessmentId, final String segmentId) { checkForLockedAssessment(assessmentId); final List<BlueprintElement> blueprintElementList = this.blueprintElementRepository .findAllByAssessmentId(assessmentId); if (!CollectionUtils.isEmpty(blueprintElementList)) { final List<BlueprintElement> updatedBlueprintElementList = Lists.transform(blueprintElementList, BLUEPRINT_ELEMENT_REMOVE_SEGMENT_VALUE_TRANSFORMER.getInstance(segmentId)); return this.blueprintElementRepository.save(updatedBlueprintElementList); } return null; } @Override public List<BlueprintElement> updateBlueprintElementItemSelectionParameters(final String assessmentId, final String segmentId) { checkForLockedAssessment(assessmentId); final Segment segment = this.segmentService.getSegment(segmentId); final List<BlueprintElement> blueprintElementList = this.blueprintElementRepository .findAllByAssessmentId(assessmentId); final List<BlueprintElement> updatedBlueprintElementList = Lists.transform(blueprintElementList, BLUEPRINT_ELEMENT_UPDATE_ISA_PARAMETERS_TRANSFORMER.getInstance(segmentId, segment == null ? null : segment.getItemSelectionAlgorithm())); return this.blueprintElementRepository.save(updatedBlueprintElementList); } @Override public int activateBlueprintElementGrades(final String assessmentId, final String[] grades, final boolean activeFlag) { return activateBlueprintElement(assessmentId, null, activeFlag, false, grades); } @Override public int activateBlueprintElementStandard(final String assessmentId, final String grade, final String standardKey, final boolean activeFlag) { return activateBlueprintElement(assessmentId, standardKey, activeFlag, true, new String[] { grade }); } private int activateBlueprintElement(final String assessmentId, final String standardKey, final boolean activeFlag, final boolean byStandard, final String... grades) { checkForLockedAssessment(assessmentId); final List<String> alteredBpElementIds = Lists.newArrayList(); final Assessment assessment = this.assessmentService.getAssessment(assessmentId); String client = assessment.getClient(); String blueprintCompatibleStandardKey = standardKey; if (byStandard) { blueprintCompatibleStandardKey = convertIntoBlueprintCompatible(client, standardKey); } final List<BlueprintElement> blueprintElements = byStandard ? this.blueprintElementRepository.findAllChildrenByAssessmentIdAndGradeAndParentKey(assessmentId, grades[0], blueprintCompatibleStandardKey) : this.blueprintElementRepository.findAllByAssessmentIdAndGradeIn(assessmentId, grades); for (final BlueprintElement bpElement : blueprintElements) { if (bpElement.isActive() && !activeFlag) { alteredBpElementIds.add(bpElement.getId()); } bpElement.setActive(activeFlag); } return saveBlueprintElementList(blueprintElements).size(); } private String convertIntoBlueprintCompatible(String client, String standardKey) { //EF: we have CoreStandards as "SBAC-ELA-v1:2-W" and need to transform to "SBAC-2-W" to match // what we store in BlueprintElement collection int pubEndPos = StringUtils.indexOf(standardKey, ":"); if (pubEndPos > 0) return (client.concat("-").concat(StringUtils.substring(standardKey, pubEndPos + 1))); else return (client.concat("-").concat(standardKey)); } @Override public List<ValidationResult<BlueprintElement>> validateBlueprint(final String assessmentId) { final List<BlueprintElement> activeBlueprintElementList = this.blueprintElementRepository .findAllByAssessmentIdAndActive(assessmentId, true); final List<ValidationResult<BlueprintElement>> blueprintValidationResultList = Lists.newArrayList(); blueprintValidationResultList.addAll( BlueprintValidationHelper.validateBlueprintElementValueItemRanges(activeBlueprintElementList)); blueprintValidationResultList.addAll( BlueprintValidationHelper.validateBlueprintHierarchicalRelationships(activeBlueprintElementList)); final List<Segment> segments = this.segmentService.findSegmentListByAssessmentId(assessmentId); blueprintValidationResultList.addAll(BlueprintValidationHelper .validateTopLevelBlueprintVsSegmentRelationships(segments, activeBlueprintElementList)); final List<BlueprintElement> blueprintElementList = this.blueprintElementRepository .findAllByAssessmentId(assessmentId); blueprintValidationResultList.addAll( BlueprintValidationHelper.validateInactiveBlueprintHierarchicalRelationships(blueprintElementList)); return blueprintValidationResultList; } @Override public List<BlueprintElement> clearBlueprint(final String assessmentId, final String segmentId) { checkForLockedAssessment(assessmentId); final List<BlueprintElement> blueprintElementList = this.blueprintElementRepository .findAllByAssessmentId(assessmentId); final List<BlueprintElement> updatedBlueprintElementList = Lists.transform(blueprintElementList, BLUEPRINT_ELEMENT_CLEAR_SEGMENT_VALUE_TRANSFORMER.getInstance(segmentId)); return this.blueprintElementRepository.save(updatedBlueprintElementList); } private void validateBlueprintElement(final BlueprintElement blueprintElement) { final BindingResult bindingResult = new BeanPropertyBindingResult(blueprintElement, "blueprintElement"); this.blueprintElementValidator.validate(blueprintElement, bindingResult); if (bindingResult.hasErrors()) { throw ValidationHelper.convertErrorsToConstraintException(blueprintElement, bindingResult); } } private void cleanupForRelatedData(final String assessmentId, final List<String> bpReferenceIdsToDelete) { this.scoringRuleService.removeByBlueprintReferenceId(assessmentId, Iterables.toArray(bpReferenceIdsToDelete, String.class)); for (final String bpReferenceIdToDelete : bpReferenceIdsToDelete) { this.performanceLevelService.removeByReferenceId(bpReferenceIdToDelete); } if (this.blueprintElementRepository.findAllByAssessmentIdAndActive(assessmentId, true).size() < 1) { // if there's nothing active left, wipe out any straggler leaf node/level scoring rules this.scoringRuleService.removeByBlueprintReferenceId(assessmentId, new String[] {}); } } }