Java tutorial
/* * Copyright 2008 The Kuali Foundation * * Licensed under the Educational Community License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ecl1.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kuali.student.r2.lum.course.service.assembler; import org.kuali.student.common.util.UUIDHelper; import org.kuali.student.r1.common.assembly.BOAssembler; import org.kuali.student.r1.common.assembly.BaseDTOAssemblyNode; import org.kuali.student.r1.common.assembly.BaseDTOAssemblyNode.NodeOperation; import org.kuali.student.r2.common.assembler.AssemblyException; import org.kuali.student.r2.common.dto.AttributeInfo; import org.kuali.student.r2.common.dto.ContextInfo; import org.kuali.student.r2.common.dto.DtoConstants; import org.kuali.student.r2.common.dto.RichTextInfo; import org.kuali.student.r2.common.exceptions.DoesNotExistException; import org.kuali.student.r2.common.exceptions.InvalidParameterException; import org.kuali.student.r2.common.exceptions.MissingParameterException; import org.kuali.student.r2.common.exceptions.OperationFailedException; import org.kuali.student.r2.common.exceptions.PermissionDeniedException; import org.kuali.student.r2.core.atp.dto.AtpInfo; import org.kuali.student.r2.core.atp.service.AtpService; import org.kuali.student.r2.lum.clu.dto.AdminOrgInfo; import org.kuali.student.r2.lum.clu.dto.CluAccountingInfo; import org.kuali.student.r2.lum.clu.dto.CluCluRelationInfo; import org.kuali.student.r2.lum.clu.dto.CluFeeInfo; import org.kuali.student.r2.lum.clu.dto.CluFeeRecordInfo; import org.kuali.student.r2.lum.clu.dto.CluIdentifierInfo; import org.kuali.student.r2.lum.clu.dto.CluInfo; import org.kuali.student.r2.lum.clu.dto.CluLoRelationInfo; import org.kuali.student.r2.lum.clu.dto.CluResultInfo; import org.kuali.student.r2.lum.clu.dto.LuCodeInfo; import org.kuali.student.r2.lum.clu.dto.ResultOptionInfo; import org.kuali.student.r2.lum.clu.service.CluService; import org.kuali.student.r2.lum.course.dto.CourseCrossListingInfo; import org.kuali.student.r2.lum.course.dto.CourseExpenditureInfo; import org.kuali.student.r2.lum.course.dto.CourseFeeInfo; import org.kuali.student.r2.lum.course.dto.CourseInfo; import org.kuali.student.r2.lum.course.dto.CourseJointInfo; import org.kuali.student.r2.lum.course.dto.CourseRevenueInfo; import org.kuali.student.r2.lum.course.dto.CourseVariationInfo; import org.kuali.student.r2.lum.course.dto.FormatInfo; import org.kuali.student.r2.lum.course.dto.LoDisplayInfo; import org.kuali.student.r2.lum.lo.dto.LoInfo; import org.kuali.student.r2.lum.lo.service.LearningObjectiveService; import org.kuali.student.r2.lum.lrc.dto.ResultValueInfo; import org.kuali.student.r2.lum.lrc.dto.ResultValueRangeInfo; import org.kuali.student.r2.lum.lrc.dto.ResultValuesGroupInfo; import org.kuali.student.r2.lum.lrc.service.LRCService; import org.kuali.student.r2.lum.service.assembler.CluAssemblerUtils; import org.kuali.student.r2.lum.util.constants.CluServiceConstants; import org.kuali.student.r2.lum.util.constants.LrcServiceConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Assembler for CourseInfo. Provides assemble and disassemble operation on * CourseInfo from/to CluInfo and other base DTOs * * @author Kuali Student Team * */ public class CourseAssembler implements BOAssembler<CourseInfo, CluInfo> { private static final Logger LOG = LoggerFactory.getLogger(CourseAssembler.class); private CluService cluService; private FormatAssembler formatAssembler; private CourseJointAssembler courseJointAssembler; private LoAssembler loAssembler; private LearningObjectiveService loService; private CluAssemblerUtils cluAssemblerUtils; private LRCService lrcService; private AtpService atpService; private float defaultCreditIncrement = 1.0f; // @Override public CourseInfo assemble(CluInfo clu, CourseInfo courseInfo, boolean shallowBuild, ContextInfo contextInfo) throws AssemblyException { CourseInfo course = (null != courseInfo) ? courseInfo : new CourseInfo(); // Copy all the data from the clu to the course course.setAttributes(clu.getAttributes()); course.setCampusLocations(clu.getCampusLocations()); course.setCode(clu.getOfficialIdentifier().getCode()); course.setCourseNumberSuffix(clu.getOfficialIdentifier().getSuffixCode()); course.setLevel(clu.getOfficialIdentifier().getLevel()); course.setOutOfClassHours(clu.getIntensity()); course.setInstructors(clu.getInstructors()); course.setStartTerm(clu.getExpectedFirstAtp()); course.setEndTerm(clu.getLastAtp()); course.setCourseTitle(clu.getOfficialIdentifier().getLongName()); // CrossListings List<CourseCrossListingInfo> crossListings = assembleCrossListings(clu.getAlternateIdentifiers()); course.setCrossListings(crossListings); //Variation List<CourseVariationInfo> variations = assembleVariations(clu.getAlternateIdentifiers()); course.setVariations(variations); // course.setDepartment(clu.getPrimaryAdminOrg().getOrgId()); if (course.getUnitsDeployment() == null) { course.setUnitsDeployment(new ArrayList<String>()); } if (course.getUnitsContentOwner() == null) { course.setUnitsContentOwner(new ArrayList<String>()); } List<String> courseAdminOrgs = new ArrayList<String>(); List<String> courseSubjectOrgs = new ArrayList<String>(); for (AdminOrgInfo adminOrg : clu.getAdminOrgs()) { if (adminOrg.getTypeKey().equals(CourseAssemblerConstants.ADMIN_ORG)) { courseAdminOrgs.add(adminOrg.getOrgId()); } if (adminOrg.getTypeKey().equals(CourseAssemblerConstants.SUBJECT_ORG)) { courseSubjectOrgs.add(adminOrg.getOrgId()); } } course.setUnitsDeployment(courseAdminOrgs); course.setUnitsContentOwner(courseSubjectOrgs); course.setDescr(clu.getDescr()); course.setDuration(clu.getStdDuration()); course.setEffectiveDate(clu.getEffectiveDate()); course.setExpirationDate(clu.getExpirationDate()); //Fees //Fee justification List<CourseFeeInfo> fees = new ArrayList<CourseFeeInfo>(); List<CourseRevenueInfo> revenues = new ArrayList<CourseRevenueInfo>(); if (clu.getFeeInfo() != null) { course.setFeeJustification(clu.getFeeInfo().getDescr()); //Fees and revenues come from the same place but revenues have a special feeType for (CluFeeRecordInfo cluFeeRecord : clu.getFeeInfo().getCluFeeRecords()) { String feeType = cluFeeRecord.getFeeType(); if (CourseAssemblerConstants.COURSE_FINANCIALS_REVENUE_TYPE.equals(feeType)) { CourseRevenueInfo courseRevenue = new CourseRevenueInfo(); courseRevenue.setFeeType(feeType); courseRevenue.setAffiliatedOrgs(cluFeeRecord.getAffiliatedOrgs()); courseRevenue.setAttributes(cluFeeRecord.getAttributes()); courseRevenue.setId(cluFeeRecord.getId()); courseRevenue.setMeta(cluFeeRecord.getMeta()); revenues.add(courseRevenue); } else { CourseFeeInfo courseFee = new CourseFeeInfo(); courseFee.setFeeType(feeType); courseFee.setRateType(cluFeeRecord.getRateType()); courseFee.setDescr(cluFeeRecord.getDescr()); courseFee.setMeta(cluFeeRecord.getMeta()); courseFee.setId(cluFeeRecord.getId()); courseFee.setFeeAmounts(cluFeeRecord.getFeeAmounts()); courseFee.setAttributes(cluFeeRecord.getAttributes()); fees.add(courseFee); } } } course.setFees(fees); course.setRevenues(revenues); //Expenditures are mapped from accounting info if (course.getExpenditure() == null || clu.getAccountingInfo() == null) { course.setExpenditure(new CourseExpenditureInfo()); } if (clu.getAccountingInfo() != null) { course.getExpenditure().setAffiliatedOrgs(clu.getAccountingInfo().getAffiliatedOrgs()); } course.setId(clu.getId()); course.setTypeKey(clu.getTypeKey()); course.setTermsOffered(clu.getOfferedAtpTypes()); course.setPrimaryInstructor(clu.getPrimaryInstructor()); course.setInstructors(clu.getInstructors()); course.setStateKey(clu.getStateKey()); course.setSubjectArea(clu.getOfficialIdentifier().getDivision()); course.setTranscriptTitle(clu.getOfficialIdentifier().getShortName()); course.setMeta(clu.getMeta()); course.setVersion(clu.getVersion()); //Special topics code course.setSpecialTopicsCourse(false); for (LuCodeInfo luCode : clu.getLuCodes()) { if (CourseAssemblerConstants.COURSE_CODE_SPECIAL_TOPICS.equals(luCode.getType())) { course.setSpecialTopicsCourse(Boolean.parseBoolean(luCode.getValue())); break; } } //Pilot Course code course.setPilotCourse(false); for (LuCodeInfo luCode : clu.getLuCodes()) { if (CourseAssemblerConstants.COURSE_CODE_PILOT_COURSE.equals(luCode.getType())) { course.setPilotCourse(Boolean.parseBoolean(luCode.getValue())); break; } } // Don't make any changes to nested datastructures if this is if (!shallowBuild) { try { // Use the cluService to find Joints, then convert and add to the // course List<CluCluRelationInfo> cluClus = cluService.getCluCluRelationsByClu(clu.getId(), contextInfo); for (CluCluRelationInfo cluRel : cluClus) { if (cluRel.getTypeKey().equals(CourseAssemblerConstants.JOINT_RELATION_TYPE)) { CourseJointInfo jointInfo = null; if (cluRel.getCluId().equals(clu.getId())) { jointInfo = courseJointAssembler.assemble(cluRel, cluRel.getRelatedCluId(), null, false, contextInfo); } else { jointInfo = courseJointAssembler.assemble(cluRel, cluRel.getCluId(), null, false, contextInfo); } if (jointInfo != null) { course.getJoints().add(jointInfo); } } } } catch (DoesNotExistException e) { } catch (Exception e) { throw new AssemblyException("Error getting course joints", e); } try { // Use the cluService to find formats, then convert and add to // the course List<CluInfo> formats = cluService.getRelatedClusByCluAndRelationType(course.getId(), CourseAssemblerConstants.COURSE_FORMAT_RELATION_TYPE, contextInfo); for (CluInfo format : formats) { FormatInfo formatInfo = formatAssembler.assemble(format, null, false, contextInfo); course.getFormats().add(formatInfo); } } catch (DoesNotExistException e) { } catch (Exception e) { throw new AssemblyException("Error getting related formats", e); } try { //Set Credit and Grading options List<CluResultInfo> cluResults = cluService.getCluResultByClu(course.getId(), contextInfo); List<ResultValuesGroupInfo> creditOptions = assembleCreditOptions(cluResults, contextInfo); course.setCreditOptions(creditOptions); List<String> gradingOptions = assembleGradingOptions(cluResults); course.setGradingOptions(gradingOptions); } catch (DoesNotExistException e) { } catch (Exception e) { throw new AssemblyException("Error getting course results", e); } //Learning Objectives course.getCourseSpecificLOs() .addAll(cluAssemblerUtils.assembleLos(course.getId(), shallowBuild, contextInfo)); } //Remove special cases for grading options boolean isAudit = course.getGradingOptions() .remove(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_AUDIT); //course.setAttributeValue(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_AUDIT, Boolean.toString(isAudit)); return course; } @Override public BaseDTOAssemblyNode<CourseInfo, CluInfo> disassemble(CourseInfo course, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException, PermissionDeniedException { if (course == null) { // FIXME Unsure now if this is an exception or just return null or // empty assemblyNode LOG.error("Course to disassemble is null!"); throw new AssemblyException("Course can not be null"); } BaseDTOAssemblyNode<CourseInfo, CluInfo> result = new BaseDTOAssemblyNode<CourseInfo, CluInfo>(this); CluInfo clu; try { clu = (NodeOperation.UPDATE == operation) ? cluService.getClu(course.getId(), contextInfo) : new CluInfo(); } catch (Exception e) { throw new AssemblyException("Error getting existing learning unit during course update", e); } // Create the id if it's not there already(important for creating // relations) clu.setId(UUIDHelper.genStringUUID(course.getId())); if (null == course.getId()) { course.setId(clu.getId()); } clu.setTypeKey(CluServiceConstants.CREDIT_COURSE_LU_TYPE_KEY); clu.setStateKey(course.getStateKey()); clu.setIsEnrollable(false); CluIdentifierInfo identifier = new CluIdentifierInfo(); identifier.setTypeKey(CourseAssemblerConstants.COURSE_OFFICIAL_IDENT_TYPE); identifier.setStateKey(course.getStateKey()); identifier.setLongName(course.getCourseTitle()); identifier.setShortName(course.getTranscriptTitle()); identifier.setSuffixCode(course.getCourseNumberSuffix()); identifier.setDivision(course.getSubjectArea()); identifier.setCode(course.getCode()); //Custom logic to set the level, if level not provided if (StringUtils.hasText(course.getLevel())) { identifier.setLevel(course.getLevel()); } else if (course.getCourseNumberSuffix() != null && course.getCourseNumberSuffix().length() >= 3) { identifier.setLevel(course.getCourseNumberSuffix().substring(0, 1) + "00"); } clu.setOfficialIdentifier(identifier); clu.setAdminOrgs(new ArrayList<AdminOrgInfo>()); // Use the Course Variation assembler to disassemble the variations // copy all fields //Remove any existing variations or crosslistings for (Iterator<CluIdentifierInfo> iter = clu.getAlternateIdentifiers().iterator(); iter.hasNext();) { CluIdentifierInfo cluIdentifier = iter.next(); if (CourseAssemblerConstants.COURSE_VARIATION_IDENT_TYPE.equals(cluIdentifier.getTypeKey()) || CourseAssemblerConstants.COURSE_CROSSLISTING_IDENT_TYPE.equals(cluIdentifier.getTypeKey())) { iter.remove(); } } //Add in variations for (CourseVariationInfo variation : course.getVariations()) { CluIdentifierInfo cluIdentifier = new CluIdentifierInfo(); cluIdentifier.setId(variation.getId()); cluIdentifier.setTypeKey(CourseAssemblerConstants.COURSE_VARIATION_IDENT_TYPE); cluIdentifier.setCode(identifier.getCode()); cluIdentifier.setSuffixCode(course.getCourseNumberSuffix()); cluIdentifier.setDivision(course.getSubjectArea()); cluIdentifier.setVariation(variation.getVariationCode()); cluIdentifier.setLongName(variation.getVariationTitle()); cluIdentifier.setStateKey(course.getStateKey()); clu.getAlternateIdentifiers().add(cluIdentifier); } //Add in crosslistings for (CourseCrossListingInfo crossListing : course.getCrossListings()) { CluIdentifierInfo cluIdentifier = new CluIdentifierInfo(); cluIdentifier.setId(crossListing.getId()); cluIdentifier.setTypeKey(CourseAssemblerConstants.COURSE_CROSSLISTING_IDENT_TYPE); cluIdentifier.setSuffixCode(crossListing.getCourseNumberSuffix()); cluIdentifier.setDivision(crossListing.getSubjectArea()); cluIdentifier.setStateKey(course.getStateKey()); cluIdentifier.setOrgId(crossListing.getSubjectOrgId()); cluIdentifier.setAttributes(crossListing.getAttributes()); cluIdentifier.setCode(crossListing.getCode()); clu.getAlternateIdentifiers().add(cluIdentifier); } List<AdminOrgInfo> adminOrgInfos = new ArrayList<AdminOrgInfo>(); for (String org : course.getUnitsDeployment()) { AdminOrgInfo adminOrg = new AdminOrgInfo(); adminOrg.setTypeKey(CourseAssemblerConstants.ADMIN_ORG); adminOrg.setOrgId(org); adminOrgInfos.add(adminOrg); } clu.getAdminOrgs().addAll(adminOrgInfos); List<AdminOrgInfo> subjectOrgs = new ArrayList<AdminOrgInfo>(); for (String subOrg : course.getUnitsContentOwner()) { AdminOrgInfo subjectOrg = new AdminOrgInfo(); subjectOrg.setTypeKey(CourseAssemblerConstants.SUBJECT_ORG); subjectOrg.setOrgId(subOrg); subjectOrgs.add(subjectOrg); } clu.getAdminOrgs().addAll(subjectOrgs); clu.setAttributes(course.getAttributes()); clu.setCampusLocations(course.getCampusLocations()); clu.setDescr(course.getDescr()); clu.setStdDuration(course.getDuration()); //Default course effective dates to the atps if entered if (org.apache.commons.lang.StringUtils.isNotBlank(course.getStartTerm())) { try { AtpInfo startAtp = atpService.getAtp(course.getStartTerm(), contextInfo); course.setEffectiveDate(startAtp.getStartDate()); } catch (Exception e) { throw new AssemblyException("Error getting start term Atp.", e); } } if (course.getEndTerm() != null) { try { AtpInfo endAtp = atpService.getAtp(course.getEndTerm(), contextInfo); course.setExpirationDate(endAtp.getEndDate()); } catch (Exception e) { throw new AssemblyException("Error getting end term Atp.", e); } } clu.setEffectiveDate(course.getEffectiveDate()); clu.setExpirationDate(course.getExpirationDate()); clu.setOfferedAtpTypes(course.getTermsOffered()); clu.setPrimaryInstructor(course.getPrimaryInstructor()); clu.setIntensity(course.getOutOfClassHours()); clu.setInstructors(course.getInstructors()); clu.setExpectedFirstAtp(course.getStartTerm()); clu.setLastAtp(course.getEndTerm()); clu.setMeta(course.getMeta()); clu.setVersion(course.getVersion()); // Add the Clu to the result result.setNodeData(clu); result.setOperation(operation); result.setBusinessDTORef(course); // Use the Format assembler to disassemble the formats and relations List<BaseDTOAssemblyNode<?, ?>> formatResults; try { formatResults = disassembleFormats(clu.getId(), course, operation, contextInfo); result.getChildNodes().addAll(formatResults); } catch (DoesNotExistException e) { } catch (Exception e) { throw new AssemblyException("Error while disassembling format", e); } // Use the CourseJoint assembler to disassemble the CourseJoints and // relations List<BaseDTOAssemblyNode<?, ?>> courseJointResults = disassembleJoints(clu.getId(), course, operation, contextInfo); result.getChildNodes().addAll(courseJointResults); //Disassemble the CluResults (grading and credit options) //Special code to take audit from attributes and put into options for (AttributeInfo attr : course.getAttributes()) { if (attr.getKey() != null) { if (attr.getKey().equals(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_AUDIT) && "true".equals(attr.getValue())) { if (!course.getGradingOptions() .contains(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_AUDIT)) { course.getGradingOptions().add(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_AUDIT); } break; } } } // Patch to disassemble pass/fail option as well (KSLAB-2633) for (AttributeInfo attr : course.getAttributes()) { if (attr.getKey() != null) { if (attr.getKey().equals(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_PASSFAIL) && "true".equals(attr.getValue())) { if (!course.getGradingOptions() .contains(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_PASSFAIL)) { course.getGradingOptions().add(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_PASSFAIL); } break; } } } List<CluResultInfo> cluResultList; try { cluResultList = cluService.getCluResultByClu(clu.getId(), contextInfo); } catch (DoesNotExistException e) { cluResultList = Collections.emptyList(); } catch (Exception e) { throw new AssemblyException("Error getting cluResults", e); } List<BaseDTOAssemblyNode<?, ?>> creditOutcomes = disassembleCreditOutcomes(course, clu, cluResultList, operation, contextInfo); result.getChildNodes().addAll(creditOutcomes); BaseDTOAssemblyNode<?, ?> gradingOptions = disassembleGradingOptions(clu.getId(), course.getStateKey(), course.getGradingOptions(), cluResultList, operation); result.getChildNodes().add(gradingOptions); //Use the LoAssembler to disassemble Los try { List<BaseDTOAssemblyNode<?, ?>> loResults; loResults = disassembleLos(clu.getId(), course, operation, contextInfo); result.getChildNodes().addAll(loResults); } catch (Exception e) { throw new AssemblyException("Error while disassembling los", e); } //add the special topics code if it did not exist, or remove if it was not wanted boolean alreadyHadSpecialTopicsCode = false; for (Iterator<LuCodeInfo> luCodeIterator = clu.getLuCodes().iterator(); luCodeIterator.hasNext();) { LuCodeInfo luCode = luCodeIterator.next(); if (CourseAssemblerConstants.COURSE_CODE_SPECIAL_TOPICS.equals(luCode.getType())) { alreadyHadSpecialTopicsCode = true; if (!course.isSpecialTopicsCourse()) { luCodeIterator.remove(); } break; } } if (!alreadyHadSpecialTopicsCode && course.isSpecialTopicsCourse()) { LuCodeInfo luCode = new LuCodeInfo(); luCode.setType(CourseAssemblerConstants.COURSE_CODE_SPECIAL_TOPICS); luCode.setValue("true"); clu.getLuCodes().add(luCode); } //add the special topics code if it did not exist, or remove if it was not wanted boolean alreadyHadPilotCourseCode = false; for (Iterator<LuCodeInfo> luCodeIterator = clu.getLuCodes().iterator(); luCodeIterator.hasNext();) { LuCodeInfo luCode = luCodeIterator.next(); if (CourseAssemblerConstants.COURSE_CODE_PILOT_COURSE.equals(luCode.getType())) { alreadyHadPilotCourseCode = true; if (!course.isPilotCourse()) { luCodeIterator.remove(); } break; } } if (!alreadyHadPilotCourseCode && course.isPilotCourse()) { LuCodeInfo luCode = new LuCodeInfo(); luCode.setType(CourseAssemblerConstants.COURSE_CODE_PILOT_COURSE); luCode.setValue("true"); clu.getLuCodes().add(luCode); } //FEES if (clu.getFeeInfo() == null) { clu.setFeeInfo(new CluFeeInfo()); } clu.getFeeInfo().setDescr(course.getFeeJustification()); clu.getFeeInfo().getCluFeeRecords().clear(); for (CourseRevenueInfo courseRevenue : course.getRevenues()) { CluFeeRecordInfo cluFeeRecord = new CluFeeRecordInfo(); cluFeeRecord.setFeeType(CourseAssemblerConstants.COURSE_FINANCIALS_REVENUE_TYPE); cluFeeRecord.setRateType(CourseAssemblerConstants.COURSE_FINANCIALS_REVENUE_TYPE); cluFeeRecord.setAttributes(courseRevenue.getAttributes()); cluFeeRecord.setAffiliatedOrgs(courseRevenue.getAffiliatedOrgs()); cluFeeRecord.setId(courseRevenue.getId()); cluFeeRecord.setMeta(courseRevenue.getMeta()); clu.getFeeInfo().getCluFeeRecords().add(cluFeeRecord); } for (CourseFeeInfo courseFee : course.getFees()) { CluFeeRecordInfo cluFeeRecord = new CluFeeRecordInfo(); cluFeeRecord.setFeeType(courseFee.getFeeType()); cluFeeRecord.setRateType(courseFee.getRateType()); cluFeeRecord.setDescr(courseFee.getDescr()); cluFeeRecord.setMeta(courseFee.getMeta()); cluFeeRecord.setId(courseFee.getId()); cluFeeRecord.setFeeAmounts(courseFee.getFeeAmounts()); cluFeeRecord.setAttributes(courseFee.getAttributes()); clu.getFeeInfo().getCluFeeRecords().add(cluFeeRecord); } if (clu.getAccountingInfo() == null || course.getExpenditure() == null) { clu.setAccountingInfo(new CluAccountingInfo()); } if (course.getExpenditure() != null) { clu.getAccountingInfo().setAffiliatedOrgs(course.getExpenditure().getAffiliatedOrgs()); clu.getAccountingInfo().setAttributes(course.getExpenditure().getAttributes()); } return result; } /** * Only checks for floats with format dddd.dddd where d's are optional (digits). * The decimal point is also optional. * Negative floats not checked. Also, floats with other formats not checked. * @param str * @return true if it's a simple float (could be an integer) */ private boolean _checkIfSimpleFloat(String str) { String DIGITS = "0123456789"; String DIGITS_AND_DOT = DIGITS + "."; boolean foundDot = false; int numDigits = 0; for (int i = 0; i < str.length(); i++) { String ch = str.substring(i, i + 1); if (DIGITS_AND_DOT.indexOf(ch) < 0) { return false; // } if (".".equals(ch)) { if (!foundDot) { foundDot = true; } else { // Found a second dot--not valid return false; } } if (DIGITS.indexOf(ch) >= 0) { numDigits++; } } // Just checks if digits are correct. return numDigits > 0; } /** * If resultValueKeys contains IDs, then look up its values and create a list of values * @param resultValueKeys Potential list of IDs * @return List of values */ private List<String> _computeResultValues(List<String> resultValueKeys, ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException { List<String> values = new ArrayList<String>(); if (resultValueKeys != null && !resultValueKeys.isEmpty()) { String firstKey = resultValueKeys.get(0); if (_checkIfSimpleFloat(firstKey)) { // No modification needed return resultValueKeys; } // Assume that resultValueKeys contains IDs for (String key : resultValueKeys) { ResultValueInfo rv = lrcService.getResultValue(key, contextInfo); String value = rv.getValue(); values.add(value); } } return values; } private List<BaseDTOAssemblyNode<?, ?>> disassembleCreditOutcomes(CourseInfo course, CluInfo clu, List<CluResultInfo> currentCluResults, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException, NumberFormatException { List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>(); String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_CREDITS; //See if we need to create any new lrcs if (NodeOperation.DELETE != operation) { //Find all the existing LRCs for the following three types Set<String> resultValueGroupIds = new HashSet<String>(); try { try { resultValueGroupIds.addAll(lrcService.getResultValuesGroupKeysByType( LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED, contextInfo)); } catch (DoesNotExistException e) { } try { resultValueGroupIds.addAll(lrcService.getResultValuesGroupKeysByType( LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE, contextInfo)); } catch (DoesNotExistException e) { } try { resultValueGroupIds.addAll(lrcService.getResultValuesGroupKeysByType( LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE, contextInfo)); } catch (DoesNotExistException e) { } //Create any LRCs that do not yet exist for (ResultValuesGroupInfo creditOption : course.getCreditOptions()) { String id = null; String type = null; List<String> resultValues = null; ResultValueRangeInfo resultValueRange = null; //Depending on the type, set the id, type and result values differently if (LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED.equals(creditOption.getTypeKey())) { float fixedCreditValue = Float.parseFloat(creditOption.getResultValueRange().getMinValue()); id = CourseAssemblerConstants.COURSE_RESULT_COMP_CREDIT_PREFIX + fixedCreditValue; type = LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_FIXED; resultValues = new ArrayList<String>(); resultValues.add(String.valueOf(fixedCreditValue)); resultValueRange = new ResultValueRangeInfo(); resultValueRange.setMinValue(String.valueOf(fixedCreditValue)); resultValueRange.setMaxValue(String.valueOf(fixedCreditValue)); } else if (LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE .equals(creditOption.getTypeKey())) { List<String> resultVals = _computeResultValues(creditOption.getResultValueKeys(), contextInfo); Collections.sort(resultVals, new Comparator<String>() { public int compare(String o1, String o2) { return Float.compare(Float.parseFloat(o1), Float.parseFloat(o2)); } }); StringBuilder sb = new StringBuilder( CourseAssemblerConstants.COURSE_RESULT_COMP_CREDIT_PREFIX); for (Iterator<String> iter = resultVals.iterator(); iter.hasNext();) { float value = Float.parseFloat(iter.next()); sb.append(value); if (iter.hasNext()) { sb.append(","); } } id = sb.toString(); type = LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_MULTIPLE; resultValues = new ArrayList<String>(); resultValues.addAll(creditOption.getResultValueKeys()); } else if (LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE .equals(creditOption.getTypeKey())) { /* * For variable credits create a Result values that goes from min to max with the specified increment. * If no increment is specified, use 1.0 as the increment. The increment can be specified as a float. */ float minCredits = Float.parseFloat(creditOption.getResultValueRange().getMinValue()); float maxCredits = Float.parseFloat(creditOption.getResultValueRange().getMaxValue()); String creditValueIncr = creditOption.getResultValueRange().getIncrement(); float increment = (null != creditValueIncr && creditValueIncr.length() > 0) ? Float.parseFloat(creditValueIncr) : defaultCreditIncrement; id = CourseAssemblerConstants.COURSE_RESULT_COMP_CREDIT_PREFIX + String.valueOf(minCredits) + "-" + String.valueOf(maxCredits); //Add in the increment to the key (logic is duplicated in LRC service) if (creditValueIncr != null && !"1".equals(creditValueIncr) && !"1.0".equals(creditValueIncr)) { id += (" by " + creditValueIncr); } type = LrcServiceConstants.RESULT_VALUES_GROUP_TYPE_KEY_RANGE; resultValues = new ArrayList<String>(); for (float i = minCredits; i <= maxCredits; i += increment) { // TODO: Refer KSCM-1910 resultValues.add( LrcServiceConstants.RESULT_VALUE_KEY_CREDIT_DEGREE_PREFIX + String.valueOf(i)); } resultValueRange = new ResultValueRangeInfo(); resultValueRange.setMinValue(String.valueOf(minCredits)); resultValueRange.setMaxValue(String.valueOf(maxCredits)); resultValueRange.setIncrement(String.valueOf(increment)); } else { resultValues = Collections.emptyList(); } //Set the id creditOption.setKey(id); //Ensure the resultValueKey has the proper prefix // TODO: Comment: Shouldn't muck around with altering IDs // Assumption is result values contains IDs, not string values like 2.0 // String resultValueKeyPrefix = "kuali.result.value.credit.degree."; // for(int i = 0; i < resultValues.size(); i++){ // if (!resultValues.get(i).contains("kuali.result.value")){ //only add the prefix if this is not a proper key // String ithResultVal = resultValues.get(i); // resultValues.set(i,resultValueKeyPrefix+Float.parseFloat(ithResultVal)); // } // } //Create a new result component if (id != null && !resultValueGroupIds.contains(id)) { //Build the new ResultValuesGroup ResultValuesGroupInfo resultValueGroup = new ResultValuesGroupInfo(); resultValueGroup.setKey(id); resultValueGroup.setTypeKey(type); if (DtoConstants.STATE_DRAFT.equals(course.getStateKey())) { resultValueGroup.setStateKey(LrcServiceConstants.RESULT_GROUPS_STATE_DRAFT); } else if (DtoConstants.STATE_APPROVED.equals(course.getStateKey())) { resultValueGroup.setStateKey(LrcServiceConstants.RESULT_GROUPS_STATE_APPROVED); } else { resultValueGroup.setStateKey(LrcServiceConstants.RESULT_GROUPS_STATE_RETIRED); } resultValueGroup.setResultScaleKey(LrcServiceConstants.RESULT_SCALE_KEY_CREDIT_DEGREE); resultValueGroup.setResultValueKeys(resultValues); resultValueGroup.setResultValueRange(resultValueRange); resultValueGroup.setName(creditOption.getName()); RichTextInfo creditOptionDescr = creditOption.getDescr(); if (creditOptionDescr != null) { RichTextInfo descr = new RichTextInfo(); descr.setPlain(creditOptionDescr.getPlain()); descr.setFormatted(creditOptionDescr.getFormatted()); resultValueGroup.setDescr(descr); } BaseDTOAssemblyNode<ResultValuesGroupInfo, ResultValuesGroupInfo> node = new BaseDTOAssemblyNode<ResultValuesGroupInfo, ResultValuesGroupInfo>( null); node.setOperation(NodeOperation.CREATE); node.setNodeData(resultValueGroup); node.setBusinessDTORef(creditOption); results.add(node); resultValueGroupIds.add(id); } } } catch (NumberFormatException e) { throw new AssemblyException("Invalid Arguments for credit outcome values", e); } catch (Exception e) { throw new AssemblyException("Error Assembling", e); } } //Now do dissassembly for the actual clu-lrc relations and result options // Get the current options and put them in a map of option type id/cluResult Map<String, List<CluResultInfo>> currentResults = new HashMap<String, List<CluResultInfo>>(); //If this is not a create, lookup the results for this clu if (!NodeOperation.CREATE.equals(operation)) { for (CluResultInfo currentResult : currentCluResults) { if (courseResultType.equals(currentResult.getTypeKey())) { //There should only be one grading option per CluResult for credit outcomes if (currentResult.getResultOptions().size() == 1) { //Create a mapping to a list of cluresults with the same result componentId String resultComponentId = currentResult.getResultOptions().get(0).getResultComponentId(); if (!currentResults.containsKey(resultComponentId)) { currentResults.put(resultComponentId, new ArrayList<CluResultInfo>()); } currentResults.get(resultComponentId).add(currentResult); } else { LOG.warn("Credit Results should have exactly one result option each"); } } } } //Loop through options on the course, if they are new, create a new cluResult for (ResultValuesGroupInfo creditOption : course.getCreditOptions()) { if (NodeOperation.CREATE == operation || (NodeOperation.UPDATE == operation && !currentResults.containsKey(creditOption.getKey()))) { ResultOptionInfo resultOption = new ResultOptionInfo(); resultOption.setStateKey(course.getStateKey()); resultOption.setResultComponentId(creditOption.getKey()); CluResultInfo cluResult = new CluResultInfo(); cluResult.setCluId(clu.getId()); cluResult.setStateKey(course.getStateKey()); cluResult.setTypeKey(courseResultType); cluResult.getResultOptions().add(resultOption); BaseDTOAssemblyNode<ResultValuesGroupInfo, CluResultInfo> cluResultNode = new BaseDTOAssemblyNode<ResultValuesGroupInfo, CluResultInfo>( null); cluResultNode.setNodeData(cluResult); cluResultNode.setOperation(NodeOperation.CREATE); results.add(cluResultNode); } else if (NodeOperation.UPDATE == operation && currentResults.containsKey(creditOption.getKey())) { //Get the list from the map and remove an entry, if the list is empty then remove it from the map List<CluResultInfo> cluResults = currentResults.get(creditOption.getKey()); cluResults.remove(cluResults.size() - 1); if (cluResults.isEmpty()) { currentResults.remove(creditOption.getKey()); } } } //Delete the leftovers for (Entry<String, List<CluResultInfo>> entry : currentResults.entrySet()) { for (CluResultInfo cluResult : entry.getValue()) { BaseDTOAssemblyNode<ResultValuesGroupInfo, CluResultInfo> cluResultNode = new BaseDTOAssemblyNode<ResultValuesGroupInfo, CluResultInfo>( null); cluResultNode.setNodeData(cluResult); cluResultNode.setOperation(NodeOperation.DELETE); results.add(cluResultNode); } } return results; } private List<String> assembleGradingOptions(List<CluResultInfo> cluResults) { String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_GRADE; List<String> results = new ArrayList<String>(); //Loop through all the CluResults to find the one with the matching type for (CluResultInfo cluResult : cluResults) { if (courseResultType.equals(cluResult.getTypeKey())) { //Loop through all options and add to the list of Strings for (ResultOptionInfo resultOption : cluResult.getResultOptions()) { results.add(resultOption.getResultComponentId()); } break; } } return results; } // private List<ResultValuesGroupInfo> assembleCreditOptions(List<CluResultInfo> cluResults, ContextInfo contextInfo) throws AssemblyException { String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_CREDITS; List<ResultValuesGroupInfo> results = new ArrayList<ResultValuesGroupInfo>(); //Loop through all the CluResults to find the one with the matching type for (CluResultInfo cluResult : cluResults) { if (courseResultType.equals(cluResult.getTypeKey())) { //Loop through all options and add to the list of Strings for (ResultOptionInfo resultOption : cluResult.getResultOptions()) { try { if (resultOption.getResultComponentId() != null) { ResultValuesGroupInfo resultValuesGroup = lrcService .getResultValuesGroup(resultOption.getResultComponentId(), contextInfo); results.add(resultValuesGroup); } } catch (DoesNotExistException e) { LOG.warn("Course Credit option:" + resultOption.getId() + " refers to non-existant ResultValuesGroupInfo " + resultOption.getResultComponentId()); } catch (Exception e) { throw new AssemblyException("Error getting ResultValuesGroupInfo", e); } } } } return results; } // TODO Use CluAssemblerUtils private List<BaseDTOAssemblyNode<?, ?>> disassembleLos(String cluId, CourseInfo course, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException { // TODO Auto-generated method stub List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>(); // Get the current formats and put them in a map of format id/relation // id Map<String, CluLoRelationInfo> currentCluLoRelations = new HashMap<String, CluLoRelationInfo>(); try { List<CluLoRelationInfo> cluLoRelations = cluService.getCluLoRelationsByClu(cluId, contextInfo); for (CluLoRelationInfo cluLoRelation : cluLoRelations) { if (CourseAssemblerConstants.COURSE_LO_COURSE_SPECIFIC_RELATION .equals(cluLoRelation.getTypeKey())) { currentCluLoRelations.put(cluLoRelation.getLoId(), cluLoRelation); } } } catch (DoesNotExistException e) { } catch (Exception e) { throw new AssemblyException("Error finding related Los"); } // Loop through all the los in this clu for (LoDisplayInfo loDisplay : course.getCourseSpecificLOs()) { // If this is a clu create/new lo update then all los will be created if (NodeOperation.CREATE == operation || (NodeOperation.UPDATE == operation && !currentCluLoRelations.containsKey(loDisplay.getLoInfo().getId()))) { // the lo does not exist, so create // Assemble and add the lo loDisplay.getLoInfo().setId(null); loDisplay.getLoInfo().setStateKey(course.getStateKey()); BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler.disassemble(loDisplay, NodeOperation.CREATE, contextInfo); results.add(loNode); // Create the relationship and add it as well CluLoRelationInfo relation = new CluLoRelationInfo(); relation.setCluId(cluId); relation.setLoId(loNode.getNodeData().getId()); relation.setTypeKey(CourseAssemblerConstants.COURSE_LO_COURSE_SPECIFIC_RELATION); relation.setStateKey(course.getStateKey()); BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo> relationNode = new BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo>( null); relationNode.setNodeData(relation); relationNode.setOperation(NodeOperation.CREATE); results.add(relationNode); } else if (NodeOperation.UPDATE == operation && currentCluLoRelations.containsKey(loDisplay.getLoInfo().getId())) { loDisplay.getLoInfo().setStateKey(course.getStateKey()); // If the clu already has this lo, then just update the lo BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler.disassemble(loDisplay, NodeOperation.UPDATE, contextInfo); results.add(loNode); // remove this entry from the map so we can tell what needs to // be deleted at the end currentCluLoRelations.remove(loDisplay.getLoInfo().getId()); } else if (NodeOperation.DELETE == operation && currentCluLoRelations.containsKey(loDisplay.getLoInfo().getId())) { // Delete the Format and its relation CluLoRelationInfo relationToDelete = currentCluLoRelations.get(loDisplay.getLoInfo().getId()); BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo>( null); relationToDeleteNode.setNodeData(relationToDelete); relationToDeleteNode.setOperation(NodeOperation.DELETE); results.add(relationToDeleteNode); BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler.disassemble(loDisplay, NodeOperation.DELETE, contextInfo); results.add(loNode); // remove this entry from the map so we can tell what needs to // be deleted at the end currentCluLoRelations.remove(loDisplay.getLoInfo().getId()); } } // Now any leftover lo ids are no longer needed, so delete // los and relations for (Entry<String, CluLoRelationInfo> entry : currentCluLoRelations.entrySet()) { // Create a new relation with the id of the relation we want to // delete CluLoRelationInfo relationToDelete = entry.getValue(); BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo>( null); relationToDeleteNode.setNodeData(relationToDelete); relationToDeleteNode.setOperation(NodeOperation.DELETE); results.add(relationToDeleteNode); try { LoInfo loToDelete = loService.getLo(entry.getKey(), contextInfo); LoDisplayInfo loDisplayToDelete = loAssembler.assemble(loToDelete, null, false, contextInfo); BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler.disassemble(loDisplayToDelete, NodeOperation.DELETE, contextInfo); results.add(loNode); } catch (DoesNotExistException e) { LOG.warn("Trying to delete non exsistant LO: {}", entry.getKey()); } catch (Exception e) { throw new AssemblyException("Error disassembling LOs", e); } } return results; } private BaseDTOAssemblyNode<?, ?> disassembleGradingOptions(String cluId, String courseState, List<String> options, List<CluResultInfo> currentCluResults, NodeOperation operation) throws AssemblyException { BaseDTOAssemblyNode<List<String>, CluResultInfo> cluResultNode = new BaseDTOAssemblyNode<List<String>, CluResultInfo>( null); String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_GRADE; String resultsDescription = "Grading options"; String resultDescription = "Grading option"; // Get the current options and put them in a map of option type id/cluResult Map<String, ResultOptionInfo> currentResults = new HashMap<String, ResultOptionInfo>(); CluResultInfo cluResult = null; //If this is not a create, lookup the results for this clu if (!NodeOperation.CREATE.equals(operation)) { for (CluResultInfo currentResult : currentCluResults) { if (courseResultType.equals(currentResult.getTypeKey())) { cluResult = currentResult; if (NodeOperation.DELETE.equals(operation)) { //if this is a delete, then we only need the CluResultInfo cluResultNode.setOperation(NodeOperation.DELETE); } else { //Find all the Result options and store in a map for easy access later cluResultNode.setOperation(NodeOperation.UPDATE); for (ResultOptionInfo resultOption : currentResult.getResultOptions()) { currentResults.put(resultOption.getResultComponentId(), resultOption); } } break; } } } //If this is a delete we don't need the result options, just the CluResultInfo if (!NodeOperation.DELETE.equals(operation)) { if (cluResult == null) { //Create a new resultInfo of the given type if one does not exist and set operation to Create cluResult = new CluResultInfo(); cluResult.setCluId(cluId); cluResult.setStateKey(courseState); cluResult.setTypeKey(courseResultType); RichTextInfo desc = new RichTextInfo(); desc.setPlain(resultsDescription); cluResult.setDescr(desc); cluResult.setEffectiveDate(new Date()); cluResultNode.setOperation(NodeOperation.CREATE); } cluResult.setResultOptions(new ArrayList<ResultOptionInfo>()); // Loop through all the credit options in this course for (String optionType : options) { if (currentResults.containsKey(optionType)) { //If the option exists already copy it to the new list of result options ResultOptionInfo resultOptionInfo = currentResults.get(optionType); cluResult.getResultOptions().add(resultOptionInfo); } else { //Otherwise create a new result option ResultOptionInfo resultOptionInfo = new ResultOptionInfo(); RichTextInfo desc = new RichTextInfo(); desc.setPlain(resultDescription); resultOptionInfo.setDescr(desc); resultOptionInfo.setResultComponentId(optionType); resultOptionInfo.setStateKey(courseState); cluResult.getResultOptions().add(resultOptionInfo); } } } cluResultNode.setNodeData(cluResult); return cluResultNode; } // TODO This is pretty much a copy of the FormatAssembler's // disassembleActivities code... maybe can be made generic private List<BaseDTOAssemblyNode<?, ?>> disassembleFormats(String nodeId, CourseInfo course, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException, DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>(); // Get the current formats and put them in a map of format id/relation // id Map<String, String> currentformatIds = new HashMap<String, String>(); if (!NodeOperation.CREATE.equals(operation)) { try { List<CluCluRelationInfo> formatRelationships = cluService.getCluCluRelationsByClu(course.getId(), contextInfo); formatRelationships = (null == formatRelationships) ? new ArrayList<CluCluRelationInfo>() : formatRelationships; for (CluCluRelationInfo formatRelation : formatRelationships) { if (CourseAssemblerConstants.COURSE_FORMAT_RELATION_TYPE.equals(formatRelation.getTypeKey())) { currentformatIds.put(formatRelation.getRelatedCluId(), formatRelation.getId()); } } } catch (DoesNotExistException e) { } catch (InvalidParameterException e) { throw new AssemblyException("Error getting related formats", e); } catch (MissingParameterException e) { throw new AssemblyException("Error getting related formats", e); } catch (OperationFailedException e) { throw new AssemblyException("Error getting related formats", e); } } // Loop through all the formats in this course for (FormatInfo format : course.getFormats()) { // If this is a course create/new format update then all formats will be created if (NodeOperation.CREATE == operation || (NodeOperation.UPDATE == operation && !currentformatIds.containsKey(format.getId()))) { // the format does not exist, so create // Assemble and add the format format.setStateKey(course.getStateKey()); BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler.disassemble(format, NodeOperation.CREATE, contextInfo); results.add(formatNode); // Create the relationship and add it as well CluCluRelationInfo relation = new CluCluRelationInfo(); relation.setCluId(nodeId); relation.setRelatedCluId(formatNode.getNodeData().getId());// this // should // already // be set even if // it's a create relation.setTypeKey(CourseAssemblerConstants.COURSE_FORMAT_RELATION_TYPE); relation.setStateKey(course.getStateKey()); BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo> relationNode = new BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo>( null); relationNode.setNodeData(relation); relationNode.setOperation(NodeOperation.CREATE); results.add(relationNode); } else if (NodeOperation.UPDATE == operation && currentformatIds.containsKey(format.getId())) { // If the course already has this format, then just update the // format format.setStateKey(course.getStateKey()); BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler.disassemble(format, NodeOperation.UPDATE, contextInfo); results.add(formatNode); // remove this entry from the map so we can tell what needs to // be deleted at the end currentformatIds.remove(format.getId()); } else if (NodeOperation.DELETE == operation && currentformatIds.containsKey(format.getId())) { // Delete the Format and its relation CluCluRelationInfo relationToDelete = new CluCluRelationInfo(); relationToDelete.setId(currentformatIds.get(format.getId())); BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo>( null); relationToDeleteNode.setNodeData(relationToDelete); relationToDeleteNode.setOperation(NodeOperation.DELETE); results.add(relationToDeleteNode); BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler.disassemble(format, NodeOperation.DELETE, contextInfo); results.add(formatNode); // remove this entry from the map so we can tell what needs to // be deleted at the end currentformatIds.remove(format.getId()); } } // Now any leftover format ids are no longer needed, so delete // formats and relations. These formats have to be assembled first before they can be marked for deletion for (Entry<String, String> entry : currentformatIds.entrySet()) { // Create a new relation with the id of the relation we want to // delete CluCluRelationInfo relationToDelete = new CluCluRelationInfo(); relationToDelete.setId(entry.getValue()); BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo>( null); relationToDeleteNode.setNodeData(relationToDelete); relationToDeleteNode.setOperation(NodeOperation.DELETE); results.add(relationToDeleteNode); CluInfo formatCluToDelete = cluService.getClu(entry.getKey(), new ContextInfo()); FormatInfo formatToDelete = formatAssembler.assemble(formatCluToDelete, null, false, contextInfo); BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler.disassemble(formatToDelete, NodeOperation.DELETE, contextInfo); results.add(formatNode); } return results; } private List<CourseVariationInfo> assembleVariations(List<CluIdentifierInfo> cluIdents) { List<CourseVariationInfo> variations = new ArrayList<CourseVariationInfo>(); if (cluIdents != null) { for (CluIdentifierInfo cluIdent : cluIdents) { if (cluIdent.getTypeKey() != null && cluIdent.getTypeKey().equals(CourseAssemblerConstants.COURSE_VARIATION_IDENT_TYPE)) { CourseVariationInfo variation = new CourseVariationInfo(); variation.setId(cluIdent.getId()); variation.setTypeKey(cluIdent.getTypeKey()); variation.setCourseNumberSuffix(cluIdent.getSuffixCode()); variation.setSubjectArea(cluIdent.getDivision()); variation.setVariationCode(cluIdent.getVariation()); variation.setVariationTitle(cluIdent.getLongName()); variations.add(variation); } } } return variations; } private List<CourseCrossListingInfo> assembleCrossListings(List<CluIdentifierInfo> cluIdents) throws AssemblyException { List<CourseCrossListingInfo> crossListings = new ArrayList<CourseCrossListingInfo>(); if (cluIdents != null) { for (CluIdentifierInfo cluIdent : cluIdents) { if (cluIdent.getTypeKey() != null && cluIdent.getTypeKey().equals(CourseAssemblerConstants.COURSE_CROSSLISTING_IDENT_TYPE)) { CourseCrossListingInfo crosslisting = new CourseCrossListingInfo(); boolean found = false; for (AttributeInfo attr : cluIdent.getAttributes()) { if (attr.getKey().equals("courseId")) { found = true; try { CluInfo cluInfo = cluService.getClu(attr.getValue(), new ContextInfo()); crosslisting.setId(cluIdent.getId()); crosslisting.setCode(cluInfo.getOfficialIdentifier().getCode()); crosslisting.setAttributes(cluIdent.getAttributes()); crosslisting.setTypeKey(cluInfo.getTypeKey()); crosslisting.setCourseNumberSuffix(cluInfo.getOfficialIdentifier().getSuffixCode()); crosslisting.setSubjectArea(cluInfo.getOfficialIdentifier().getDivision()); crosslisting.setSubjectOrgId(cluIdent.getOrgId()); } catch (Exception e) { throw new AssemblyException("Error getting related clus", e); } } } if (!found) { crosslisting.setId(cluIdent.getId()); crosslisting.setCode(cluIdent.getCode()); crosslisting.setAttributes(cluIdent.getAttributes()); crosslisting.setTypeKey(cluIdent.getTypeKey()); crosslisting.setCourseNumberSuffix(cluIdent.getSuffixCode()); crosslisting.setSubjectArea(cluIdent.getDivision()); crosslisting.setSubjectOrgId(cluIdent.getOrgId()); } crossListings.add(crosslisting); } } } return crossListings; } // TODO This is pretty much a copy of the disassembleJoints // code... maybe can be made generic private List<BaseDTOAssemblyNode<?, ?>> disassembleJoints(String nodeId, CourseInfo course, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException, PermissionDeniedException { List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>(); // Get the current joints and put them in a map of joint id/relation // id Map<String, CluCluRelationInfo> currentJointIds = new HashMap<String, CluCluRelationInfo>(); if (!NodeOperation.CREATE.equals(operation)) { try { List<CluCluRelationInfo> jointRelationships = cluService.getCluCluRelationsByClu(course.getId(), contextInfo); for (CluCluRelationInfo jointRelation : jointRelationships) { if (CourseAssemblerConstants.JOINT_RELATION_TYPE.equals(jointRelation.getTypeKey())) { if (jointRelation.getCluId().equals(course.getId())) { cluService.getClu(jointRelation.getRelatedCluId(), contextInfo); currentJointIds.put(jointRelation.getId(), jointRelation); } else { cluService.getClu(jointRelation.getCluId(), contextInfo); currentJointIds.put(jointRelation.getId(), jointRelation); } } } } catch (DoesNotExistException e) { } catch (InvalidParameterException e) { throw new AssemblyException("Error getting related formats", e); } catch (MissingParameterException e) { throw new AssemblyException("Error getting related formats", e); } catch (OperationFailedException e) { throw new AssemblyException("Error getting related formats", e); } } // Loop through all the joints in this course for (CourseJointInfo joint : course.getJoints()) { // If this is a course create then all joints will be created if (NodeOperation.UPDATE.equals(operation) && joint.getRelationId() != null && currentJointIds.containsKey(joint.getRelationId())) { // remove this entry from the map so we can tell what needs to // be deleted at the end CluCluRelationInfo relation = currentJointIds.remove(joint.getRelationId()); relation.setRelatedCluId(joint.getCourseId()); relation.setStateKey(course.getStateKey()); BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo> jointNode = new BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo>( courseJointAssembler); jointNode.setBusinessDTORef(joint); jointNode.setNodeData(relation); jointNode.setOperation(NodeOperation.UPDATE); results.add(jointNode); } else if (!NodeOperation.DELETE.equals(operation)) { // the joint does not exist, so create cluclurelation BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo> jointNode = courseJointAssembler .disassemble(joint, NodeOperation.CREATE, contextInfo); jointNode.getNodeData().setCluId(nodeId); jointNode.getNodeData().setStateKey(course.getStateKey()); results.add(jointNode); } } // Now any leftover joint ids are no longer needed, so delete // joint relations for (String id : currentJointIds.keySet()) { // Create a new relation with the id of the relation we want to // delete CluCluRelationInfo relationToDelete = new CluCluRelationInfo(); relationToDelete.setId(id); BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo>( courseJointAssembler); relationToDeleteNode.setNodeData(relationToDelete); relationToDeleteNode.setOperation(NodeOperation.DELETE); results.add(relationToDeleteNode); } return results; } public void setCluService(CluService cluService) { this.cluService = cluService; } public void setFormatAssembler(FormatAssembler formatAssembler) { this.formatAssembler = formatAssembler; } public void setCourseJointAssembler(CourseJointAssembler courseJointAssembler) { this.courseJointAssembler = courseJointAssembler; } public void setLoAssembler(LoAssembler loAssembler) { this.loAssembler = loAssembler; } public void setLoService(LearningObjectiveService loService) { this.loService = loService; } public void setCluAssemblerUtils(CluAssemblerUtils cluAssemblerUtils) { this.cluAssemblerUtils = cluAssemblerUtils; } public void setLrcService(LRCService lrcService) { this.lrcService = lrcService; } public void setAtpService(AtpService atpService) { this.atpService = atpService; } public void setDefaultCreditIncrement(float defaultCreditIncrement) { this.defaultCreditIncrement = defaultCreditIncrement; } }