001/*
002 *  Copyright 2019 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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 */
016package org.ametys.plugins.odfsync.apogee.ws.structure;
017
018import java.rmi.RemoteException;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.stream.Collectors;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.avalon.framework.service.Serviceable;
026import org.apache.commons.lang3.StringUtils;
027
028import org.ametys.cms.contenttype.AttributeDefinition;
029import org.ametys.cms.data.ContentValue;
030import org.ametys.cms.data.type.ModelItemTypeConstants;
031import org.ametys.cms.repository.Content;
032import org.ametys.core.util.I18nUtils;
033import org.ametys.odf.course.Course;
034import org.ametys.odf.courselist.CourseList;
035import org.ametys.odf.enumeration.OdfReferenceTableHelper;
036import org.ametys.odf.orgunit.OrgUnit;
037import org.ametys.odf.program.AbstractProgram;
038import org.ametys.odf.program.Container;
039import org.ametys.odf.program.SubProgram;
040import org.ametys.plugins.odfsync.apogee.ws.ApogeeExportReport;
041import org.ametys.plugins.odfsync.apogee.ws.ApogeeExportReport.ExportStatus;
042import org.ametys.plugins.odfsync.apogee.ws.ApogeeWS;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.runtime.i18n.I18nizableText;
045import org.ametys.runtime.model.ModelItem;
046import org.ametys.runtime.plugin.component.AbstractLogEnabled;
047
048import gouv.education.apogee.commun.client.ws.creationse.CreationSEMetierServiceInterface;
049
050/**
051 * The abstract class to handle an export in Apogee
052 */
053public abstract class AbstractApogeeStructure extends AbstractLogEnabled implements ApogeeExportStructure, Serviceable
054{
055    /** The attribute name for the code Apogee */
056    public static final String CODE_APOGEE_ATTRIBUTE_NAME = "codeApogee";
057    
058    /** The false attribute name for the version Apogee */
059    public static final String VERSION_APOGEE_ATTRIBUTE_NAME = "versionApogee";
060    
061    /** The separator between the code and the version */
062    public static final String CODE_APOGEE_SEPARATOR = "-";
063    
064    /** The i18n utils */
065    protected I18nUtils _i18nUtils;
066    
067    /** The apogee WS */
068    protected ApogeeWS _apogeeWS;
069    
070    /** The Ametys object resolver */
071    protected AmetysObjectResolver _resolver;
072    
073    /** The ODF reference table helper */
074    protected OdfReferenceTableHelper _odfRefTableHelper;
075    
076    public void service(ServiceManager manager) throws ServiceException
077    {
078        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
079        _apogeeWS = (ApogeeWS) manager.lookup(ApogeeWS.ROLE);
080        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
081        _odfRefTableHelper = (OdfReferenceTableHelper) manager.lookup(OdfReferenceTableHelper.ROLE);
082    }
083    
084    /**
085     * Mandatory data to export a content in a DIP in Apogee
086     * @param content the content to export
087     * @return the list of mandatory data
088     */
089    public List<String> getDIPMandatoryData(Content content)
090    {
091        List<String> mandatoryData = new ArrayList<>();
092        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME);
093        mandatoryData.add(AbstractProgram.EDUCATION_KIND);
094        mandatoryData.add("cycleApogee");
095        mandatoryData.add(AbstractProgram.DEGREE);
096        
097        return mandatoryData;
098    }
099    
100    /**
101     * Mandatory data to export a content in a VDI in Apogee
102     * @param content the content to export
103     * @return the list of mandatory data
104     */
105    public List<String> getVDIMandatoryData(Content content)
106    {
107        // The orgunit Apogee Code is mandatory too
108        
109        List<String> mandatoryData = new ArrayList<>();
110        mandatoryData.add(VERSION_APOGEE_ATTRIBUTE_NAME);
111        mandatoryData.add("start-date-recruitment");
112        mandatoryData.add("end-date-recruitment");
113        mandatoryData.add("start-date-validation");
114        mandatoryData.add("end-date-validation");
115        
116        return mandatoryData;
117    }
118    
119    /**
120     * Mandatory data to export a content in a ETP in Apogee
121     * @param content the content to export
122     * @return the list of mandatory data
123     */
124    public List<String> getETPMandatoryData(Content content)
125    {
126        // The orgunit Apogee Code is mandatory too
127        
128        List<String> mandatoryData = new ArrayList<>();
129        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME); 
130        mandatoryData.add("cycleApogee");
131        
132        return mandatoryData;
133    }
134    
135    /**
136     * Mandatory data to export a content in a VET in Apogee
137     * @param content the content to export
138     * @return the list of mandatory data
139     */
140    public List<String> getVETMandatoryData(Content content)
141    {
142        // The orgunit Apogee Code is mandatory too
143        
144        List<String> mandatoryData = new ArrayList<>();
145        mandatoryData.add(VERSION_APOGEE_ATTRIBUTE_NAME);
146        mandatoryData.add("duration-apogee");
147        mandatoryData.add("inscription-types"); 
148        mandatoryData.add("cips"); 
149        
150        return mandatoryData;
151    }
152    
153    /**
154     * Mandatory data to export a content in a LSE in Apogee
155     * @param content the content to export
156     * @return the list of mandatory data
157     */
158    public List<String> getLSEMandatoryData(Content content)
159    {
160        List<String> mandatoryData = new ArrayList<>();
161        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME); 
162        mandatoryData.add("choiceType"); 
163        
164        String choiceTypeAmetys = content.getValue("choiceType");
165        if (choiceTypeAmetys.equals("CHOICE"))
166        {
167            mandatoryData.add("min"); 
168        }
169        
170        return mandatoryData;
171    }
172    
173    /**
174     * Mandatory data to export a content in a ELP in Apogee
175     * @param content the content to export
176     * @return the list of mandatory data
177     */
178    public List<String> getELPMandatoryData(Content content)
179    {
180        // The orgunit Apogee Code is mandatory too
181        
182        List<String> mandatoryData = new ArrayList<>();
183        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME); 
184        mandatoryData.add("cips"); 
185        mandatoryData.add("orgUnit"); 
186        if (content instanceof Container)
187        {
188            mandatoryData.add("nature"); 
189        }
190        else if (content instanceof Course)
191        {
192            mandatoryData.add("courseType"); 
193        }
194        
195        return mandatoryData;
196    }
197    
198    /**
199     * Mandatory data to export an orgunit for a DIP in Apogee
200     * @return the list of mandatory data
201     */
202    public List<String> getOrgUnitMandatoryDataForDIP()
203    {
204        List<String> mandatoryData = new ArrayList<>();
205        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME); 
206        
207        return mandatoryData;
208    }
209    
210    /**
211     * Mandatory data to export an orgunit for a ETP in Apogee
212     * @return the list of mandatory data
213     */
214    public List<String> getOrgUnitMandatoryDataForETP()
215    {
216        List<String> mandatoryData = new ArrayList<>();
217        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME); 
218        mandatoryData.add("codeCGE"); 
219        
220        return mandatoryData;
221    }
222    
223    /**
224     * Mandatory data to export an orgunit for a ELP in Apogee
225     * @return the list of mandatory data
226     */
227    public List<String> getOrgUnitMandatoryDataForELP()
228    {
229        List<String> mandatoryData = new ArrayList<>();
230        mandatoryData.add(CODE_APOGEE_ATTRIBUTE_NAME); 
231        
232        return mandatoryData;
233    }
234    
235    /**
236     * Get the code of the container nature
237     * @param container the container
238     * @param report the Apogee export report
239     * @return the code of the nature
240     */
241    public String getContainerNatureCode(Container container, ApogeeExportReport report)
242    {
243        String nature = container.getNature();
244        if (StringUtils.isNotBlank(nature))
245        {
246            return _odfRefTableHelper.getItemCode(nature);
247        }
248        
249        // The structure is not handled by this export because the container has no type
250        report.setExportStatus(ExportStatus.CONTENT_STRUCTURE_INVALID);
251        
252        return null;
253    }
254    
255    /**
256     * True if the container has for nature "annee"
257     * @param container the container
258     * @param report the Apogee export report
259     * @return true if it's a year container
260     */
261    public boolean isYearContainer(Container container, ApogeeExportReport report)
262    {
263        String containerNatureCode = getContainerNatureCode(container, report);
264        return containerNatureCode != null ? "annee".equals(containerNatureCode) : false;
265    }
266    
267    /**
268     * True if the container has for nature "semestre"
269     * @param container the container
270     * @param report the Apogee export report
271     * @return true if it's a semester container
272     */
273    public boolean isSemesterContainer(Container container, ApogeeExportReport report)
274    {
275        String containerNatureCode = getContainerNatureCode(container, report);
276        return containerNatureCode != null ? "semestre".equals(containerNatureCode) : false;
277    }
278    
279    /**
280     * Get the code Apogee of the content
281     * @param content the content
282     * @return the code Apogee
283     */
284    public String getCodeApogee(Content content)
285    {
286        String codeApogee = content.getValue(CODE_APOGEE_ATTRIBUTE_NAME);
287        if (StringUtils.isNotBlank(codeApogee) && codeApogee.contains(CODE_APOGEE_SEPARATOR))
288        {
289            return StringUtils.substringBefore(codeApogee, CODE_APOGEE_SEPARATOR);
290        }
291        else
292        {
293            return codeApogee;
294        }
295    }
296    
297    /**
298     * Get the version, Apogee of the content
299     * @param content the content
300     * @return the version Apogee
301     */
302    public Long getVersionApogee(Content content)
303    {
304        String codeApogee = content.getValue(CODE_APOGEE_ATTRIBUTE_NAME);
305        if (StringUtils.isNotBlank(codeApogee) && codeApogee.contains(CODE_APOGEE_SEPARATOR))
306        {
307            return Long.parseLong(StringUtils.substringAfterLast(codeApogee, CODE_APOGEE_SEPARATOR));
308        }
309        else
310        {
311            return null;
312        }
313    }
314    
315    /**
316     * Check if the subProgram has the good data and structure to be export in Apogee
317     * @param subProgram the subProgram to check
318     * @param report the Apogee export report
319     */
320    public abstract void checkSubProgram(SubProgram subProgram, ApogeeExportReport report);
321    
322    /**
323     * Check if the container as year has the good data and structure to be export in Apogee
324     * @param container the container to check
325     * @param report the Apogee export report
326     */
327    public abstract void checkContainerAsYear(Container container, ApogeeExportReport report);
328    
329    /**
330     * Check if the container as semester has the good data and structure to be export in Apogee
331     * @param container the container to check
332     * @param report the Apogee export report
333     */
334    public abstract void checkContainerAsSemester(Container container, ApogeeExportReport report);
335    
336    /**
337     * Check if the course list has the good data and structure to be export in Apogee
338     * @param courseList the course list to check
339     * @param report the Apogee export report
340     */
341    public void checkCourseList(CourseList courseList, ApogeeExportReport report)
342    {
343        // Check mandatory data for course list
344        checkMandatoryDataForContent(courseList, getLSEMandatoryData(courseList), report);
345        
346        // Check the course list structure
347        for (Course course : courseList.getCourses())
348        {
349            checkCourse(course, report);
350        }
351    }
352    
353    /**
354     * Check if the course has the good data and structure to be export in Apogee
355     * @param course the course to check
356     * @param report the Apogee export report
357     */
358    public void checkCourse(Course course, ApogeeExportReport report)
359    {
360        // Check mandatory data for course
361        checkMandatoryDataForContent(course, getELPMandatoryData(course), report);
362        
363        // Check mandatory data for course orgUnits
364        checkMandatoryDataForOrgunits(course, course.getOrgUnits(), getOrgUnitMandatoryDataForELP(), report);
365        
366         // Check the course structure
367        for (CourseList courseList : course.getCourseLists())
368        {
369            checkCourseList(courseList, report);
370        }
371    }
372    
373    
374    /**
375     * Check if the content has a value for all mandatory data
376     * @param content the content to check
377     * @param mandatoryData the list of mandatory data path
378     * @param report the Apogee export report
379     */
380    public void checkMandatoryDataForContent(Content content, List<String> mandatoryData, ApogeeExportReport report)
381    {
382        for (String dataPath : mandatoryData)
383        {
384            if (VERSION_APOGEE_ATTRIBUTE_NAME.equals(dataPath))
385            {
386                _checkVersionApogee(content, report);
387            }
388            else if (content.hasNonEmptyValue(dataPath))
389            {
390                if (org.ametys.runtime.model.type.ModelItemTypeConstants.STRING_TYPE_ID.equals(content.getType(dataPath).getId()))
391                {
392                    _checkSimpleData(content, dataPath, report);    
393                }
394                else if (ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID.equals(content.getType(dataPath).getId()))
395                {
396                    _checkTableRef(content, dataPath, report);
397                }
398            }
399            else
400            {
401                report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
402                ModelItem modelItem = content.getDefinition(dataPath);
403                report.addMandatoryDataPath(content, modelItem);
404            }
405        }
406    }
407
408    /**
409     * Check if the content has a value for the simple data
410     * @param content the content to check
411     * @param dataPath the data path
412     * @param report the Apogee export report
413     */
414    protected void _checkSimpleData(Content content, String dataPath, ApogeeExportReport report)
415    {
416        if (content.isMultiple(dataPath))
417        {
418            String[] values = content.getValue(dataPath);
419            if (values.length == 0 || StringUtils.isBlank(values[0]))
420            {
421                report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
422                ModelItem modelItem = content.getDefinition(dataPath);
423                report.addMandatoryDataPath(content, modelItem);
424            }
425        }
426        else
427        {
428            String value = content.getValue(dataPath);
429            if (StringUtils.isBlank(value))
430            {
431                report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
432                ModelItem modelItem = content.getDefinition(dataPath);
433                report.addMandatoryDataPath(content, modelItem);
434            }
435        }
436    }
437
438    /**
439     * Check if the content has a value for the simple table ref data
440     * @param content the content to check
441     * @param dataPath the data path
442     * @param report the Apogee export report
443     */
444    protected void _checkTableRef(Content content, String dataPath, ApogeeExportReport report)
445    {
446        // We handle only simple table ref
447        if (!content.isMultiple(dataPath))
448        {
449            ContentValue value = content.getValue(dataPath);
450            Content refContent = value.getContent();
451            if (!value.hasValue(CODE_APOGEE_ATTRIBUTE_NAME) || StringUtils.isBlank(refContent.getValue(CODE_APOGEE_ATTRIBUTE_NAME)))
452            {
453                report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
454                ModelItem modelItem = refContent.getDefinition(CODE_APOGEE_ATTRIBUTE_NAME);
455                report.addMandatoryDataPath(refContent, modelItem);
456            }
457        }
458    }
459
460    /**
461     * Check if the version Apogee exist in the Apogee code
462     * @param content the content
463     * @param report the Apogee export report
464     */
465    protected void _checkVersionApogee(Content content, ApogeeExportReport report)
466    {
467        if (!content.hasValue(CODE_APOGEE_ATTRIBUTE_NAME) || StringUtils.isBlank(content.getValue(CODE_APOGEE_ATTRIBUTE_NAME)))
468        {
469            report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
470            AttributeDefinition<String> attributeDefinition = new AttributeDefinition<>();
471            attributeDefinition.setLabel(new I18nizableText("plugin.odf-sync", "PLUGINS_ODF_MANDATORY_APOGEE_VERSION_MESSAGE"));
472            report.addMandatoryDataPath(content, attributeDefinition);
473        }
474        else
475        {
476            String codeApogee = content.getValue(CODE_APOGEE_ATTRIBUTE_NAME);
477            if (!codeApogee.contains("-") || StringUtils.isBlank(StringUtils.substringAfterLast(codeApogee, "-")))
478            {
479                report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
480                AttributeDefinition<String> attributeDefinition = new AttributeDefinition<>();
481                attributeDefinition.setLabel(new I18nizableText("plugin.odf-sync", "PLUGINS_ODF_MANDATORY_APOGEE_VERSION_MESSAGE"));
482                report.addMandatoryDataPath(content, attributeDefinition);
483            }
484        }
485    }
486    
487    /**
488     * Check if the orgUnits has a value for all mandatory data
489     * @param content the content to check
490     * @param orgUnits the list of orgUnit to check
491     * @param mandatoryData the list of mandatory data path
492     * @param report the Apogee export report
493     */
494    public void checkMandatoryDataForOrgunits(Content content, List<String> orgUnits, List<String> mandatoryData, ApogeeExportReport report)
495    {
496        if (orgUnits.size() == 0)
497        {
498            // No orgUnits, course data is invalid
499            report.setExportStatus(ExportStatus.CONTENT_DATA_INVALID);
500            ModelItem modelItem = content.getDefinition("orgUnit");
501            report.addMandatoryDataPath(content, modelItem);
502        }
503        else
504        {
505            for (String orgUnitId : orgUnits)
506            {
507                OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
508                checkMandatoryDataForContent(orgUnit, mandatoryData, report);
509            }
510        }
511    }
512    
513    /**
514     * Create a course list in Apogee
515     * @param courseList the course list to create
516     * @param parentApogee the parent in Apogee
517     * @param creationService the service to create element in Apogee
518     * @param report the Apogee export report
519     * @throws RemoteException if an export error occurred
520     */
521    protected void _createCourseList(CourseList courseList, Content parentApogee, CreationSEMetierServiceInterface creationService, ApogeeExportReport report) throws RemoteException
522    {
523        // Create ELPs before the list
524        for (Course course : courseList.getCourses()) 
525        {
526            _createCourse(course, courseList, creationService, report);
527        }
528        
529        String codELP = getCodeApogee(parentApogee);
530        String codLSE = getCodeApogee(courseList);
531
532        _apogeeWS.createLSE(courseList, null, codLSE, creationService);
533        
534        Long nbELP = null;
535        Double ectsMin = 0D;
536        Double ectsMax = 0D;
537        String choiceTypeAmetys = courseList.getValue("choiceType");
538        if (choiceTypeAmetys.equals("CHOICE"))
539        {
540            nbELP = courseList.getValue("min");
541            
542            List<Double> ectsList = courseList.getCourses()
543                .stream()
544                .map(Course::getEcts)
545                .sorted()
546                .collect(Collectors.toList());
547            
548            for (int i = 0; i < nbELP; i++)
549            {
550                ectsMin += ectsList.get(0);
551            }
552            
553            ectsMin = (Math.floor(ectsMin) == 0D) ? 1D : Math.floor(ectsMin);
554            
555            for (int i = 1; i <= nbELP; i++)
556            {
557                ectsMax += ectsList.get(ectsList.size() - i);
558            }
559            
560            ectsMax = (Math.ceil(ectsMax) == 0D || ectsMin > ectsMax) ? 1D : Math.ceil(ectsMax);
561        }
562        
563        _apogeeWS.createLinkETPELPLSE(null, null, codLSE, codELP, nbELP, ectsMin, ectsMax, creationService);
564    }
565    
566    /**
567     * Create a course in Apogee
568     * @param course the course to create
569     * @param parentApogee the parent in Apogee
570     * @param creationService the service to create element in Apogee
571     * @param report the Apogee export report
572     * @throws RemoteException if an export error occurred
573     */
574    protected void _createCourse(Course course, Content parentApogee, CreationSEMetierServiceInterface creationService, ApogeeExportReport report) throws RemoteException
575    {
576        String codELP = getCodeApogee(course);
577        _apogeeWS.createELP(course, null, codELP, creationService);
578        
579        for (CourseList courseList : course.getCourseLists())
580        {
581            _createCourseList(courseList, course, creationService, report);
582        }
583    }
584}