001    /*
002     * Copyright 2007 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 1.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl1.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.lum.program.service.assembler;
017    
018    import java.util.ArrayList;
019    import java.util.List;
020    import java.util.Map;
021    
022    import org.apache.log4j.Logger;
023    import org.kuali.student.common.assembly.BOAssembler;
024    import org.kuali.student.common.assembly.BaseDTOAssemblyNode;
025    import org.kuali.student.common.assembly.BaseDTOAssemblyNode.NodeOperation;
026    import org.kuali.student.common.assembly.data.AssemblyException;
027    import org.kuali.student.common.dto.AmountInfo;
028    import org.kuali.student.common.dto.DtoConstants;
029    import org.kuali.student.common.exceptions.DataValidationErrorException;
030    import org.kuali.student.common.exceptions.DoesNotExistException;
031    import org.kuali.student.common.exceptions.InvalidParameterException;
032    import org.kuali.student.common.exceptions.MissingParameterException;
033    import org.kuali.student.common.exceptions.OperationFailedException;
034    import org.kuali.student.lum.course.service.assembler.CourseAssembler;
035    import org.kuali.student.lum.lu.dto.CluCluRelationInfo;
036    import org.kuali.student.lum.lu.dto.CluInfo;
037    import org.kuali.student.lum.lu.service.LuService;
038    import org.kuali.student.lum.program.dto.CoreProgramInfo;
039    import org.kuali.student.lum.program.dto.MajorDisciplineInfo;
040    import org.kuali.student.lum.program.dto.ProgramVariationInfo;
041    import org.kuali.student.lum.service.assembler.CluAssemblerUtils;
042    
043    
044    /**
045     * @author KS TODO - Much of this should be shared with ProgramVariationAssembler (and probably other Program Assemblers to
046     *         come). AssemblerUtils?
047     */
048    public class MajorDisciplineAssembler implements BOAssembler<MajorDisciplineInfo, CluInfo> {
049        final static Logger LOG = Logger.getLogger(CourseAssembler.class);
050    
051        private LuService luService;
052    
053        private ProgramVariationAssembler programVariationAssembler;
054        private CoreProgramAssembler coreProgramAssembler;
055        private CluAssemblerUtils cluAssemblerUtils;
056        private ProgramAssemblerUtils programAssemblerUtils;
057    
058        @Override
059        public MajorDisciplineInfo assemble(CluInfo clu, MajorDisciplineInfo majorDiscipline, boolean shallowBuild) throws AssemblyException {
060            MajorDisciplineInfo mdInfo = (null != majorDiscipline) ? majorDiscipline : new MajorDisciplineInfo();
061    
062            // Copy all the data from the clu to the majordiscipline
063            programAssemblerUtils.assembleBasics(clu, mdInfo);
064            programAssemblerUtils.assembleIdentifiers(clu, mdInfo);
065            programAssemblerUtils.assembleBasicAdminOrgs(clu, mdInfo);
066            programAssemblerUtils.assembleFullOrgs(clu, mdInfo);
067            programAssemblerUtils.assembleAtps(clu, mdInfo);
068            programAssemblerUtils.assembleLuCodes(clu, mdInfo);
069    
070            mdInfo.setIntensity((null != clu.getIntensity()) ? clu.getIntensity().getUnitType() : null);
071            mdInfo.setStdDuration(clu.getStdDuration());
072            mdInfo.setPublishedInstructors(clu.getInstructors());
073            mdInfo.setCampusLocations(clu.getCampusLocations());        
074            mdInfo.setAccreditingAgencies(clu.getAccreditations());
075            mdInfo.setEffectiveDate(clu.getEffectiveDate());
076            mdInfo.setDescr(clu.getDescr());
077            mdInfo.setVersionInfo(clu.getVersionInfo());
078            mdInfo.setNextReviewPeriod(clu.getNextReviewPeriod());
079    
080            if (!shallowBuild) {
081                    programAssemblerUtils.assembleRequirements(clu, mdInfo);
082                mdInfo.setCredentialProgramId(programAssemblerUtils.getCredentialProgramID(clu.getId()));
083                mdInfo.setResultOptions(programAssemblerUtils.assembleResultOptions(clu.getId()));
084                mdInfo.setLearningObjectives(cluAssemblerUtils.assembleLos(clu.getId(), shallowBuild));
085                mdInfo.setVariations(assembleVariations(clu.getId(), shallowBuild));
086                mdInfo.setOrgCoreProgram(assembleCoreProgram(clu.getId(), shallowBuild));
087                programAssemblerUtils.assemblePublications(clu, mdInfo);
088            }
089            
090           return mdInfo;
091        }
092    
093        private CoreProgramInfo assembleCoreProgram(String cluId, boolean shallowBuild) throws AssemblyException {
094            CoreProgramInfo coreProgramInfo = null;
095            try {
096                List<CluInfo> corePrograms = luService.getRelatedClusByCluId(cluId, ProgramAssemblerConstants.HAS_CORE_PROGRAM);
097                // TODO - is it an error if there's more than one core program?
098                if (corePrograms.size() == 1) {
099                    coreProgramInfo = coreProgramAssembler.assemble(corePrograms.get(0), null, shallowBuild);
100                } else if (corePrograms.size() > 1) {
101                    throw new AssemblyException(new DataValidationErrorException("MajorDiscipline has more than one associated Core Program"));
102                }
103            } catch (Exception e) {
104                throw new AssemblyException(e);
105            }
106            return coreProgramInfo;
107        }
108    
109        private List<ProgramVariationInfo> assembleVariations(String cluId, boolean shallowBuild) throws AssemblyException {
110            List<ProgramVariationInfo> variations = new ArrayList<ProgramVariationInfo>();
111    
112            try {
113                    Map<String, CluCluRelationInfo> currentRelations = null;
114                    currentRelations = programAssemblerUtils.getCluCluActiveRelations(cluId, ProgramAssemblerConstants.HAS_PROGRAM_VARIATION);
115                    
116                    if(currentRelations != null && !currentRelations.isEmpty()){
117                            for (String variationId : currentRelations.keySet()) {
118                                    CluInfo variationClu = luService.getClu(variationId);
119                                    variations.add(programVariationAssembler.assemble(variationClu, null, shallowBuild));
120                            }
121                    }
122            } catch (Exception e) {
123                throw new AssemblyException(e);
124            }
125            return variations;
126        }
127    
128        @Override
129        public BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> disassemble(MajorDisciplineInfo major, NodeOperation operation) throws AssemblyException {
130                    if (major == null) {
131                        LOG.error("Major for  disassemble is null!");
132                            throw new AssemblyException("Major cannot be null");
133                    }
134    
135            //TODO   IDs for objects w/o ids
136    
137                    BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> result = new BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo>(
138                                    this);
139                    
140                    CluInfo clu;
141                    try {
142                            clu = (NodeOperation.UPDATE == operation) ? luService.getClu(major.getId()) : new CluInfo();
143            } catch (Exception e) {
144                            throw new AssemblyException("Error getting existing learning unit during major update", e);
145            } 
146            
147            boolean stateChanged = NodeOperation.UPDATE == operation && major.getState() != null && !major.getState().equals(clu.getState());
148            
149            programAssemblerUtils.disassembleBasics(clu, major);
150            if (major.getId() == null)
151                major.setId(clu.getId());
152            programAssemblerUtils.disassembleLuCodes(clu, major, operation);
153            programAssemblerUtils.disassembleAdminOrgs(clu, major, operation);
154            programAssemblerUtils.disassembleAtps(clu, major, operation);
155            programAssemblerUtils.disassembleIdentifiers(clu, major, operation);
156            programAssemblerUtils.disassemblePublications(clu, major, operation, result);
157            
158            if(major.getProgramRequirements() != null && !major.getProgramRequirements().isEmpty()) {
159                    programAssemblerUtils.disassembleRequirements(clu, major, operation, result, stateChanged);             
160            }
161    
162            if (major.getVariations() != null && !major.getVariations().isEmpty()) {
163                try {
164                                    disassembleVariations(major, operation, result);
165                            } catch (Exception e) {
166                                    throw new AssemblyException("Error diassembling Variations during major update", e);
167                            } 
168            }
169            if (major.getOrgCoreProgram() != null ) {
170                disassembleCoreProgram(major, operation, result);
171            }
172            if (major.getCredentialProgramId() != null) {
173                disassembleCredentialProgram(major, operation, result);
174            }
175            if (major.getResultOptions() != null) {
176                disassembleResultOptions(major, operation, result);
177            }
178            if (major.getLearningObjectives() != null) {
179                disassembleLearningObjectives(major, operation, result);
180            }
181    
182            AmountInfo intensity = new AmountInfo();
183            intensity.setUnitType(major.getIntensity());
184                    clu.setIntensity(intensity);
185            clu.setStdDuration(major.getStdDuration());
186            clu.setInstructors(major.getPublishedInstructors());
187    
188            clu.setNextReviewPeriod(major.getNextReviewPeriod());
189            clu.setEffectiveDate(major.getEffectiveDate());
190    
191            clu.setCampusLocations(major.getCampusLocations());
192            clu.setDescr(major.getDescr());
193    
194            clu.setAccreditations(major.getAccreditingAgencies());
195            clu.setNextReviewPeriod(major.getNextReviewPeriod());
196            clu.setState(major.getState());
197    
198                    // Add the Clu to the result
199                    result.setNodeData(clu);
200                    result.setOperation(operation);
201                    result.setBusinessDTORef(major);
202    
203            return result;
204        }
205    
206        private void disassembleLearningObjectives(MajorDisciplineInfo major, NodeOperation operation, BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> result) throws AssemblyException {
207            try {
208                List<BaseDTOAssemblyNode<?, ?>> loResults = cluAssemblerUtils.disassembleLos(major.getId(), major.getState(),  major.getLearningObjectives(), operation);
209                if (loResults != null) {
210                    result.getChildNodes().addAll(loResults);
211                }
212            } catch (DoesNotExistException e) {
213            } catch (Exception e) {
214                throw new AssemblyException("Error while disassembling los", e);
215            }
216        }
217    
218        private void disassembleResultOptions(MajorDisciplineInfo major, NodeOperation operation, BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> result) throws AssemblyException {
219            //TODO Check for ProgramAssemblerConstants.CERTIFICATE_RESULTS too
220            
221            BaseDTOAssemblyNode<?, ?> degreeResults = cluAssemblerUtils.disassembleCluResults(
222                    major.getId(), major.getState(), major.getResultOptions(), operation, ProgramAssemblerConstants.DEGREE_RESULTS, "Result options", "Result option");
223            if (degreeResults != null) {
224                result.getChildNodes().add(degreeResults);
225            }
226        }
227    
228        private void disassembleCredentialProgram(MajorDisciplineInfo major, NodeOperation operation, BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> result) throws AssemblyException {
229    
230            List<BaseDTOAssemblyNode<?,?>> credentialResults;
231            try {
232                credentialResults = programAssemblerUtils.disassembleCredentialProgram(major, operation, ProgramAssemblerConstants.HAS_MAJOR_PROGRAM);
233                if (credentialResults != null) {
234                    result.getChildNodes().addAll(credentialResults);
235                }
236            } catch (Exception e) {
237                throw new AssemblyException("Error while disassembling Credential program", e);
238            }
239        }
240    
241        private void disassembleVariations(MajorDisciplineInfo major, NodeOperation operation, BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> result) throws AssemblyException, DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException {
242            Map<String, CluCluRelationInfo> currentRelations = null;
243            List<BaseDTOAssemblyNode<?, ?>> nodes = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
244            
245            if (!NodeOperation.CREATE.equals(operation)){
246                    currentRelations = programAssemblerUtils.getCluCluActiveRelations(major.getId(), ProgramAssemblerConstants.HAS_PROGRAM_VARIATION);
247            }
248            
249            // Loop through all the variations in this MD
250            for (ProgramVariationInfo variation : major.getVariations()) {
251                BaseDTOAssemblyNode<?,?> variationNode;
252                variation.setState(major.getState());
253                try {
254                        if (NodeOperation.UPDATE.equals(operation) && variation.getId() != null
255                                                    && (currentRelations != null && currentRelations.containsKey(variation.getId()))) {
256                            // If the relationship already exists update it
257                            // remove this entry from the map so we can tell what needs to be deleted at the end
258                            variationNode = programVariationAssembler.disassemble(variation, operation);
259                            if (variationNode != null) nodes.add(variationNode);
260                            currentRelations.remove(variation.getId());  
261                        } else if (!NodeOperation.DELETE.equals(operation)) {
262                                            // the variation does not exist, so create variation & cluclurelation
263                            variationNode = programVariationAssembler.disassemble(variation, NodeOperation.CREATE);
264                            if (variationNode != null) nodes.add(variationNode);
265                                            programAssemblerUtils.addCreateRelationNode(major.getId(), variation.getId(), ProgramAssemblerConstants.HAS_PROGRAM_VARIATION, nodes);
266                                    }
267                } catch (Exception e) {
268                    throw new AssemblyException("Error while disassembling Variation", e);
269                } 
270            }
271            
272            // Now any leftover variation ids are no longer needed, so suspend them
273            if(currentRelations != null && currentRelations.size() > 0){
274                    programAssemblerUtils.addSuspendedRelationNodes(currentRelations, nodes);       
275                    addInactivateVariationNodes(currentRelations, nodes);
276            }
277    
278            result.getChildNodes().addAll(nodes);
279        }
280    
281        private void addInactivateVariationNodes(Map<String, CluCluRelationInfo> currentRelations, List<BaseDTOAssemblyNode<?, ?>> nodes) throws AssemblyException{
282            for (String variationId : currentRelations.keySet()) {
283                            CluInfo variationClu;
284                            try {
285                                    variationClu = luService.getClu(variationId);
286                                    ProgramVariationInfo delVariation = programVariationAssembler.assemble(variationClu, null, true);
287                                    delVariation.setState(DtoConstants.STATE_SUSPENDED);
288                                    BaseDTOAssemblyNode<?,?> variationNode = programVariationAssembler.disassemble(delVariation , NodeOperation.UPDATE);
289                                    if (variationNode != null) nodes.add(variationNode);
290                            } catch (Exception e) {
291                                    throw new AssemblyException("Error while disassembling variation, deactivateVariations", e);
292                            }
293            }
294        }
295        
296        private void disassembleCoreProgram(MajorDisciplineInfo major, NodeOperation operation, BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> result) throws AssemblyException {
297    
298            BaseDTOAssemblyNode<?,?> coreResults;
299            try {
300                    major.getOrgCoreProgram().setState(major.getState());
301                coreResults = coreProgramAssembler.disassemble(major.getOrgCoreProgram(), operation);
302                if (coreResults != null) {
303                    result.getChildNodes().add(coreResults);
304                }
305            } catch (Exception e) {
306                throw new AssemblyException("Error while disassembling Core program", e);
307            }
308        }
309    
310        // Setters for Spring
311        public void setLuService(LuService luService) {
312            this.luService = luService;
313        }
314    
315            public void setProgramVariationAssembler(ProgramVariationAssembler programVariationAssembler) {
316            this.programVariationAssembler = programVariationAssembler;
317        }
318    
319        public ProgramVariationAssembler getProgramVariationAssembler() {
320            return programVariationAssembler;
321        }
322    
323        public void setCoreProgramAssembler(CoreProgramAssembler coreProgramAssembler) {
324            this.coreProgramAssembler = coreProgramAssembler;
325        }
326    
327        public void setCluAssemblerUtils(CluAssemblerUtils cluAssemblerUtils) {
328            this.cluAssemblerUtils = cluAssemblerUtils;
329        }
330    
331        public void setProgramAssemblerUtils(ProgramAssemblerUtils programAssemblerUtils) {
332            this.programAssemblerUtils = programAssemblerUtils;
333        }
334    }