Coverage Report - org.kuali.student.lum.course.service.assembler.CourseAssembler
 
Classes in this File Line Coverage Branch Coverage Complexity
CourseAssembler
84%
554/653
77%
214/276
11.444
 
 1  
 /*
 2  
  * Copyright 2008 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 1.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl1.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.student.lum.course.service.assembler;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Collections;
 20  
 import java.util.Date;
 21  
 import java.util.HashMap;
 22  
 import java.util.HashSet;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 import java.util.Map.Entry;
 27  
 import java.util.Set;
 28  
 
 29  
 import org.apache.log4j.Logger;
 30  
 import org.kuali.student.common.util.UUIDHelper;
 31  
 import org.kuali.student.core.assembly.BOAssembler;
 32  
 import org.kuali.student.core.assembly.BaseDTOAssemblyNode;
 33  
 import org.kuali.student.core.assembly.BaseDTOAssemblyNode.NodeOperation;
 34  
 import org.kuali.student.core.assembly.data.AssemblyException;
 35  
 import org.kuali.student.core.dto.RichTextInfo;
 36  
 import org.kuali.student.core.exceptions.DoesNotExistException;
 37  
 import org.kuali.student.core.exceptions.InvalidParameterException;
 38  
 import org.kuali.student.core.exceptions.MissingParameterException;
 39  
 import org.kuali.student.core.exceptions.OperationFailedException;
 40  
 import org.kuali.student.lum.course.dto.CourseCrossListingInfo;
 41  
 import org.kuali.student.lum.course.dto.CourseExpenditureInfo;
 42  
 import org.kuali.student.lum.course.dto.CourseFeeInfo;
 43  
 import org.kuali.student.lum.course.dto.CourseInfo;
 44  
 import org.kuali.student.lum.course.dto.CourseJointInfo;
 45  
 import org.kuali.student.lum.course.dto.CourseRevenueInfo;
 46  
 import org.kuali.student.lum.course.dto.CourseVariationInfo;
 47  
 import org.kuali.student.lum.course.dto.FormatInfo;
 48  
 import org.kuali.student.lum.course.dto.LoDisplayInfo;
 49  
 import org.kuali.student.lum.lo.dto.LoInfo;
 50  
 import org.kuali.student.lum.lo.service.LearningObjectiveService;
 51  
 import org.kuali.student.lum.lrc.dto.ResultComponentInfo;
 52  
 import org.kuali.student.lum.lrc.service.LrcService;
 53  
 import org.kuali.student.lum.lu.dto.AdminOrgInfo;
 54  
 import org.kuali.student.lum.lu.dto.CluAccountingInfo;
 55  
 import org.kuali.student.lum.lu.dto.CluCluRelationInfo;
 56  
 import org.kuali.student.lum.lu.dto.CluFeeInfo;
 57  
 import org.kuali.student.lum.lu.dto.CluFeeRecordInfo;
 58  
 import org.kuali.student.lum.lu.dto.CluIdentifierInfo;
 59  
 import org.kuali.student.lum.lu.dto.CluInfo;
 60  
 import org.kuali.student.lum.lu.dto.CluLoRelationInfo;
 61  
 import org.kuali.student.lum.lu.dto.CluResultInfo;
 62  
 import org.kuali.student.lum.lu.dto.LuCodeInfo;
 63  
 import org.kuali.student.lum.lu.dto.ResultOptionInfo;
 64  
 import org.kuali.student.lum.lu.service.LuService;
 65  
 import org.kuali.student.lum.service.assembler.CluAssemblerUtils;
 66  
 import org.springframework.util.StringUtils;
 67  
 /**
 68  
  * Assembler for CourseInfo. Provides assemble and disassemble operation on
 69  
  * CourseInfo from/to CluInfo and other base DTOs
 70  
  * 
 71  
  * @author Kuali Student Team
 72  
  * 
 73  
  */
 74  23
 public class CourseAssembler implements BOAssembler<CourseInfo, CluInfo> {
 75  
 
 76  1
     final static Logger LOG = Logger.getLogger(CourseAssembler.class);
 77  
         private LuService luService;
 78  
         private FormatAssembler formatAssembler;
 79  
         private CourseJointAssembler courseJointAssembler;
 80  
         private LoAssembler loAssembler;
 81  
         private LearningObjectiveService loService;
 82  
     private CluAssemblerUtils cluAssemblerUtils;
 83  
     private LrcService lrcService;
 84  
         
 85  
         @Override
 86  
         public CourseInfo assemble(CluInfo clu, CourseInfo courseInfo,
 87  
                         boolean shallowBuild) throws AssemblyException {
 88  
 
 89  43
                 CourseInfo course = (null != courseInfo) ? courseInfo
 90  
                                 : new CourseInfo();
 91  
 
 92  
                 // Copy all the data from the clu to the course
 93  
                 
 94  43
                 course.setAttributes(clu.getAttributes());
 95  43
                 course.setCampusLocations(clu.getCampusLocations());
 96  43
                 course.setCode(clu.getOfficialIdentifier().getCode());
 97  43
                 course.setCourseNumberSuffix(clu.getOfficialIdentifier()
 98  
                                 .getSuffixCode());
 99  43
                 course.setLevel(clu.getOfficialIdentifier().getLevel());
 100  43
                 course.setOutOfClassHours(clu.getIntensity());
 101  43
                 course.setInstructors(clu.getInstructors());
 102  43
                 course.setStartTerm(clu.getExpectedFirstAtp());
 103  43
                 course.setEndTerm(clu.getLastAtp());
 104  43
                 course.setCourseTitle(clu.getOfficialIdentifier().getLongName());
 105  
 
 106  
                 // CrossListings
 107  43
                 List<CourseCrossListingInfo> crossListings = assembleCrossListings(clu.getAlternateIdentifiers()); 
 108  43
                 course.setCrossListings(crossListings);
 109  
                 
 110  
                 //Variation
 111  43
                 List<CourseVariationInfo> variations = assembleVariations(clu.getAlternateIdentifiers()); 
 112  43
                 course.setVariations(variations);
 113  
                 
 114  
 //                course.setDepartment(clu.getPrimaryAdminOrg().getOrgId());
 115  43
                 if(course.getUnitsDeployment()==null){
 116  0
                         course.setUnitsDeployment(new ArrayList<String>());
 117  
                 }
 118  43
                 if(course.getUnitsContentOwner()==null){
 119  0
                         course.setUnitsContentOwner(new ArrayList<String>());
 120  
                 }
 121  43
                 List<String> courseAdminOrgs = new ArrayList<String>();
 122  43
                 List<String> courseSubjectOrgs = new ArrayList<String>();
 123  43
                 for(AdminOrgInfo adminOrg: clu.getAdminOrgs()){
 124  170
                         if(adminOrg.getType().equals(CourseAssemblerConstants.ADMIN_ORG)){
 125  86
                                 courseAdminOrgs.add(adminOrg.getOrgId());
 126  
                         }
 127  170
                         if(adminOrg.getType().equals(CourseAssemblerConstants.SUBJECT_ORG)){
 128  84
                                 courseSubjectOrgs.add(adminOrg.getOrgId());
 129  
                         }
 130  
                 }
 131  43
                 course.setUnitsDeployment(courseAdminOrgs);
 132  43
                 course.setUnitsContentOwner(courseSubjectOrgs);
 133  43
                 course.setDescr(clu.getDescr());
 134  43
                 course.setDuration(clu.getStdDuration());
 135  43
                 course.setEffectiveDate(clu.getEffectiveDate());
 136  43
                 course.setExpirationDate(clu.getExpirationDate());
 137  
 
 138  
                 //Fees
 139  
                 //Fee justification
 140  43
                 List<CourseFeeInfo> fees = new ArrayList<CourseFeeInfo>();
 141  43
                 List<CourseRevenueInfo> revenues = new ArrayList<CourseRevenueInfo>();
 142  43
                 if(clu.getFeeInfo() != null){
 143  43
                         course.setFeeJustification(clu.getFeeInfo().getDescr());
 144  
 
 145  
                         //Fees and revenues come from the same place but revenues have a special feeType
 146  43
                         for(CluFeeRecordInfo cluFeeRecord: clu.getFeeInfo().getCluFeeRecords()){
 147  170
                                 String feeType = cluFeeRecord.getFeeType();
 148  170
                                 if(CourseAssemblerConstants.COURSE_FINANCIALS_REVENUE_TYPE.equals(feeType)){
 149  86
                                         CourseRevenueInfo courseRevenue = new CourseRevenueInfo();
 150  86
                                         courseRevenue.setFeeType(feeType);
 151  86
                                         courseRevenue.setAffiliatedOrgs(cluFeeRecord.getAffiliatedOrgs());
 152  86
                                         courseRevenue.setAttributes(cluFeeRecord.getAttributes());
 153  86
                                         courseRevenue.setId(cluFeeRecord.getId());
 154  86
                                         courseRevenue.setMetaInfo(cluFeeRecord.getMetaInfo());
 155  86
                                         revenues.add(courseRevenue);
 156  86
                                 }else{
 157  84
                                         CourseFeeInfo courseFee = new CourseFeeInfo();
 158  84
                                         courseFee.setFeeType(feeType);
 159  84
                                         courseFee.setRateType(cluFeeRecord.getRateType());
 160  84
                                         courseFee.setDescr(cluFeeRecord.getDescr());
 161  84
                                         courseFee.setMetaInfo(cluFeeRecord.getMetaInfo());
 162  84
                                         courseFee.setId(cluFeeRecord.getId());
 163  84
                                         courseFee.setFeeAmounts(cluFeeRecord.getFeeAmounts());
 164  84
                                         courseFee.setAttributes(cluFeeRecord.getAttributes());
 165  84
                                         fees.add(courseFee);
 166  
                                 }
 167  170
                         }
 168  
                 }
 169  43
                 course.setFees(fees);
 170  43
                 course.setRevenues(revenues);
 171  
                 //Expenditures are mapped from accounting info
 172  43
                 if(course.getExpenditure() == null || clu.getAccountingInfo() == null){
 173  13
                         course.setExpenditure(new CourseExpenditureInfo());
 174  
                 }
 175  43
                 if(clu.getAccountingInfo() != null){
 176  43
                         course.getExpenditure().setAffiliatedOrgs(clu.getAccountingInfo().getAffiliatedOrgs());
 177  
                 }
 178  
                 
 179  43
                 course.setId(clu.getId());
 180  43
                 course.setType(clu.getType());
 181  43
                 course.setTermsOffered(clu.getOfferedAtpTypes());
 182  43
                 course.setPrimaryInstructor(clu.getPrimaryInstructor());
 183  43
                 course.setInstructors(clu.getInstructors());
 184  43
                 course.setState(clu.getState());
 185  43
                 course.setSubjectArea(clu.getOfficialIdentifier().getDivision());
 186  43
                 course.setTranscriptTitle(clu.getOfficialIdentifier().getShortName());
 187  43
                 course.setMetaInfo(clu.getMetaInfo());
 188  43
                 course.setVersionInfo(clu.getVersionInfo());
 189  
 
 190  
                 
 191  
                 //Special topics code
 192  43
                 course.setSpecialTopicsCourse(false);
 193  43
                 for(LuCodeInfo luCode : clu.getLuCodes()){
 194  3
                         if(CourseAssemblerConstants.COURSE_CODE_SPECIAL_TOPICS.equals(luCode.getType())){
 195  3
                                 course.setSpecialTopicsCourse(Boolean.parseBoolean(luCode.getValue()));
 196  3
                                 break;
 197  
                         }
 198  
                 }
 199  
                 //Pilot Course code
 200  43
                 course.setPilotCourse(false);
 201  43
                 for(LuCodeInfo luCode : clu.getLuCodes()){
 202  6
                         if(CourseAssemblerConstants.COURSE_CODE_PILOT_COURSE.equals(luCode.getType())){
 203  3
                                 course.setPilotCourse(Boolean.parseBoolean(luCode.getValue()));
 204  3
                                 break;
 205  
                         }
 206  
                 }
 207  
                 
 208  
                 // Don't make any changes to nested datastructures if this is
 209  43
                 if (!shallowBuild) {
 210  
                         try {
 211  
                                 // Use the luService to find Joints, then convert and add to the
 212  
                                 // course
 213  13
                                 List<CluCluRelationInfo> cluClus = luService
 214  
                                                 .getCluCluRelationsByClu(clu.getId());
 215  
                                 
 216  13
                                 for (CluCluRelationInfo cluRel : cluClus) {
 217  27
                                         if (cluRel.getType().equals(
 218  
                                                         CourseAssemblerConstants.JOINT_RELATION_TYPE)) {
 219  0
                                                 CourseJointInfo jointInfo = courseJointAssembler
 220  
                                                                 .assemble(cluRel, null, false);
 221  0
                                                 course.getJoints().add(jointInfo);
 222  27
                                         }
 223  
                                 }
 224  0
                         } catch (DoesNotExistException e) {
 225  0
                         } catch (Exception e) {
 226  0
                                 throw new AssemblyException("Error getting course joints", e);
 227  13
                         }
 228  
 
 229  
                         try {
 230  
                                 // Use the luService to find formats, then convert and add to
 231  
                                 // the course
 232  13
                                 List<CluInfo> formats = luService.getRelatedClusByCluId(course
 233  
                                                 .getId(),
 234  
                                                 CourseAssemblerConstants.COURSE_FORMAT_RELATION_TYPE);
 235  
                                 
 236  13
                                 for (CluInfo format : formats) {
 237  27
                                         FormatInfo formatInfo = formatAssembler.assemble(format,
 238  
                                                         null, false);
 239  27
                                         course.getFormats().add(formatInfo);
 240  27
                                 }
 241  
 
 242  0
                         } catch (DoesNotExistException e) {
 243  0
                         } catch (Exception e) {
 244  0
                                 throw new AssemblyException("Error getting related formats", e);
 245  13
                         }
 246  
 
 247  
                         try{
 248  
                                 //Set Credit and Grading options
 249  13
                                 List<CluResultInfo> cluResults = luService.getCluResultByClu(course.getId());
 250  
 
 251  13
                                 List<ResultComponentInfo> creditOptions = assembleCreditOptions(cluResults);
 252  13
                                 course.setCreditOptions(creditOptions);
 253  
                                 
 254  13
                                 List<String> gradingOptions = assembleGradingOptions(cluResults);
 255  
                                 
 256  13
                                 course.setGradingOptions(gradingOptions);
 257  0
                         } catch (DoesNotExistException e){
 258  0
                         } catch (Exception e) {
 259  0
                                 throw new AssemblyException("Error getting course results", e);
 260  13
                         }
 261  
                         
 262  
                         //Learning Objectives
 263  13
             course.getCourseSpecificLOs().addAll(cluAssemblerUtils.assembleLos(course.getId(), shallowBuild));
 264  
                         
 265  
                 }
 266  
 
 267  
                 //Remove special cases for grading options
 268  43
                 course.getGradingOptions().remove(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_AUDIT);
 269  
                 
 270  43
                 return course;
 271  
         }
 272  
 
 273  
         @Override
 274  
         public BaseDTOAssemblyNode<CourseInfo, CluInfo> disassemble(
 275  
                         CourseInfo course, NodeOperation operation)
 276  
                         throws AssemblyException {
 277  
 
 278  24
                 if (course == null) {
 279  
                         // FIXME Unsure now if this is an exception or just return null or
 280  
                         // empty assemblyNode
 281  0
                     LOG.error("Course to disassemble is null!");
 282  0
                         throw new AssemblyException("Course can not be null");
 283  
                 }
 284  
 
 285  24
                 BaseDTOAssemblyNode<CourseInfo, CluInfo> result = new BaseDTOAssemblyNode<CourseInfo, CluInfo>(
 286  
                                 this);
 287  
 
 288  
                 CluInfo clu;
 289  
                 try {
 290  24
                         clu = (NodeOperation.UPDATE == operation) ? luService.getClu(course.getId()) : new CluInfo();
 291  0
         } catch (Exception e) {
 292  0
                         throw new AssemblyException("Error getting existing learning unit during course update", e);
 293  24
         } 
 294  
 
 295  
                 // Create the id if it's not there already(important for creating
 296  
                 // relations)
 297  24
                 clu.setId(UUIDHelper.genStringUUID(course.getId()));
 298  24
                 if (null == course.getId()) {
 299  13
                         course.setId(clu.getId());
 300  
                 }
 301  24
                 clu.setType(CourseAssemblerConstants.COURSE_TYPE);
 302  24
                 clu.setState(course.getState());
 303  
 
 304  24
                 CluIdentifierInfo identifier = new CluIdentifierInfo();
 305  24
                 identifier.setType(CourseAssemblerConstants.COURSE_OFFICIAL_IDENT_TYPE);
 306  24
                 identifier.setState(course.getState());
 307  24
                 identifier.setLongName(course.getCourseTitle());
 308  24
                 identifier.setShortName(course.getTranscriptTitle());
 309  24
                 identifier.setSuffixCode(course.getCourseNumberSuffix());
 310  24
                 identifier.setDivision(course.getSubjectArea());
 311  
 
 312  
                 //Custom logic to set the code as the concatenation of division and course number suffix if code not provided
 313  24
                 if (StringUtils.hasText(course.getCode())){
 314  23
                         identifier.setCode(course.getCode());
 315  1
                 } else if(course.getCourseNumberSuffix()!=null&&course.getSubjectArea()!=null&&!course.getCourseNumberSuffix().isEmpty()&&!course.getSubjectArea().isEmpty()){
 316  1
                         identifier.setCode(course.getSubjectArea()+course.getCourseNumberSuffix());                        
 317  
                 }else{
 318  0
                         identifier.setCode(null);
 319  
                 }
 320  
                 
 321  
                 //Custom logic to set the level, if level not provided
 322  24
                 if(StringUtils.hasText(course.getLevel())) {
 323  24
                     identifier.setLevel(course.getLevel());
 324  0
                 } else if(course.getCourseNumberSuffix()!=null&&course.getCourseNumberSuffix().length()>=3){
 325  0
                         identifier.setLevel(course.getCourseNumberSuffix().substring(0, 1)+"00");
 326  
                 }
 327  
                 
 328  24
                 clu.setOfficialIdentifier(identifier);
 329  
 
 330  24
                 clu.setAdminOrgs(new ArrayList<AdminOrgInfo>());
 331  
 
 332  
                 // Use the Course Variation assembler to disassemble the variations
 333  
                 
 334  
                 // copy all fields
 335  
                 //Remove any existing variations or crosslistings
 336  24
                 for(Iterator<CluIdentifierInfo> iter = clu.getAlternateIdentifiers().iterator();iter.hasNext();){
 337  20
                         CluIdentifierInfo cluIdentifier = iter.next();
 338  20
                         if(CourseAssemblerConstants.COURSE_VARIATION_IDENT_TYPE.equals(cluIdentifier.getType()) ||
 339  
                                 CourseAssemblerConstants.COURSE_CROSSLISTING_IDENT_TYPE.equals(cluIdentifier.getType()) ){
 340  20
                                 iter.remove();
 341  
                         }
 342  20
                 }
 343  
                 //Add in variations
 344  24
                 for(CourseVariationInfo variation:course.getVariations()){
 345  48
                         CluIdentifierInfo cluIdentifier = new CluIdentifierInfo();
 346  48
                         cluIdentifier.setId(variation.getId());
 347  48
                         cluIdentifier.setType(CourseAssemblerConstants.COURSE_VARIATION_IDENT_TYPE);
 348  48
                         cluIdentifier.setCode(identifier.getCode());
 349  48
                         cluIdentifier.setSuffixCode(course.getCourseNumberSuffix());
 350  48
                         cluIdentifier.setDivision(course.getSubjectArea());
 351  48
                         cluIdentifier.setVariation(variation.getVariationCode());
 352  48
                         cluIdentifier.setLongName(variation.getVariationTitle());
 353  48
                         cluIdentifier.setState(course.getState());
 354  48
                         clu.getAlternateIdentifiers().add(cluIdentifier);
 355  48
                 }
 356  
                 //Add in crossListings
 357  24
                 for(CourseCrossListingInfo crossListing:course.getCrossListings()){
 358  0
                         CluIdentifierInfo cluIdentifier = new CluIdentifierInfo();
 359  0
                         cluIdentifier.setId(crossListing.getId());
 360  0
                         cluIdentifier.setType(CourseAssemblerConstants.COURSE_CROSSLISTING_IDENT_TYPE);
 361  0
                         cluIdentifier.setSuffixCode(crossListing.getCourseNumberSuffix());
 362  0
                         cluIdentifier.setDivision(crossListing.getSubjectArea());
 363  0
                         cluIdentifier.setState(course.getState());
 364  0
                         cluIdentifier.setOrgId(crossListing.getDepartment());
 365  0
                         clu.getAlternateIdentifiers().add(cluIdentifier);
 366  0
                 }
 367  
 
 368  24
                 List<AdminOrgInfo> adminOrgInfos = new ArrayList<AdminOrgInfo>();
 369  24
                 for(String org:course.getUnitsDeployment()){
 370  48
                         AdminOrgInfo adminOrg = new AdminOrgInfo();
 371  48
                         adminOrg.setType(CourseAssemblerConstants.ADMIN_ORG);
 372  48
                         adminOrg.setOrgId(org);
 373  48
                         adminOrgInfos.add(adminOrg);
 374  48
                 }
 375  24
                 clu.getAdminOrgs().addAll(adminOrgInfos);
 376  
                 
 377  24
                 List<AdminOrgInfo> subjectOrgs = new ArrayList<AdminOrgInfo>();
 378  24
                 for (String subOrg : course.getUnitsContentOwner()) {
 379  46
                         AdminOrgInfo subjectOrg = new AdminOrgInfo();
 380  46
                         subjectOrg.setType(CourseAssemblerConstants.SUBJECT_ORG);
 381  46
                         subjectOrg.setOrgId(subOrg);
 382  46
                         subjectOrgs.add(subjectOrg);
 383  46
                 }
 384  24
                 clu.getAdminOrgs().addAll(subjectOrgs);
 385  
 
 386  
                 
 387  24
                 clu.setAttributes(course.getAttributes());
 388  24
                 clu.setCampusLocations(course.getCampusLocations());
 389  24
                 clu.setDescr(course.getDescr());
 390  24
                 clu.setStdDuration(course.getDuration());
 391  24
                 clu.setEffectiveDate(course.getEffectiveDate());
 392  24
                 clu.setExpirationDate(course.getExpirationDate());
 393  
 
 394  24
                 clu.setOfferedAtpTypes(course.getTermsOffered());
 395  24
                 clu.setPrimaryInstructor(course.getPrimaryInstructor());
 396  
                 
 397  24
                 clu.setIntensity(course.getOutOfClassHours());
 398  24
                 clu.setInstructors(course.getInstructors());
 399  
                 
 400  24
                 clu.setExpectedFirstAtp(course.getStartTerm());
 401  24
                 clu.setLastAtp(course.getEndTerm());
 402  
                 
 403  24
                 clu.setMetaInfo(course.getMetaInfo());
 404  24
                 clu.setVersionInfo(course.getVersionInfo());
 405  
 
 406  
                 // Add the Clu to the result
 407  24
                 result.setNodeData(clu);
 408  24
                 result.setOperation(operation);
 409  24
                 result.setBusinessDTORef(course);
 410  
 
 411  
                 // Use the Format assembler to disassemble the formats and relations
 412  
                 List<BaseDTOAssemblyNode<?, ?>> formatResults;
 413  
         try {
 414  24
             formatResults = disassembleFormats(clu
 415  
                             .getId(), course, operation);
 416  24
             result.getChildNodes().addAll(formatResults);
 417  
             
 418  0
         } catch (DoesNotExistException e) {
 419  0
         } catch (Exception e) {
 420  0
             throw new AssemblyException("Error while disassembling format", e);
 421  24
         }
 422  
 
 423  
                 // Use the CourseJoint assembler to disassemble the CourseJoints and
 424  
                 // relations
 425  24
                 List<BaseDTOAssemblyNode<?, ?>> courseJointResults = disassembleJoints(
 426  
                                 clu.getId(), course, operation);
 427  24
                 result.getChildNodes().addAll(courseJointResults);
 428  
 
 429  
                 //Disassemble the CluResults (grading and credit options)
 430  
                 //Special code to take audit from attributes and put into options
 431  24
                 if(course.getAttributes().containsKey(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_AUDIT)&&"true".equals(course.getAttributes().get(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_AUDIT))){
 432  0
                         if(!course.getGradingOptions().contains(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_AUDIT)){
 433  0
                                 course.getGradingOptions().add(CourseAssemblerConstants.COURSE_RESULT_COMP_GRADE_AUDIT);
 434  
                         }
 435  
                 }
 436  
                 
 437  
                 List<CluResultInfo> cluResultList;
 438  
                 try {
 439  24
                         cluResultList = luService.getCluResultByClu(clu.getId());
 440  0
                 } catch (DoesNotExistException e) {
 441  0
                         cluResultList = Collections.emptyList();
 442  0
                 } catch (Exception e) {
 443  0
                         throw new AssemblyException("Error getting cluResults", e);
 444  24
                 }
 445  
                 
 446  24
                 List<BaseDTOAssemblyNode<?, ?>> creditOutcomes = disassembleCreditOutcomes(course, clu, cluResultList, operation);
 447  24
                 result.getChildNodes().addAll(creditOutcomes);
 448  
                 
 449  24
                 BaseDTOAssemblyNode<?, ?> gradingOptions = disassembleGradingOptions(
 450  
                                 clu.getId(), course.getState(), course.getGradingOptions(), cluResultList, operation);
 451  24
                 result.getChildNodes().add(gradingOptions);
 452  
                 
 453  
                 //Use the LoAssembler to disassemble Los
 454  
         try {
 455  
                     List<BaseDTOAssemblyNode<?, ?>> loResults;
 456  24
                     loResults = disassembleLos(clu.getId(), course, operation);
 457  24
             result.getChildNodes().addAll(loResults);
 458  0
         } catch (Exception e) {
 459  0
             throw new AssemblyException("Error while disassembling los", e);
 460  24
         }
 461  
                 
 462  
                 //add the special topics code if it did not exist, or remove if it was not wanted
 463  24
                 boolean alreadyHadSpecialTopicsCode = false;
 464  24
                 for(Iterator<LuCodeInfo> luCodeIterator = clu.getLuCodes().iterator();luCodeIterator.hasNext();){
 465  1
                         LuCodeInfo luCode = luCodeIterator.next();
 466  1
                         if(CourseAssemblerConstants.COURSE_CODE_SPECIAL_TOPICS.equals(luCode.getType())){
 467  1
                                 alreadyHadSpecialTopicsCode = true;
 468  1
                                 if(!course.isSpecialTopicsCourse()){
 469  1
                                         luCodeIterator.remove();
 470  
                                 }
 471  
                                 break;
 472  
                         }
 473  0
                 }
 474  24
                 if(!alreadyHadSpecialTopicsCode && course.isSpecialTopicsCourse()){
 475  2
                         LuCodeInfo luCode = new LuCodeInfo();
 476  2
                         luCode.setType(CourseAssemblerConstants.COURSE_CODE_SPECIAL_TOPICS);
 477  2
                         luCode.setValue("true");
 478  2
                         clu.getLuCodes().add(luCode);
 479  
                 }
 480  
                 
 481  
                 //add the special topics code if it did not exist, or remove if it was not wanted
 482  24
                 boolean alreadyHadPilotCourseCode = false;
 483  24
                 for(Iterator<LuCodeInfo> luCodeIterator = clu.getLuCodes().iterator();luCodeIterator.hasNext();){
 484  3
                         LuCodeInfo luCode = luCodeIterator.next();
 485  3
                         if(CourseAssemblerConstants.COURSE_CODE_PILOT_COURSE.equals(luCode.getType())){
 486  1
                                 alreadyHadPilotCourseCode = true;
 487  1
                                 if(!course.isPilotCourse()){
 488  1
                                         luCodeIterator.remove();
 489  
                                 }
 490  
                                 break;
 491  
                         }
 492  2
                 }
 493  24
                 if(!alreadyHadPilotCourseCode && course.isPilotCourse()){
 494  2
                         LuCodeInfo luCode = new LuCodeInfo();
 495  2
                         luCode.setType(CourseAssemblerConstants.COURSE_CODE_PILOT_COURSE);
 496  2
                         luCode.setValue("true");
 497  2
                         clu.getLuCodes().add(luCode);
 498  
                 }
 499  
                 
 500  
                 //FEES
 501  24
                 if(clu.getFeeInfo() == null){
 502  14
                         clu.setFeeInfo(new CluFeeInfo());
 503  
                 }
 504  24
                 clu.getFeeInfo().setDescr(course.getFeeJustification());
 505  24
                 clu.getFeeInfo().getCluFeeRecords().clear();
 506  24
                 for(CourseRevenueInfo courseRevenue:course.getRevenues()){
 507  48
                         CluFeeRecordInfo cluFeeRecord  = new CluFeeRecordInfo();
 508  48
                         cluFeeRecord.setFeeType(CourseAssemblerConstants.COURSE_FINANCIALS_REVENUE_TYPE);
 509  48
                         cluFeeRecord.setRateType(CourseAssemblerConstants.COURSE_FINANCIALS_REVENUE_TYPE);
 510  48
                         cluFeeRecord.setAttributes(courseRevenue.getAttributes());
 511  48
                         cluFeeRecord.setAffiliatedOrgs(courseRevenue.getAffiliatedOrgs());
 512  48
                         cluFeeRecord.setId(courseRevenue.getId());
 513  48
                         cluFeeRecord.setMetaInfo(courseRevenue.getMetaInfo());
 514  48
                         clu.getFeeInfo().getCluFeeRecords().add(cluFeeRecord);
 515  48
                 }
 516  24
                 for(CourseFeeInfo courseFee : course.getFees()){
 517  46
                         CluFeeRecordInfo cluFeeRecord  = new CluFeeRecordInfo();
 518  46
                         cluFeeRecord.setFeeType(courseFee.getFeeType());
 519  46
                         cluFeeRecord.setRateType(courseFee.getRateType());
 520  46
                         cluFeeRecord.setDescr(courseFee.getDescr());
 521  46
                         cluFeeRecord.setMetaInfo(courseFee.getMetaInfo());
 522  46
                         cluFeeRecord.setId(courseFee.getId());
 523  46
                         cluFeeRecord.setFeeAmounts(courseFee.getFeeAmounts());
 524  46
                         cluFeeRecord.setAttributes(courseFee.getAttributes());
 525  46
                         clu.getFeeInfo().getCluFeeRecords().add(cluFeeRecord);
 526  46
                 }
 527  24
                 if(clu.getAccountingInfo() == null || course.getExpenditure()== null){
 528  14
                         clu.setAccountingInfo( new CluAccountingInfo());
 529  
                 }
 530  24
                 if(course.getExpenditure() != null){
 531  24
                         clu.getAccountingInfo().setAffiliatedOrgs(course.getExpenditure().getAffiliatedOrgs());
 532  24
                         clu.getAccountingInfo().setAttributes(course.getExpenditure().getAttributes());
 533  
                 }
 534  
                 
 535  24
                 return result;
 536  
         }
 537  
 
 538  
         private List<BaseDTOAssemblyNode<?, ?>> disassembleCreditOutcomes(CourseInfo course, CluInfo clu, List<CluResultInfo> currentCluResults, NodeOperation operation) throws AssemblyException, NumberFormatException {
 539  
                 
 540  24
                 List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
 541  
                 
 542  24
                 String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_CREDITS;
 543  
                 
 544  
                 //See if we need to create any new lrcs
 545  24
                 if(NodeOperation.DELETE!=operation){
 546  
                         //Find all the existing LRCs for the following three types
 547  23
                         Set<String> rsltComps = new HashSet<String>();
 548  
                         
 549  
                         try{
 550  
                                 try {
 551  23
                                         rsltComps.addAll(lrcService.getResultComponentIdsByResultComponentType(CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_FIXED));
 552  23
                                 } catch (DoesNotExistException e) {}
 553  
                                 try {
 554  23
                                         rsltComps.addAll(lrcService.getResultComponentIdsByResultComponentType(CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_MULTIPLE));
 555  23
                                 } catch (DoesNotExistException e) {}
 556  
                                 try {
 557  23
                                         rsltComps.addAll(lrcService.getResultComponentIdsByResultComponentType(CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_VARIABLE));
 558  23
                                 } catch (DoesNotExistException e) {}
 559  
 
 560  
                                 //Create any LRCs that do not yet exist
 561  23
                                 for(ResultComponentInfo creditOption:course.getCreditOptions()){
 562  47
                                         String id = null;
 563  47
                                         String type = null;
 564  47
                                         List<String> resultValues = null;
 565  47
                                         Map<String,String> attributes = null;
 566  
                                         //Depending on the type, set the id, type and result values differently
 567  47
                                         if(CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_FIXED.equals(creditOption.getType())){
 568  42
                                                 float fixedCreditValue = Float.parseFloat(creditOption.getAttributes().get(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_FIXED_CREDIT_VALUE));
 569  42
                                                 id = CourseAssemblerConstants.COURSE_RESULT_COMP_CREDIT_PREFIX + fixedCreditValue;
 570  42
                                                 type = CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_FIXED;
 571  42
                                                 resultValues = new ArrayList<String>();
 572  42
                                                 resultValues.add(String.valueOf(fixedCreditValue));
 573  42
                                                 attributes = new HashMap<String,String>();
 574  42
                                                 attributes.put(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_FIXED_CREDIT_VALUE, String.valueOf(fixedCreditValue));
 575  42
                                         }else if(CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_MULTIPLE.equals(creditOption.getType())){
 576  3
                                                 Collections.sort(creditOption.getResultValues());
 577  3
                                                 StringBuilder sb = new StringBuilder(CourseAssemblerConstants.COURSE_RESULT_COMP_CREDIT_PREFIX);
 578  3
                                                 for(Iterator<String> iter = creditOption.getResultValues().iterator();iter.hasNext();){
 579  7
                                                         sb.append(iter.next());
 580  7
                                                         if(iter.hasNext()){
 581  4
                                                                 sb.append(",");
 582  
                                                         }
 583  
                                                 }
 584  3
                                                 id = sb.toString();
 585  3
                                                 type = CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_MULTIPLE;
 586  3
                                                 resultValues = creditOption.getResultValues();
 587  3
                                         }else if(CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_VARIABLE.equals(creditOption.getType())){
 588  
                                             /*
 589  
                                              * For variable credits create a Result values that goes from min to max with the specified increment. 
 590  
                                              * If no increment is specified, use 1.0 as the increment. The increment can be specified as a float.
 591  
                                              */
 592  
                                                                                      
 593  2
                                             String minCreditValue = creditOption.getAttributes().get(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_MIN_CREDIT_VALUE);
 594  2
                                                 String maxCreditValue = creditOption.getAttributes().get(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_MAX_CREDIT_VALUE);
 595  2
                                                 String creditValueIncr = creditOption.getAttributes().get(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_CREDIT_VALUE_INCR);
 596  2
                                                 float minCredits = Float.parseFloat(minCreditValue);
 597  2
                                                 float maxCredits = Float.parseFloat(maxCreditValue);
 598  
                                                                                                 
 599  2
                                                 float increment = (null != creditValueIncr && creditValueIncr.length() > 0 ) ? Float.parseFloat(creditValueIncr) : 1.0f ;
 600  
                                                                                                 
 601  2
                                                 id = CourseAssemblerConstants.COURSE_RESULT_COMP_CREDIT_PREFIX + minCreditValue + "-" + maxCreditValue;
 602  2
                                                 type = CourseAssemblerConstants.COURSE_RESULT_COMP_TYPE_CREDIT_VARIABLE;
 603  2
                                                 resultValues = new ArrayList<String>();
 604  16
                                                 for(float i = minCredits; i <= maxCredits; i+=increment){
 605  14
                                                         resultValues.add(String.valueOf(i));
 606  
                                                 }
 607  2
                                                 attributes = new HashMap<String,String>();
 608  2
                                                 attributes.put(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_MIN_CREDIT_VALUE, minCreditValue);
 609  2
                                                 attributes.put(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_MAX_CREDIT_VALUE, maxCreditValue);
 610  2
                         attributes.put(CourseAssemblerConstants.COURSE_RESULT_COMP_ATTR_CREDIT_VALUE_INCR, creditValueIncr);
 611  
                                         }
 612  
         
 613  
                                         //Set the id
 614  47
                                         creditOption.setId(id);
 615  
                                         
 616  
                                         //Create a new result component
 617  47
                                         if(id != null && !rsltComps.contains(id)){
 618  
                                                                                                 
 619  
                                                 //need to make a fixed degree result type component
 620  4
                                                 ResultComponentInfo resultComponent = new ResultComponentInfo();
 621  4
                                                 resultComponent.setId(id);
 622  4
                                                 resultComponent.setType(type);
 623  4
       resultComponent.setState ("Active");
 624  4
                                                 resultComponent.setResultValues(resultValues);
 625  4
                                                 resultComponent.setAttributes(attributes);
 626  4
                                                 BaseDTOAssemblyNode<ResultComponentInfo, ResultComponentInfo> node = new BaseDTOAssemblyNode<ResultComponentInfo, ResultComponentInfo>(null);
 627  4
                                                 node.setOperation(NodeOperation.CREATE);
 628  4
                                                 node.setNodeData(resultComponent);
 629  4
                                                 node.setBusinessDTORef(creditOption);
 630  4
                                                 results.add(node);
 631  
                                                 
 632  4
                                                 rsltComps.add(id);
 633  
                                         }
 634  47
                                 }
 635  0
                         }catch (NumberFormatException e){
 636  0
                                 throw new AssemblyException("Invalid Arguments for credit outcome values",e);
 637  0
                         }catch (Exception e){
 638  0
                                 throw new AssemblyException("Error Assembling", e);
 639  23
                         }
 640  
                 }
 641  
                 
 642  
                 //Now do dissassembly for the actual clu-lrc relations and result options
 643  
                 
 644  
                 // Get the current options and put them in a map of option type id/cluResult
 645  24
                 Map<String, List<CluResultInfo>> currentResults = new HashMap<String, List<CluResultInfo>>();
 646  
                 
 647  
                 //If this is not a create, lookup the results for this clu
 648  24
                 if (!NodeOperation.CREATE.equals(operation)) {
 649  11
                         for (CluResultInfo currentResult : currentCluResults) {
 650  9
                                 if (courseResultType.equals(currentResult.getType())) {
 651  
                                         //There should only be one grading option per CluResult for credit outcomes
 652  6
                                         if(currentResult.getResultOptions().size()==1){
 653  
                                                 //Create a mapping to a list of cluresults with the same result componentId
 654  6
                                                 String resultComponentId = currentResult.getResultOptions().get(0).getResultComponentId();
 655  6
                                                 if(!currentResults.containsKey(resultComponentId)){
 656  4
                                                         currentResults.put(resultComponentId, new ArrayList<CluResultInfo>());
 657  
                                                 }
 658  6
                                                 currentResults.get(resultComponentId).add(currentResult);
 659  6
                                         }else{
 660  0
                                                 LOG.warn("Credit Results should have exactly one result option each");
 661  
                                         }
 662  
                                 }
 663  
                         }
 664  
                 }
 665  
                 
 666  
                 //Loop through options on the course, if they are new, create a new cluResult
 667  24
                 for(ResultComponentInfo creditOption : course.getCreditOptions()){
 668  49
                     if (NodeOperation.CREATE == operation
 669  
                             || (NodeOperation.UPDATE == operation && !currentResults.containsKey(creditOption.getId()) )) {
 670  
                             
 671  44
                             ResultOptionInfo resultOption = new ResultOptionInfo();
 672  44
                             resultOption.setState(course.getState());
 673  44
                             resultOption.setResultComponentId(creditOption.getId());
 674  
                             
 675  44
                             CluResultInfo cluResult = new CluResultInfo();
 676  44
                                 cluResult.setCluId(clu.getId());
 677  44
                                 cluResult.setState(course.getState());
 678  44
                                 cluResult.setType(courseResultType);
 679  
                                 
 680  44
                                 cluResult.getResultOptions().add(resultOption);
 681  
                                 
 682  44
                                 BaseDTOAssemblyNode<ResultComponentInfo, CluResultInfo> cluResultNode = new BaseDTOAssemblyNode<ResultComponentInfo, CluResultInfo>(null);
 683  44
                                 cluResultNode.setNodeData(cluResult);
 684  44
                                 cluResultNode.setOperation(NodeOperation.CREATE);
 685  
                                 
 686  44
                 results.add(cluResultNode);
 687  44
             } else if (NodeOperation.UPDATE == operation
 688  
                                         && currentResults.containsKey(creditOption.getId())) {
 689  
                     //Get the list from the map and remove an entry, if the list is empty then remove it from the map
 690  3
                     List<CluResultInfo> cluResults = currentResults.get(creditOption.getId());
 691  3
                     cluResults.remove(cluResults.size()-1);
 692  3
                     if(cluResults.isEmpty()){
 693  2
                             currentResults.remove(creditOption.getId());
 694  
                     }
 695  49
                         }
 696  
                 }
 697  
                 
 698  
                 //Delete the leftovers
 699  24
                 for(Entry<String,List<CluResultInfo>> entry:currentResults.entrySet()){
 700  2
                         for(CluResultInfo cluResult:entry.getValue()){
 701  3
                                 BaseDTOAssemblyNode<ResultComponentInfo, CluResultInfo> cluResultNode = new BaseDTOAssemblyNode<ResultComponentInfo, CluResultInfo>(null);
 702  3
                                 cluResultNode.setNodeData(cluResult);
 703  3
                                 cluResultNode.setOperation(NodeOperation.DELETE);
 704  3
                                 results.add(cluResultNode);
 705  3
                         }
 706  
                 }
 707  
                 
 708  24
                 return results;
 709  
         }
 710  
 
 711  
         private List<String> assembleGradingOptions(List<CluResultInfo> cluResults){
 712  
                 
 713  13
                 String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_GRADE;
 714  
                 
 715  13
                 List<String> results = new ArrayList<String>();
 716  
                 //Loop through all the CluResults to find the one with the matching type
 717  13
                 for(CluResultInfo cluResult:cluResults){
 718  39
                         if(courseResultType.equals(cluResult.getType())){
 719  
                                 //Loop through all options and add to the list of Strings
 720  13
                                 for(ResultOptionInfo resultOption: cluResult.getResultOptions()){
 721  26
                                         results.add(resultOption.getResultComponentId());
 722  
                                 }
 723  13
                                 break;
 724  
                         }
 725  
                 }
 726  13
                 return results;
 727  
         }
 728  
         
 729  
         private List<ResultComponentInfo> assembleCreditOptions(
 730  
                         List<CluResultInfo> cluResults) throws AssemblyException {
 731  13
                 String courseResultType = CourseAssemblerConstants.COURSE_RESULT_TYPE_CREDITS;
 732  13
                 List<ResultComponentInfo> results = new ArrayList<ResultComponentInfo>();
 733  
                 //Loop through all the CluResults to find the one with the matching type
 734  13
                 for(CluResultInfo cluResult:cluResults){
 735  40
                         if(courseResultType.equals(cluResult.getType())){
 736  
                                 //Loop through all options and add to the list of Strings
 737  27
                                 for(ResultOptionInfo resultOption: cluResult.getResultOptions()){
 738  
                                         try {
 739  27
                                                 ResultComponentInfo resultComponent = lrcService.getResultComponent(resultOption.getResultComponentId());
 740  27
                                                 results.add(resultComponent);
 741  0
                                         } catch (DoesNotExistException e) {
 742  0
                                                 LOG.warn("Course Credit option:"+resultOption.getId()+" refers to non-existant ResultComponentInfo "+resultOption.getResultComponentId());
 743  0
                                         } catch (Exception e) {
 744  0
                                                 throw new AssemblyException("Error getting result components",e);
 745  27
                                         }
 746  
                                 }
 747  
                         }
 748  
                 }
 749  13
                 return results;
 750  
         }
 751  
         
 752  
         // TODO Use CluAssemblerUtils
 753  
         private List<BaseDTOAssemblyNode<?, ?>> disassembleLos(String cluId,
 754  
                         CourseInfo course, NodeOperation operation) throws AssemblyException {
 755  
                 // TODO Auto-generated method stub
 756  24
                 List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
 757  
 
 758  
                 // Get the current formats and put them in a map of format id/relation
 759  
                 // id
 760  24
                 Map<String, CluLoRelationInfo> currentCluLoRelations = new HashMap<String, CluLoRelationInfo>();
 761  
                 try {
 762  24
                         List<CluLoRelationInfo> cluLoRelations = luService.getCluLoRelationsByClu(cluId);
 763  24
                         for(CluLoRelationInfo cluLoRelation:cluLoRelations){
 764  6
                                 if(CourseAssemblerConstants.COURSE_LO_COURSE_SPECIFIC_RELATION.equals(cluLoRelation.getType())){
 765  6
                                         currentCluLoRelations.put(cluLoRelation.getLoId(), cluLoRelation);
 766  
                                 }
 767  
                         }
 768  0
                 } catch (DoesNotExistException e) {
 769  0
                 } catch (Exception e) {
 770  0
                         throw new AssemblyException("Error finding related Los");
 771  24
                 }
 772  
                 
 773  
                 // Loop through all the los in this clu
 774  24
                 for(LoDisplayInfo loDisplay : course.getCourseSpecificLOs()){
 775  
 
 776  
                         // If this is a clu create/new lo update then all los will be created
 777  48
                     if (NodeOperation.CREATE == operation
 778  
                             || (NodeOperation.UPDATE == operation &&  !currentCluLoRelations.containsKey(loDisplay.getLoInfo().getId()))) {
 779  
                         
 780  
                 // the lo does not exist, so create
 781  
                 // Assemble and add the lo
 782  43
                             loDisplay.getLoInfo().setId(null);
 783  43
                 BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler
 784  
                         .disassemble(loDisplay, NodeOperation.CREATE);
 785  43
                 results.add(loNode);
 786  
 
 787  
                 // Create the relationship and add it as well
 788  43
                 CluLoRelationInfo relation = new CluLoRelationInfo();
 789  43
                 relation.setCluId(cluId);
 790  43
                 relation.setLoId(loNode.getNodeData().getId());
 791  43
                 relation
 792  
                         .setType(CourseAssemblerConstants.COURSE_LO_COURSE_SPECIFIC_RELATION);
 793  43
                 relation.setState(course.getState());
 794  
 
 795  43
                 BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo> relationNode = new BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo>(
 796  
                         null);
 797  43
                 relationNode.setNodeData(relation);
 798  43
                 relationNode.setOperation(NodeOperation.CREATE);
 799  
 
 800  43
                 results.add(relationNode);
 801  43
             } else if (NodeOperation.UPDATE == operation
 802  
                                         && currentCluLoRelations.containsKey(loDisplay.getLoInfo().getId())) {
 803  
                                 // If the clu already has this lo, then just update the lo
 804  3
                 BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler
 805  
                                 .disassemble(loDisplay, NodeOperation.UPDATE);
 806  3
                                 results.add(loNode);
 807  
 
 808  
                                 // remove this entry from the map so we can tell what needs to
 809  
                                 // be deleted at the end
 810  3
                                 currentCluLoRelations.remove(loDisplay.getLoInfo().getId());
 811  3
                         } else if (NodeOperation.DELETE == operation
 812  
                     && currentCluLoRelations.containsKey(loDisplay.getLoInfo().getId())) {
 813  
                             
 814  
                 // Delete the Format and its relation
 815  2
                                 CluLoRelationInfo relationToDelete = currentCluLoRelations.get(loDisplay.getLoInfo().getId());
 816  2
                 BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo>(
 817  
                         null);
 818  2
                 relationToDeleteNode.setNodeData(relationToDelete);
 819  2
                 relationToDeleteNode.setOperation(NodeOperation.DELETE);
 820  2
                 results.add(relationToDeleteNode);
 821  
             
 822  2
                 BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler
 823  
                                         .disassemble(loDisplay, NodeOperation.DELETE);
 824  2
                 results.add(loNode);                                
 825  
 
 826  
                 // remove this entry from the map so we can tell what needs to
 827  
                 // be deleted at the end
 828  2
                 currentCluLoRelations.remove(loDisplay.getLoInfo().getId());                            
 829  48
                         }
 830  
                 }         
 831  
 
 832  
         // Now any leftover lo ids are no longer needed, so delete
 833  
         // los and relations
 834  24
         for (Entry<String, CluLoRelationInfo> entry : currentCluLoRelations.entrySet()) {
 835  
             // Create a new relation with the id of the relation we want to
 836  
             // delete
 837  1
                 CluLoRelationInfo relationToDelete = entry.getValue();
 838  1
             BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, CluLoRelationInfo>(
 839  
                     null);
 840  1
             relationToDeleteNode.setNodeData(relationToDelete);
 841  1
             relationToDeleteNode.setOperation(NodeOperation.DELETE);
 842  1
             results.add(relationToDeleteNode);
 843  
 
 844  
             try{
 845  1
                     LoInfo loToDelete = loService.getLo(entry.getKey());
 846  
             
 847  1
                     LoDisplayInfo loDisplayToDelete = loAssembler.assemble(loToDelete, null, false);
 848  1
                     BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = loAssembler
 849  
                                     .disassemble(loDisplayToDelete, NodeOperation.DELETE);
 850  1
                     results.add(loNode);
 851  0
                 } catch (DoesNotExistException e){
 852  0
                         LOG.warn("Trying to delete non exsistant LO:"+entry.getKey());
 853  0
             } catch (Exception e) {
 854  0
                                 throw new AssemblyException("Error disassembling LOs",e);
 855  1
                         }
 856  1
         }
 857  
                 
 858  24
                 return results;
 859  
         }
 860  
         
 861  
         private BaseDTOAssemblyNode<?, ?> disassembleGradingOptions(String cluId,
 862  
                         String courseState, List<String> options, List<CluResultInfo> currentCluResults, NodeOperation operation) throws AssemblyException {
 863  24
                 BaseDTOAssemblyNode<List<String>, CluResultInfo> cluResultNode = new BaseDTOAssemblyNode<List<String>, CluResultInfo>(null);
 864  
                                 
 865  24
                 String courseResultType=CourseAssemblerConstants.COURSE_RESULT_TYPE_GRADE; 
 866  24
                 String resultsDescription="Grading options";
 867  24
                 String resultDescription="Grading option";
 868  
 
 869  
                 // Get the current options and put them in a map of option type id/cluResult
 870  24
                 Map<String, ResultOptionInfo> currentResults = new HashMap<String, ResultOptionInfo>();
 871  
 
 872  24
                 CluResultInfo cluResult = null;
 873  
                 
 874  
                 //If this is not a create, lookup the results for this clu
 875  24
                 if (!NodeOperation.CREATE.equals(operation)) {
 876  11
                         for (CluResultInfo currentResult : currentCluResults) {
 877  8
                                 if (courseResultType.equals(currentResult.getType())) {
 878  3
                                         cluResult = currentResult;
 879  3
                                         if(NodeOperation.DELETE.equals(operation)){
 880  
                                                 //if this is a delete, then we only need the CluResultInfo
 881  1
                                                 cluResultNode.setOperation(NodeOperation.DELETE);
 882  
                                         }else{
 883  
                                                 //Find all the Result options and store in a map for easy access later
 884  2
                                                 cluResultNode.setOperation(NodeOperation.UPDATE);
 885  2
                                                 for(ResultOptionInfo resultOption:currentResult.getResultOptions()){
 886  4
                                                         currentResults.put(resultOption.getResultComponentId(), resultOption);
 887  
                                                 }
 888  
                                         }
 889  2
                                         break;
 890  
                                 }
 891  
                         }
 892  
                 }
 893  
 
 894  
                 //If this is a delete we don't need the result options, just the CluResultInfo
 895  24
                 if(!NodeOperation.DELETE.equals(operation)){
 896  23
                         if(cluResult == null){
 897  
                                 //Create a new resultInfo of the given type if one does not exist and set operation to Create
 898  21
                                 cluResult = new CluResultInfo();
 899  21
                                 cluResult.setCluId(cluId);
 900  21
                                 cluResult.setState(courseState);
 901  21
                                 cluResult.setType(courseResultType);
 902  21
                                 RichTextInfo desc = new RichTextInfo();
 903  21
                                 desc.setPlain(resultsDescription);
 904  21
                                 cluResult.setDesc(desc);
 905  21
                                 cluResult.setEffectiveDate(new Date());
 906  21
                                 cluResultNode.setOperation(NodeOperation.CREATE);
 907  
                         }
 908  
         
 909  23
                         cluResult.setResultOptions(new ArrayList<ResultOptionInfo>());
 910  
         
 911  
                         // Loop through all the credit options in this course
 912  23
                         for (String optionType : options) {
 913  46
                                 if(currentResults.containsKey(optionType)){
 914  
                                         //If the option exists already copy it to the new list of result options
 915  3
                                         ResultOptionInfo resultOptionInfo = currentResults.get(optionType);
 916  3
                                         cluResult.getResultOptions().add(resultOptionInfo);
 917  3
                                 }else{
 918  
                                         //Otherwise create a new result option
 919  43
                                         ResultOptionInfo resultOptionInfo = new ResultOptionInfo();
 920  43
                                         RichTextInfo desc = new RichTextInfo();
 921  43
                                         desc.setPlain(resultDescription);
 922  43
                                         resultOptionInfo.setDesc(desc);
 923  43
                                         resultOptionInfo.setResultComponentId(optionType);
 924  43
                                         resultOptionInfo.setState(courseState);
 925  
                                         
 926  43
                                         cluResult.getResultOptions().add(resultOptionInfo);
 927  46
                                 }
 928  
                         }
 929  
                 }
 930  
                 
 931  24
                 cluResultNode.setNodeData(cluResult);
 932  24
                 return cluResultNode;
 933  
         }
 934  
 
 935  
         // TODO This is pretty much a copy of the FormatAssembler's
 936  
         // disassembleActivities code... maybe can be made generic
 937  
         private List<BaseDTOAssemblyNode<?, ?>> disassembleFormats(String nodeId,
 938  
                         CourseInfo course, NodeOperation operation)
 939  
                         throws AssemblyException, DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException {
 940  
 
 941  24
                 List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
 942  
 
 943  
                 // Get the current formats and put them in a map of format id/relation
 944  
                 // id
 945  24
                 Map<String, String> currentformatIds = new HashMap<String, String>();
 946  
 
 947  24
                 if (!NodeOperation.CREATE.equals(operation)) {
 948  
                         try {
 949  11
                                 List<CluCluRelationInfo> formatRelationships = luService
 950  
                                                 .getCluCluRelationsByClu(course.getId());
 951  
                                 
 952  
                                 //formatRelationships = (null == formatRelationships) ? new ArrayList<CluCluRelationInfo>() : formatRelationships;
 953  
                                 
 954  11
                                 for (CluCluRelationInfo formatRelation : formatRelationships) {
 955  7
                                         if (CourseAssemblerConstants.COURSE_FORMAT_RELATION_TYPE
 956  
                                                         .equals(formatRelation.getType())) {
 957  7
                                                 currentformatIds.put(formatRelation.getRelatedCluId(),
 958  
                                                                 formatRelation.getId());
 959  
                                         }
 960  
                                 }
 961  0
                         } catch (DoesNotExistException e) {
 962  0
                         } catch (InvalidParameterException e) {
 963  0
                                 throw new AssemblyException("Error getting related formats", e);
 964  0
                         } catch (MissingParameterException e) {
 965  0
                                 throw new AssemblyException("Error getting related formats", e);
 966  0
                         } catch (OperationFailedException e) {
 967  0
                                 throw new AssemblyException("Error getting related formats", e);
 968  11
                         }
 969  
                 }
 970  
 
 971  
                 // Loop through all the formats in this course
 972  24
                 for (FormatInfo format : course.getFormats()) {
 973  
 
 974  
                     //  If this is a course create/new format update then all formats will be created
 975  49
                     if (NodeOperation.CREATE == operation
 976  
                             || (NodeOperation.UPDATE == operation && !currentformatIds.containsKey(format.getId()) )) {
 977  
                 // the format does not exist, so create
 978  
                 // Assemble and add the format
 979  43
                 BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler
 980  
                         .disassemble(format, NodeOperation.CREATE);
 981  43
                 results.add(formatNode);
 982  
 
 983  
                 // Create the relationship and add it as well
 984  43
                 CluCluRelationInfo relation = new CluCluRelationInfo();
 985  43
                 relation.setCluId(nodeId);
 986  43
                 relation.setRelatedCluId(formatNode.getNodeData().getId());// this
 987  
                 // should
 988  
                 // already
 989  
                 // be set even if
 990  
                 // it's a create
 991  43
                 relation
 992  
                         .setType(CourseAssemblerConstants.COURSE_FORMAT_RELATION_TYPE);
 993  43
                 relation.setState(course.getState());
 994  
 
 995  43
                 BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo> relationNode = new BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo>(
 996  
                         null);
 997  43
                 relationNode.setNodeData(relation);
 998  43
                 relationNode.setOperation(NodeOperation.CREATE);
 999  
 
 1000  43
                 results.add(relationNode);
 1001  43
             } else if (NodeOperation.UPDATE == operation
 1002  
                                         && currentformatIds.containsKey(format.getId())) {
 1003  
                                 // If the course already has this format, then just update the
 1004  
                                 // format
 1005  4
                                 BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler
 1006  
                                                 .disassemble(format, NodeOperation.UPDATE);
 1007  4
                                 results.add(formatNode);
 1008  
 
 1009  
                                 // remove this entry from the map so we can tell what needs to
 1010  
                                 // be deleted at the end
 1011  4
                                 currentformatIds.remove(format.getId());
 1012  4
                         } else if (NodeOperation.DELETE == operation
 1013  
                     && currentformatIds.containsKey(format.getId()))  {
 1014  
                             // Delete the Format and its relation
 1015  2
                     CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
 1016  2
                     relationToDelete.setId( currentformatIds.get(format.getId()) );
 1017  2
                     BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo>(
 1018  
                             null);
 1019  2
                     relationToDeleteNode.setNodeData(relationToDelete);
 1020  2
                     relationToDeleteNode.setOperation(NodeOperation.DELETE);
 1021  2
                     results.add(relationToDeleteNode);
 1022  
                         
 1023  2
                 BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler
 1024  
                 .disassemble(format, NodeOperation.DELETE);
 1025  2
                 results.add(formatNode);                                        
 1026  
 
 1027  
                 // remove this entry from the map so we can tell what needs to
 1028  
                 // be deleted at the end
 1029  2
                 currentformatIds.remove(format.getId());
 1030  49
                         }                            
 1031  
                 }
 1032  
                 
 1033  
         // Now any leftover format ids are no longer needed, so delete
 1034  
         // formats and relations. These formats have to be assembled first before they can be marked for deletion
 1035  24
         for (Entry<String, String> entry : currentformatIds.entrySet()) {
 1036  
             // Create a new relation with the id of the relation we want to
 1037  
             // delete
 1038  1
             CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
 1039  1
             relationToDelete.setId( entry.getValue() );
 1040  1
             BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<CourseInfo, CluCluRelationInfo>(
 1041  
                     null);
 1042  1
             relationToDeleteNode.setNodeData(relationToDelete);
 1043  1
             relationToDeleteNode.setOperation(NodeOperation.DELETE);
 1044  1
             results.add(relationToDeleteNode);
 1045  
             
 1046  1
             CluInfo formatCluToDelete = luService.getClu(entry.getKey());
 1047  1
             FormatInfo formatToDelete = formatAssembler.assemble(formatCluToDelete, null, false);
 1048  1
             BaseDTOAssemblyNode<FormatInfo, CluInfo> formatNode = formatAssembler
 1049  
             .disassemble(formatToDelete, NodeOperation.DELETE);
 1050  1
             results.add(formatNode);                                            
 1051  1
         }
 1052  
 
 1053  24
                 return results;
 1054  
         }
 1055  
         
 1056  
         private List<CourseVariationInfo> assembleVariations(List<CluIdentifierInfo> cluIdents) {
 1057  43
                 List<CourseVariationInfo> variations = new ArrayList<CourseVariationInfo>();
 1058  43
                 if (cluIdents != null) {
 1059  43
                         for (CluIdentifierInfo cluIdent : cluIdents) {
 1060  86
                                 if (cluIdent.getType() != null && 
 1061  
                                                 cluIdent.getType().equals(CourseAssemblerConstants.COURSE_VARIATION_IDENT_TYPE)) {
 1062  86
                                         CourseVariationInfo variation = new CourseVariationInfo();
 1063  86
                                         variation.setId(cluIdent.getId());
 1064  86
                                         variation.setType(cluIdent.getType());
 1065  86
                                         variation.setCourseNumberSuffix(cluIdent.getSuffixCode());
 1066  86
                                         variation.setSubjectArea(cluIdent.getDivision());
 1067  86
                                         variation.setVariationCode(cluIdent.getVariation());
 1068  86
                                         variation.setVariationTitle(cluIdent.getLongName());
 1069  86
                                         variations.add(variation);
 1070  86
                                 }
 1071  
                         }
 1072  
                 }
 1073  43
                 return variations;
 1074  
         }
 1075  
         
 1076  
         private List<CourseCrossListingInfo> assembleCrossListings(List<CluIdentifierInfo> cluIdents) {
 1077  43
                 List<CourseCrossListingInfo> crossListings = new ArrayList<CourseCrossListingInfo>();
 1078  43
                 if (cluIdents != null) {
 1079  43
                         for (CluIdentifierInfo cluIdent : cluIdents) {
 1080  86
                                 if (cluIdent.getType() != null && 
 1081  
                                                 cluIdent.getType().equals(CourseAssemblerConstants.COURSE_CROSSLISTING_IDENT_TYPE)) {
 1082  0
                                         CourseCrossListingInfo crosslisting = new CourseCrossListingInfo();
 1083  0
                                         crosslisting.setId(cluIdent.getId());
 1084  0
                                         crosslisting.setType(cluIdent.getType());
 1085  0
                                         crosslisting.setCourseNumberSuffix(cluIdent.getSuffixCode());
 1086  0
                                         crosslisting.setSubjectArea(cluIdent.getDivision());
 1087  0
                                         crosslisting.setDepartment(cluIdent.getOrgId());
 1088  0
                                         crossListings.add(crosslisting);
 1089  86
                                 }
 1090  
                         }
 1091  
                 }
 1092  43
                 return crossListings;
 1093  
         }
 1094  
         
 1095  
         // TODO This is pretty much a copy of the disassembleJoints
 1096  
         // code... maybe can be made generic
 1097  
         private List<BaseDTOAssemblyNode<?, ?>> disassembleJoints(String nodeId,
 1098  
                         CourseInfo course, NodeOperation operation)
 1099  
                         throws AssemblyException {
 1100  
 
 1101  24
                 List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
 1102  
 
 1103  
                 // Get the current joints and put them in a map of joint id/relation
 1104  
                 // id
 1105  24
                 Map<String, CluCluRelationInfo> currentJointIds = new HashMap<String, CluCluRelationInfo>();
 1106  
 
 1107  24
                 if (!NodeOperation.CREATE.equals(operation)) {
 1108  
                         try {
 1109  11
                                 List<CluCluRelationInfo> jointRelationships = luService
 1110  
                                                 .getCluCluRelationsByClu(course.getId());
 1111  11
                                 for (CluCluRelationInfo jointRelation : jointRelationships) {
 1112  7
                                         if (CourseAssemblerConstants.JOINT_RELATION_TYPE
 1113  
                                                         .equals(jointRelation.getType())) {
 1114  0
                                                 currentJointIds.put(jointRelation.getId(),jointRelation);
 1115  
                                         }
 1116  
                                 }
 1117  0
                         } catch (DoesNotExistException e) {
 1118  0
                         } catch (InvalidParameterException e) {
 1119  0
                                 throw new AssemblyException("Error getting related formats", e);
 1120  0
                         } catch (MissingParameterException e) {
 1121  0
                                 throw new AssemblyException("Error getting related formats", e);
 1122  0
                         } catch (OperationFailedException e) {
 1123  0
                                 throw new AssemblyException("Error getting related formats", e);
 1124  11
                         }
 1125  
                 }
 1126  
 
 1127  
                 // Loop through all the joints in this course
 1128  24
                 for (CourseJointInfo joint : course.getJoints()) {
 1129  
 
 1130  
                         // If this is a course create then all joints will be created
 1131  0
                         if (NodeOperation.UPDATE.equals(operation) && joint.getRelationId() != null
 1132  
                                         && currentJointIds.containsKey(joint.getRelationId())) {
 1133  
                                 // remove this entry from the map so we can tell what needs to
 1134  
                                 // be deleted at the end
 1135  0
                                 CluCluRelationInfo relation = currentJointIds.remove(joint.getRelationId());
 1136  0
                                 relation.setRelatedCluId(joint.getCourseId());
 1137  0
                                 BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo> jointNode = new BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo>(courseJointAssembler);
 1138  0
                                 jointNode.setBusinessDTORef(joint);
 1139  0
                                 jointNode.setNodeData(relation);
 1140  0
                                 jointNode.setOperation(NodeOperation.UPDATE);
 1141  0
                                 results.add(jointNode);
 1142  0
                         } else if (!NodeOperation.DELETE.equals(operation)) {
 1143  
                                 // the joint does not exist, so create cluclurelation
 1144  0
                                 BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo> jointNode = courseJointAssembler
 1145  
                                                 .disassemble(joint, NodeOperation.CREATE);
 1146  0
                                 jointNode.getNodeData().setCluId(nodeId);
 1147  0
                                 results.add(jointNode);
 1148  0
                         }
 1149  
                 }
 1150  
 
 1151  
         // Now any leftover joint ids are no longer needed, so delete
 1152  
         // joint relations
 1153  24
         for (String id : currentJointIds.keySet()) {
 1154  
             // Create a new relation with the id of the relation we want to
 1155  
             // delete
 1156  0
             CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
 1157  0
             relationToDelete.setId(id);
 1158  0
             BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<CourseJointInfo, CluCluRelationInfo>(
 1159  
                     courseJointAssembler);
 1160  0
             relationToDeleteNode.setNodeData(relationToDelete);
 1161  0
             relationToDeleteNode.setOperation(NodeOperation.DELETE);
 1162  0
             results.add(relationToDeleteNode);
 1163  0
         }
 1164  
                 
 1165  24
                 return results;
 1166  
         }
 1167  
 
 1168  
         public void setLuService(LuService luService) {
 1169  1
                 this.luService = luService;
 1170  1
         }
 1171  
 
 1172  
         public void setFormatAssembler(FormatAssembler formatAssembler) {
 1173  1
                 this.formatAssembler = formatAssembler;
 1174  1
         }
 1175  
 
 1176  
         public void setCourseJointAssembler(
 1177  
                         CourseJointAssembler courseJointAssembler) {
 1178  1
                 this.courseJointAssembler = courseJointAssembler;
 1179  1
         }
 1180  
 
 1181  
         public void setLoAssembler(LoAssembler loAssembler) {
 1182  1
                 this.loAssembler = loAssembler;
 1183  1
         }
 1184  
 
 1185  
         public void setLoService(LearningObjectiveService loService) {
 1186  1
                 this.loService = loService;
 1187  1
         }
 1188  
 
 1189  
     public void setCluAssemblerUtils(CluAssemblerUtils cluAssemblerUtils) {
 1190  1
         this.cluAssemblerUtils = cluAssemblerUtils;
 1191  1
     }
 1192  
 
 1193  
         public void setLrcService(LrcService lrcService) {
 1194  1
                 this.lrcService = lrcService;
 1195  1
         }
 1196  
 }