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.ItemHelper.ITEM_TIB_IDENTIFIER_TRANSFORMER; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; import org.opentestsystem.authoring.testauth.config.TestAuthUtil; import org.opentestsystem.authoring.testauth.domain.AdaptiveItemLocation; import org.opentestsystem.authoring.testauth.domain.Assessment; import org.opentestsystem.authoring.testauth.domain.FixedFormItemLocation; import org.opentestsystem.authoring.testauth.domain.FormPartition; import org.opentestsystem.authoring.testauth.domain.Item; import org.opentestsystem.authoring.testauth.domain.ItemGroup; import org.opentestsystem.authoring.testauth.domain.ItemGroupLocationType; import org.opentestsystem.authoring.testauth.domain.ItemLocation; import org.opentestsystem.authoring.testauth.domain.Publication; import org.opentestsystem.authoring.testauth.domain.TibImportResult; import org.opentestsystem.authoring.testauth.persistence.ItemRepository; import org.opentestsystem.authoring.testauth.persistence.SegmentRepository; import org.opentestsystem.authoring.testauth.service.EnemyService; import org.opentestsystem.authoring.testauth.service.FormPartitionService; import org.opentestsystem.authoring.testauth.service.ItemGroupService; import org.opentestsystem.authoring.testauth.service.ItemMetadataKeysService; import org.opentestsystem.authoring.testauth.service.PublicationService; import org.opentestsystem.authoring.testauth.service.TibItemService; import org.opentestsystem.authoring.testitembank.client.TibItem; import org.opentestsystem.authoring.testitembank.client.TibItemStandardPublication; import org.opentestsystem.shared.exception.LocalizedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @Component public class ItemImportHelper extends ItemBaseHelper { private static ObjectMapper STATIC_JSON_MAPPER = new ObjectMapper(); private static final Logger LOGGER = LoggerFactory.getLogger(ItemImportHelper.class); private static final int TIB_IMPORT_LIMIT = 10000; private static final String STIMULUS_PREFIX = "stim"; private static final String ITEM_PREFIX = "item"; @Autowired private transient ItemRepository itemRepository; @Autowired private SegmentRepository segmentRepository; @Autowired private ItemGroupService itemGroupService; @Autowired private TibItemService tibItemService; @Autowired private PublicationService publicationService; @Autowired private FormPartitionService formPartitionService; @Autowired private ItemMetadataKeysService itemMetadataKeysService; @Autowired private EnemyService enemyService; @Autowired private ItemOrderingHelper itemOrderingHelper; @Autowired private ItemEnemyRunner itemEnemyRunner; @Value("testauth.item.count.max.limit") private String itemCountLimit; @Value("${testauth.item.op.statuses}") private String opItemStatuses; private List<String> opItemStatusList = null; public TibImportResult importTibItemsToSegment(final Assessment assessment, final String segmentId, final String importToGroupId, final List<TibItem> tibItems) { final AdaptiveItemLocation location = new AdaptiveItemLocation(); location.setSegmentId(segmentId); location.setItemGroupId(importToGroupId); checkItemCountLimit(tibItems.size(), assessment.getId()); final List<ItemGroup> groups = this.itemGroupService.getItemGroupsBySegment(segmentId); final TibImportResult result = importItems(assessment, groups, location, tibItems); this.itemOrderingHelper.autoSortSegmentItems(assessment.getId(), segmentId); return result; } protected TibImportResult importItemsFromTib(final Assessment assessment, final String targetSegmentId, final String targetformPartitionId, final String targetItemGroupId, final Map<String, String[]> params) { TibImportResult results = null; String message = null; if (assessment == null || StringUtils.isEmpty(targetSegmentId) && StringUtils.isEmpty(targetformPartitionId)) { message = "Invalid import target, segment or form partition is required."; } else { final List<TibItem> tibItems = getTibItemsToImport(params); results = importTibItemList(assessment, targetSegmentId, targetformPartitionId, targetItemGroupId, tibItems); } if (results == null) { results = new TibImportResult(); results.setMessage(message); } return results; } private TibImportResult importTibItemList(final Assessment assessment, final String targetSegmentId, final String targetFormPartitionId, final String targetItemGroupId, final List<TibItem> items) { TibImportResult results = null; String message = null; if (items.size() > TIB_IMPORT_LIMIT) { message = "Too many items, to import please increase search criteria"; } else { if (!StringUtils.isEmpty(targetSegmentId)) { results = importTibItemsToSegment(assessment, targetSegmentId, targetItemGroupId, items); } else if (!StringUtils.isEmpty(targetFormPartitionId)) { results = importTibItemsToFormPartition(assessment, targetFormPartitionId, targetItemGroupId, items); } if (results.getItemsOmitted() > 0) { results.setMessage("Some or all omitted items may already be imported."); } } final Set<String> relatedItemIdentifiersToSaveList = reconcileItemRelatedContent(assessment, items); if (!CollectionUtils.isEmpty(relatedItemIdentifiersToSaveList)) { // recursion to get related items final List<TibItem> relatedItemsToImport = this.tibItemService.retrieveItemsByIdentifierList( assessment.getTenantId(), assessment.getItemBank(), new ArrayList<String>(relatedItemIdentifiersToSaveList)); final TibImportResult relatedTibImportResult = importTibItemList(assessment, targetSegmentId, targetFormPartitionId, targetItemGroupId, relatedItemsToImport); if (relatedTibImportResult != null && results != null) { results.setAssociatedItemsImported( results.getAssociatedItemsImported() + relatedTibImportResult.getItemsImported()); } } if (results == null) { results = new TibImportResult(); results.setMessage(message); } return results; } private Set<String> reconcileItemRelatedContent(final Assessment assessment, final List<TibItem> tibItemList) { final Set<String> relatedItemIdentifiersToSaveList = Sets.newHashSet(); final List<Item> itemList = this.itemRepository.findAllByAssessmentId(assessment.getId()); final List<String> allItemTibIdentifiers = Lists.transform(itemList, ITEM_TIB_IDENTIFIER_TRANSFORMER); for (final TibItem tibItem : tibItemList) { if (StringUtils.isNotBlank(tibItem.getAssociatedStimulus())) { String tibIdent = buildTibIdentifier(STIMULUS_PREFIX, assessment.getItemBank(), tibItem.getAssociatedStimulus()); if (!allItemTibIdentifiers.contains(tibIdent)) relatedItemIdentifiersToSaveList.add(tibIdent); } if (StringUtils.isNotBlank(tibItem.getAssociatedTutorial())) { String tibIdent = buildTibIdentifier(ITEM_PREFIX, assessment.getItemBank(), tibItem.getAssociatedTutorial()); if (!allItemTibIdentifiers.contains(tibIdent)) relatedItemIdentifiersToSaveList.add(tibIdent); } if (StringUtils.isNotBlank(tibItem.getAssociatedWordlist())) { String tibIdent = buildTibIdentifier(ITEM_PREFIX, assessment.getItemBank(), tibItem.getAssociatedWordlist()); if (!allItemTibIdentifiers.contains(tibIdent)) relatedItemIdentifiersToSaveList.add(tibIdent); } } return relatedItemIdentifiersToSaveList; } private String buildTibIdentifier(String prefix, String itemBank, String id) { return prefix + "-" + itemBank + "-" + id; } public TibImportResult importTibItemsToFormPartition(final Assessment assessment, final String partitionId, final String importToGroupId, final List<TibItem> tibItems) { final FixedFormItemLocation location = new FixedFormItemLocation(); FormPartition formPartion = this.formPartitionService.getFormPartition(partitionId); location.setSegmentId(formPartion.getSegmentId()); location.setFormPartitionId(partitionId); location.setItemGroupId(importToGroupId); checkItemCountLimit(tibItems.size(), assessment.getId()); final List<ItemGroup> groups = this.itemGroupService.getItemGroupsFormPartition(partitionId); final TibImportResult result = importItems(assessment, groups, location, tibItems); this.itemOrderingHelper.autoSortFormPartitionItems(assessment.getId(), partitionId); return result; } private void checkItemCountLimit(final long tibItemsCount, final String assessmentId) { // see if items to be imported exceeds the threshold of items per assessment final long totalAssessmentItemCount = this.itemRepository.findCountByAssessmentId(assessmentId); if (StringUtils.isNotBlank(this.itemCountLimit) && StringUtils.isNumeric(this.itemCountLimit) && Long.valueOf(this.itemCountLimit).compareTo(totalAssessmentItemCount + tibItemsCount) < 0) { throw new LocalizedException("item.count.exceeds.limit", new String[] { this.itemCountLimit }); } } private List<TibItem> getTibItemsToImport(final Map<String, String[]> params) { final Map<String, String[]> searchParams = Maps.newHashMap(params); searchParams.put("pageSize", new String[] { "10000" }); return this.tibItemService.searchItems(params); } private String getImportItemGroupId(final String assessmentId, final Map<String, String> passageItemGroupMap, final Item item, final ItemLocation targetLocation) { String groupId = targetLocation.getItemGroupId(); if (StringUtils.isEmpty(groupId)) { if (StringUtils.isEmpty(passageItemGroupMap.get(item.getAssociatedStimulus()))) { groupId = createItemGroup(assessmentId, targetLocation, item.getAssociatedStimulus(), item.getStimulusName()); passageItemGroupMap.put(item.getAssociatedStimulus(), groupId); } else { groupId = passageItemGroupMap.get(item.getAssociatedStimulus()); } } return groupId; } private TibImportResult importItems(final Assessment assessment, final List<ItemGroup> existingItemGroups, final ItemLocation targetLocation, final List<TibItem> tibItems) { final long start = System.currentTimeMillis(); final TibImportResult results = new TibImportResult(); int totalAdded = 0; int itemsOmitted = 0; final String targetGroupId = targetLocation.getItemGroupId(); final Map<String, String> passageItemGroupMap = Maps.newHashMap(); passageItemGroupMap.put("", Item.NO_GROUP_KEY); passageItemGroupMap.put(null, Item.NO_GROUP_KEY); if (!CollectionUtils.isEmpty(existingItemGroups)) { for (final ItemGroup group : existingItemGroups) { if (StringUtils.isNotEmpty(group.getPassageId())) { passageItemGroupMap.put(group.getPassageId(), group.getId()); } } } // defaults for targetLocation; targetLocation.setBlockId("A"); targetLocation.setAdminRequired(true); targetLocation.setResponseRequired(true); targetLocation.setActive(true); if (!CollectionUtils.isEmpty(tibItems)) { final Set<String> metadataKeys = Sets.newHashSet(); for (final TibItem tibItem : tibItems) { Item item = this.itemRepository.findByAssessmentIdAndTibIdentifier(assessment.getId(), tibItem.getIdentifier()); if (item == null) { item = convertToItem(tibItem, assessment); item.setAssessmentId(assessment.getId()); } targetLocation.setFieldTestItem(isFieldTestStatus(item.getStatus())); if (item.getInteractionType().equalsIgnoreCase("Stimulus") || item.getInteractionType().equalsIgnoreCase("WIT") || item.getInteractionType().equalsIgnoreCase("TUT")) targetLocation.setAssociatedItem(true); else targetLocation.setAssociatedItem(false); // Associated items should not be assigned to item groups if (targetLocation.isAssociatedItem() == false) { targetLocation.setItemGroupId(targetGroupId); final String groupId = getImportItemGroupId(assessment.getId(), passageItemGroupMap, item, targetLocation); targetLocation.setItemGroupId(groupId); } else targetLocation.setItemGroupId(Item.SKIP_GROUP_KEY); targetLocation.setItemIdentifier(item.getTibIdentifier()); if (hasLocation(item, targetLocation)) { itemsOmitted++; } else { item.getItemLocation().add(targetLocation); this.itemRepository.save(item); metadataKeys.addAll(tibItem.getAllIncludedMetatdata().keySet()); totalAdded++; } } this.itemMetadataKeysService.loadTibItemImportIntoItemMetadataKeys(assessment.getId(), tibItems); this.itemEnemyRunner.reconcileItemEnemyMetadata(assessment.getId()); } results.setItemsImported(totalAdded); results.setItemsOmitted(itemsOmitted); results.setImportMillis(System.currentTimeMillis() - start); return results; } public static final String NO_STANDARD = "No standard alignment"; public static final String NO_PUBLICATION = "No publication alignment"; public static final String WRONG_PUBLICATION = "Not aligned to assessment publication"; private static final String TIB_ITEM_SCORE_DIMENSION_NAME = "IrtDimension"; private static final String ITEM_SCORE_DIMENSION_NAME = "ItemScoreDimension"; private Item convertToItem(final TibItem tibItem, final Assessment assessment) { final Item item = new Item(); item.setItemLocation(new ArrayList<ItemLocation>()); item.setId(null); item.setTibIdentifier(tibItem.getIdentifier()); item.setVersion(tibItem.getVersion()); item.setInteractionType(tibItem.getInteractionType()); item.setIntendedGrade(tibItem.getIntendedGrade()); if (StringUtils.isEmpty(item.getIntendedGrade()) && "Stimulus".equalsIgnoreCase(item.getInteractionType())) item.setIntendedGrade("NA"); item.setStatus(tibItem.getStatus()); item.setSubject(tibItem.getSubject()); if (CollectionUtils.isEmpty(tibItem.getItemStandardPublicationList())) { item.setPublicationKey(NO_PUBLICATION); item.setPrimaryStandard(NO_STANDARD); } else { final Publication pub = this.publicationService .getPublicationWithoutReferences(assessment.getPublicationId()); item.setPublicationKey(WRONG_PUBLICATION); item.setPrimaryStandard(WRONG_PUBLICATION); for (final TibItemStandardPublication tibPub : tibItem.getItemStandardPublicationList()) { if (pub.getCoreStandardsPublicationKey().equals(tibPub.getPublication())) { item.setPublicationKey(tibPub.getPublication()); item.setPrimaryStandard(tibPub.getPrimaryStandard()); break; } } } item.setAssociatedTutorial(tibItem.getAssociatedTutorial()); item.setAssociatedWordlist(tibItem.getAssociatedWordlist()); item.setEnemyList(tibItem.getItemEnemies()); item.setStimulusName(tibItem.getStimulusName()); item.setAssociatedStimulus(tibItem.getAssociatedStimulus()); item.setItemScoreDimensionList(TestAuthUtil.nullsafeListTransform(tibItem.getItemScoreDimensionList(), ItemHelper.TIB_IRT_DIMENSION_TRANSFORMER)); item.setAllIncludedMetadata(tibItem.getAllIncludedMetatdata()); // Replace IrtDimension element in metadata with ItemScoreDimension // because relevant elements had been just renamed while copying from TibItem to Item if (item.getAllIncludedMetadata().containsKey(TIB_ITEM_SCORE_DIMENSION_NAME)) { item.getAllIncludedMetadata().remove(TIB_ITEM_SCORE_DIMENSION_NAME); item.getAllIncludedMetadata().put(ITEM_SCORE_DIMENSION_NAME, item.getItemScoreDimensionList()); } return item; } private String createItemGroup(final String assessmentId, final ItemLocation itemLocation, final String passageId, final String passageName) { final String groupName = "Passage: " + passageId + " - " + passageName; LOGGER.info("Creating item group " + groupName); final ItemGroup newGroup = new ItemGroup(); if (itemLocation instanceof AdaptiveItemLocation) { newGroup.setLocationId(itemLocation.getSegmentId()); newGroup.setLocationType(ItemGroupLocationType.SEGMENT); } else if (itemLocation instanceof FixedFormItemLocation) { newGroup.setLocationId(itemLocation.getFormPartitionId()); newGroup.setLocationType(ItemGroupLocationType.FORM_PARTITION); } newGroup.setGroupName(StringUtils.length(groupName) > 60 ? groupName.substring(0, 59) : groupName); newGroup.setAssessmentId(assessmentId); newGroup.setPassageId(passageId); newGroup.setPassageName(passageName); newGroup.setMaxResponses(0); newGroup.setMaxitems(0); final ItemGroup created = this.itemGroupService.saveItemGroup(newGroup); return created.getId(); } private boolean isFieldTestStatus(final String itemStatus) { if (this.opItemStatusList == null) { if (StringUtils.isEmpty(this.opItemStatuses)) { throw new LocalizedException("item.op.statuses.empty"); } this.opItemStatusList = Arrays.asList(this.opItemStatuses.split(",")); } return !this.opItemStatusList.contains(itemStatus); } }