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

Java tutorial

Introduction

Here is the source code for org.opentestsystem.authoring.testauth.service.impl.ItemImportHelper.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.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);
    }
}