001/*
002 *  Copyright 2018 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.odfpilotage.report.impl;
017
018import java.io.File;
019import java.io.FileOutputStream;
020import java.io.IOException;
021import java.util.Iterator;
022import java.util.List;
023
024import javax.xml.transform.Result;
025import javax.xml.transform.TransformerFactory;
026import javax.xml.transform.sax.SAXTransformerFactory;
027import javax.xml.transform.sax.TransformerHandler;
028import javax.xml.transform.stream.StreamResult;
029
030import org.apache.cocoon.xml.AttributesImpl;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.commons.io.FileUtils;
033import org.apache.commons.lang3.StringUtils;
034import org.xml.sax.SAXException;
035
036import org.ametys.cms.FilterNameHelper;
037import org.ametys.cms.data.ContentDataHelper;
038import org.ametys.odf.course.Course;
039import org.ametys.odf.courselist.CourseList;
040import org.ametys.odf.courselist.CourseList.ChoiceType;
041import org.ametys.odf.coursepart.CoursePart;
042import org.ametys.odf.orgunit.OrgUnit;
043import org.ametys.odf.program.Container;
044import org.ametys.odf.program.Program;
045import org.ametys.odf.program.SubProgram;
046import org.ametys.plugins.odfpilotage.helper.PilotageHelper;
047import org.ametys.plugins.odfpilotage.report.impl.mcc.MCCAmetysObjectTree;
048import org.ametys.plugins.repository.AmetysObject;
049import org.ametys.plugins.repository.AmetysObjectIterable;
050import org.ametys.plugins.repository.AmetysObjectIterator;
051import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeaterEntry;
052
053/**
054 * Class to generate a report based on MCC.
055 */
056public abstract class AbstractMCCReport extends AbstractReport
057{
058    /** Prefix of the sessions name */
059    protected static final String SESSION_NAME_PREFIX = "mccSession";
060    /** Name of the first session */
061    protected static final String FIRST_SESSION_NAME = SESSION_NAME_PREFIX + "1";
062    /** Name of the second session */
063    protected static final String SECOND_SESSION_NAME = SESSION_NAME_PREFIX + "2";
064    
065    @Override
066    protected void _launchByOrgUnit(String uaiCode, String catalog, String lang) throws Exception
067    {
068        // Initialize data
069        MCCAmetysObjectTree tree = _processMCC(uaiCode, lang, catalog);
070        
071        // Generate the XML file corresponding to the organization with the current org unit reference
072        _writeReportsMcc(tree);
073    }
074    
075    /**
076     * Processing of the MCC
077     * @param uaiCode the UAI code of the orgunit
078     * @param lang The language code
079     * @param catalog The catalogue
080     * @return {@link MCCAmetysObjectTree}
081     */
082    protected MCCAmetysObjectTree _processMCC(String uaiCode, String lang, String catalog)
083    {
084        MCCAmetysObjectTree tree = null;
085        
086        // Filter the programs with this uai code
087        AmetysObjectIterable<OrgUnit> orgUnits = _reportHelper.getRootOrgUnitsByUaiCode(uaiCode);
088        AmetysObjectIterator<OrgUnit> orgUnitsIterator = orgUnits.iterator();
089        if (orgUnitsIterator.hasNext())
090        {
091            OrgUnit orgUnit = orgUnitsIterator.next();
092            getLogger().info("Rapport de pilotage construit sur la composante '{}' ({})", orgUnit.getTitle(), orgUnit.getUAICode());
093            List<Program> selectedPrograms = _reportHelper.filterProgramsFromOrgUnits(orgUnit, lang, catalog);
094            tree = extractMCCAmetysObjectTree(orgUnit, selectedPrograms);
095        }
096        else
097        {
098            getLogger().error("Il n'existe pas de composante ayant pour code UAI '{}'", uaiCode);
099        }
100
101        return tree;
102    }
103    
104    /**
105     * Create the MCC report for one organization unit 
106     * @param tree The MCC tree
107     */
108    protected void _writeReportsMcc(MCCAmetysObjectTree tree)
109    {
110        SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
111        
112        for (MCCAmetysObjectTree child : tree.getChildren())
113        {
114            AmetysObject ao = child.getCurrent();
115            
116            // children are supposed to be programs
117            if (ao instanceof Program)
118            {
119                Program program = (Program) ao;
120                String fileName = _getReportFileName(program);
121                
122                // Delete old files
123                File xmlFile = new File(_tmpFolder, fileName + ".xml");
124                FileUtils.deleteQuietly(xmlFile);
125                
126                try (FileOutputStream fos = new FileOutputStream(xmlFile))
127                {
128                    TransformerHandler handler = factory.newTransformerHandler();
129                    
130                    // Prepare the transformation
131                    Result result = new StreamResult(fos);
132                    handler.setResult(result);
133                    handler.startDocument();
134                    
135                    // Write XML file
136                    AttributesImpl attrs = new AttributesImpl();
137                    attrs.addCDATAAttribute("type", getType());
138                    attrs.addCDATAAttribute("date", _reportHelper.getReadableCurrentDate());
139                    XMLUtils.startElement(handler, "report", attrs);
140                    
141                    // SAX other global informations
142                    saxGlobalInformations(handler, program);
143                    
144                    // SAX natures enseignement
145                    _reportHelper.saxNaturesEnseignement(handler, getLogger());
146                    _refTableHelper.saxItems(handler, PilotageHelper.MCC_MODALITE_SESSION1);
147                    _refTableHelper.saxItems(handler, PilotageHelper.MCC_MODALITE_SESSION2);
148                    _refTableHelper.saxItems(handler, PilotageHelper.MCC_SESSION_NATURE);
149                    
150                    // SAX tree
151                    saxTree(handler, child);
152                    
153                    XMLUtils.endElement(handler, "report");
154                    handler.endDocument();
155
156                    // Convert the report to configured output format
157                    convertReport(_tmpFolder, fileName, xmlFile);
158                }
159                catch (Exception e)
160                {
161                    getLogger().error("An error occured while generating 'MCC' report for program '{}' ({})", program.getTitle(), program.getCode(), e);
162                }
163                finally
164                {
165                    FileUtils.deleteQuietly(xmlFile);
166                }
167            }
168        }
169    }
170    
171    /**
172     * SAX the MCC tree for the report
173     * @param handler The handler
174     * @param tree The MCC tree
175     * @throws SAXException if an error occurs
176     */
177    protected void saxTree(TransformerHandler handler, MCCAmetysObjectTree tree) throws SAXException
178    {
179        AmetysObject ao = tree.getCurrent();
180        
181        if (ao instanceof OrgUnit)
182        {
183            saxOrgUnit(handler, (OrgUnit) ao, tree);
184        }
185        else if (ao instanceof Program)
186        {
187            saxProgram(handler, (Program) ao, tree);
188        }
189        else if (ao instanceof SubProgram)
190        {
191            saxSubProgram(handler, (SubProgram) ao, tree);
192        }
193        else if (ao instanceof Container)
194        {
195            saxContainer(handler, (Container) ao, tree);
196        }
197        else if (ao instanceof CourseList)
198        {
199            saxCourseList(handler, (CourseList) ao, tree);
200        }
201        else if (ao instanceof Course)
202        {
203            saxCourse(handler, (Course) ao, tree);
204        }
205    }
206
207    /**
208     * Sax an {@link OrgUnit}.
209     * @param handler The transformer handler
210     * @param orgunit The orgunit to sax
211     * @param tree The MCC tree
212     * @throws SAXException If an error occurs
213     */
214    protected void saxOrgUnit(TransformerHandler handler, OrgUnit orgunit, MCCAmetysObjectTree tree) throws SAXException
215    {
216        // id + name
217        AttributesImpl attrs = new AttributesImpl();
218        attrs.addCDATAAttribute("name", orgunit.getName());
219        attrs.addCDATAAttribute("title", orgunit.getTitle());
220
221        // Start tag + children + end tag
222        XMLUtils.startElement(handler, "orgunit", attrs);
223        saxTreeChildren(handler, tree);
224        XMLUtils.endElement(handler, "orgunit");
225    }
226
227    /**
228     * Sax a {@link Program}.
229     * @param handler The transformer handler
230     * @param program The program to sax
231     * @param tree The MCC tree
232     * @throws SAXException If an error occurs
233     */
234    protected void saxProgram(TransformerHandler handler, Program program, MCCAmetysObjectTree tree) throws SAXException
235    {
236        // id + name
237        AttributesImpl attrs = new AttributesImpl();
238        attrs.addCDATAAttribute("id", program.getId());
239        attrs.addCDATAAttribute("name", program.getName());
240
241        // Start tag + title
242        XMLUtils.startElement(handler, "program", attrs);
243        XMLUtils.createElement(handler, "title", program.getTitle());
244        XMLUtils.createElement(handler, "catalog", program.getCatalog());
245        
246        // VDI
247        XMLUtils.createElement(handler, "dip", _reportHelper.getCodeDIP(program));
248        XMLUtils.createElement(handler, "vdi", _reportHelper.getCodeVRSVDI(program));
249        XMLUtils.createElement(handler, "speciality", program.getSpeciality());
250
251        // Degree
252        String degreeId = program.getDegree();
253        if (StringUtils.isNotEmpty(degreeId))
254        {
255            AttributesImpl degreeAttrs = new AttributesImpl();
256            degreeAttrs.addCDATAAttribute("degree", _refTableHelper.getItemCode(degreeId));
257            XMLUtils.startElement(handler, "degree", degreeAttrs);
258            XMLUtils.createElement(handler, "label", _refTableHelper.getItemLabel(degreeId, program.getLanguage()));
259            XMLUtils.endElement(handler, "degree");
260        }
261        
262        // Domaine
263        _reportHelper.saxContentAttribute(handler, program, "domain", "domain");
264        
265        // Mention
266        String mentionId = program.getMention();
267        if (StringUtils.isNotEmpty(mentionId))
268        {
269            XMLUtils.createElement(handler, "mention", _refTableHelper.getItemLabel(mentionId, program.getLanguage()));
270        }
271        
272        // Code anu
273        Long codeAnu = _getCodeAnu(tree);
274        if (codeAnu != null)
275        {
276            XMLUtils.createElement(handler, "codeAnu", String.valueOf(codeAnu));
277        }
278        
279        // Contrôles de connaissances (rich text)
280        try
281        {
282            program.dataToSAX(handler, "knowledgeCheck");
283        }
284        catch (IOException e)
285        {
286            getLogger().error("Unable to SAX rich-text metadata 'knowledgeCheck'.", e);
287        }
288        
289        // Children
290        saxTreeChildren(handler, tree);
291        
292        // End tag
293        XMLUtils.endElement(handler, "program");
294    }
295
296    /**
297     * Sax a {@link SubProgram}.
298     * @param handler The transformer handler
299     * @param subProgram The subprogram to sax
300     * @param tree The MCC tree
301     * @throws SAXException If an error occurs
302     */
303    protected void saxSubProgram(TransformerHandler handler, SubProgram subProgram, MCCAmetysObjectTree tree) throws SAXException
304    {
305        // id + name
306        AttributesImpl attrs = new AttributesImpl();
307        attrs.addCDATAAttribute("id", subProgram.getId());
308        attrs.addCDATAAttribute("name", subProgram.getName());
309
310        // Start tag + title
311        XMLUtils.startElement(handler, "subprogram", attrs);
312        XMLUtils.createElement(handler, "title", subProgram.getTitle());
313        
314        // COD_PAR
315        XMLUtils.createElement(handler, "codePar", subProgram.getValue("codePar", false, StringUtils.EMPTY));
316
317        // Contrôles de connaissances (rich text)
318        try
319        {
320            subProgram.dataToSAX(handler, "knowledgeCheck");
321        }
322        catch (IOException e)
323        {
324            getLogger().error("Unable to SAX rich-text metadata 'knowledgeCheck'.", e);
325        }
326        
327        // Children
328        saxTreeChildren(handler, tree);
329        
330        // End tag
331        XMLUtils.endElement(handler, "subprogram");
332    }
333
334    /**
335     * Sax a {@link Container}.
336     * @param handler The transformer handler
337     * @param container The container to sax
338     * @param tree The MCC tree
339     * @throws SAXException If an error occurs
340     */
341    protected void saxContainer(TransformerHandler handler, Container container, MCCAmetysObjectTree tree) throws SAXException
342    {
343        // id + name
344        AttributesImpl attrs = new AttributesImpl();
345        attrs.addCDATAAttribute("id", container.getId());
346        attrs.addCDATAAttribute("name", container.getName());
347        
348        // Nature
349        String natureId = container.getNature();
350        if (StringUtils.isNotEmpty(natureId))
351        {
352            attrs.addCDATAAttribute("nature", _refTableHelper.getItemCode(natureId));
353        }
354
355        // Période
356        String periodId = container.getPeriod();
357        if (StringUtils.isNotEmpty(periodId))
358        {
359            attrs.addCDATAAttribute("periodCode", _refTableHelper.getItemCode(periodId));
360            attrs.addCDATAAttribute("periodValue", _refTableHelper.getItemLabel(periodId, container.getLanguage()));
361        }
362        
363        // ECTS
364        attrs.addCDATAAttribute("ects", String.valueOf(container.getEcts()));
365        
366        // Start tag + title
367        XMLUtils.startElement(handler, "container", attrs);
368        XMLUtils.createElement(handler, "title", container.getTitle());
369        
370        // ETP + VRS_ETP
371        XMLUtils.createElement(handler, "etp", container.getValue("etpCode", false, StringUtils.EMPTY));
372        XMLUtils.createElement(handler, "vrsEtp", container.getValue("vrsEtpCode", false, StringUtils.EMPTY));
373        
374        // DIP + VDI
375        XMLUtils.createElement(handler, "dip", _reportHelper.getCodeDIP(container));
376        XMLUtils.createElement(handler, "vdi", _reportHelper.getCodeVRSVDI(container));
377        
378        // Code (cdm)
379        XMLUtils.createElement(handler, "code", container.getCode());
380        
381        // Info particulières (rich text)
382        try
383        {
384            container.dataToSAX(handler, "controles");
385        }
386        catch (IOException e)
387        {
388            getLogger().error("Unable to SAX rich-text metadata 'controles'.", e);
389        }
390        
391        // Children
392        saxTreeChildren(handler, tree);
393        
394        // End tag
395        XMLUtils.endElement(handler, "container");
396    }
397
398    /**
399     * Sax a {@link CourseList}.
400     * @param handler The transformer handler
401     * @param courseList The course list to sax
402     * @param tree The MCC tree
403     * @throws SAXException If an error occurs
404     */
405    protected void saxCourseList(TransformerHandler handler, CourseList courseList, MCCAmetysObjectTree tree) throws SAXException
406    {
407        // id + name
408        AttributesImpl attrs = new AttributesImpl();
409        attrs.addCDATAAttribute("id", courseList.getId());
410        attrs.addCDATAAttribute("name", courseList.getName());
411        
412        // type
413        ChoiceType type = courseList.getType();
414        attrs.addCDATAAttribute(type.name().toLowerCase(), "true");
415        attrs.addCDATAAttribute("minCourses", String.valueOf(courseList.getMinNumberOfCourses()));
416
417        // Start tag + title
418        XMLUtils.startElement(handler, "courselist", attrs);
419        XMLUtils.createElement(handler, "title", courseList.getTitle());
420
421        // Children
422        saxTreeChildren(handler, tree);
423        
424        // End tag
425        XMLUtils.endElement(handler, "courselist");
426    }
427
428    /**
429     * Sax a {@link Course}.
430     * @param handler The transformer handler
431     * @param course The course to sax
432     * @param tree The MCC tree
433     * @throws SAXException If an error occurs
434     */
435    protected void saxCourse(TransformerHandler handler, Course course, MCCAmetysObjectTree tree) throws SAXException
436    {
437        // id + name
438        AttributesImpl attrs = new AttributesImpl();
439        attrs.addCDATAAttribute("id", course.getId());
440        attrs.addCDATAAttribute("name", course.getName());
441        
442        // type
443        attrs.addCDATAAttribute("type", _refTableHelper.getItemCode(course.getCourseType()));
444        
445        // ECTS
446        attrs.addCDATAAttribute("ects", String.valueOf(course.getEcts()));
447        
448        // MODU weight
449        attrs.addCDATAAttribute("poidsModu", String.valueOf(course.getValue("poidsModu", false, 0D)));
450        
451        // Start tag + title
452        XMLUtils.startElement(handler, "course", attrs);
453        XMLUtils.createElement(handler, "title", course.getTitle());
454        
455        // short label
456        XMLUtils.createElement(handler, "shortLabel", course.getValue("shortLabel", false, StringUtils.EMPTY));
457        
458        // Code (cdm)
459        XMLUtils.createElement(handler, "code", course.getCode());
460        
461        // Is evaluated
462        XMLUtils.createElement(handler, "evaluated", course.getValue("isEvaluated", false, Boolean.FALSE) ? "X" : StringUtils.EMPTY);
463        
464        // Code Apogée
465        XMLUtils.createElement(handler,  "codeApogee", course.getValue("elpCode", false, StringUtils.EMPTY));
466        
467        // TODO besoin de etapePorteuse?
468        /**
469        if (course.hasValue("etapePorteuse"))
470        {
471            Content etapePorteuse = Optional.ofNullable((ContentValue) course.getValue("etapePorteuse"))
472                    .map(ContentValue::getContent)
473                    .orElse(null);
474
475            if (etapePorteuse != null && etapePorteuse.hasNonEmptyValue("etpCode"))
476            {
477                attr.addCDATAAttribute("etapePorteuse", etapePorteuse.getTitle());
478                attr.addCDATAAttribute("codeEtapePorteuse", etapePorteuse.getValue("etpCode"));
479            }
480        }
481        */
482        
483        saxCourseParts(handler, course);
484        
485        saxMCCs(handler, course, tree);
486        
487        saxTreeChildren(handler, tree);
488        
489        // End tag
490        XMLUtils.endElement(handler, "course");
491    }
492    
493    /**
494     * Iterate on tree's children.
495     * @param handler The transformer handler
496     * @param tree The MCC tree
497     * @throws SAXException If an error occurs 
498     */
499    protected void saxTreeChildren(TransformerHandler handler, MCCAmetysObjectTree tree) throws SAXException
500    {
501        for (MCCAmetysObjectTree child : tree.getChildren())
502        {
503            saxTree(handler, child);
504        }
505    }
506
507    /**
508     * Sax the {@link CoursePart}s containing in a {@link Course}.
509     * @param handler The transformer handler
510     * @param course The course with course parts to sax
511     * @throws SAXException If an error occurs
512     */
513    protected void saxCourseParts(TransformerHandler handler, Course course) throws SAXException
514    {
515        // Volume horaire
516        XMLUtils.startElement(handler, "volumeHoraire");
517        
518        // Heures d'enseignement
519        for (CoursePart coursePart : course.getCourseParts())
520        {
521            AttributesImpl attr = new AttributesImpl();
522            attr.addCDATAAttribute("nature", coursePart.getNature());
523            XMLUtils.createElement(handler, "volume", attr, String.valueOf(coursePart.getNumberOfHours()));
524        }
525        
526        XMLUtils.endElement(handler, "volumeHoraire");
527    }
528    
529    /**
530     * Sax the MCC sessions.
531     * @param handler The transformer handler
532     * @param course The concerned {@link Course} to sax the MCCs on
533     * @param tree The MCC tree
534     * @throws SAXException If an error occurs
535     */
536    protected abstract void saxMCCs(TransformerHandler handler, Course course, MCCAmetysObjectTree tree) throws SAXException;
537    
538    /**
539     * Generates SAX events for the details of a MCC session entry.
540     * @param handler The transformer handler
541     * @param sessionEntry The session repeater entry to SAX
542     * @throws SAXException If an error occurs
543     */
544    protected void saxSessionEntryDetails(TransformerHandler handler, ModelAwareRepeaterEntry sessionEntry) throws SAXException
545    {
546        // modalite
547        String modalite = _refTableHelper.getItemCode(ContentDataHelper.getContentIdFromContentData(sessionEntry, "modalite"));
548        if (StringUtils.isNotEmpty(modalite))
549        {
550            XMLUtils.createElement(handler, "modalite", modalite);
551        }
552        
553        // nature
554        String nature = _refTableHelper.getItemCode(ContentDataHelper.getContentIdFromContentData(sessionEntry, "nature"));
555        if (StringUtils.isNotEmpty(nature))
556        {
557            XMLUtils.createElement(handler, "nature", nature);
558        }
559        
560        // duree
561        long duree = sessionEntry.getValue("duree", false, 0L);
562        if (duree > 0)
563        {
564            AttributesImpl dureeAttrs = new AttributesImpl();
565            dureeAttrs.addCDATAAttribute("minutes", String.valueOf(duree));
566            XMLUtils.createElement(handler, "duree", _reportHelper.minute2hour((int) duree));
567        }
568
569        // nombre
570        String nombre = sessionEntry.getValue("nombre", false, StringUtils.EMPTY);
571        if (StringUtils.isNotEmpty(nombre))
572        {
573            XMLUtils.createElement(handler, "nombre", nombre);
574        }
575        // coefficient
576        String coeff = sessionEntry.getValue("coefficient", false, StringUtils.EMPTY);
577        if (StringUtils.isNotEmpty(coeff))
578        {
579            AttributesImpl coeffAttrs = new AttributesImpl();
580
581            if (coeff.indexOf('%') != -1)
582            {
583                coeffAttrs.addCDATAAttribute("pourcent", StringUtils.substringBefore(coeff, "%"));
584                XMLUtils.createElement(handler, "coefficient", coeffAttrs, coeff);
585            }
586            else if (coeff.indexOf('/') != -1)
587            {
588                coeffAttrs.addCDATAAttribute("num", StringUtils.substringBefore(coeff, "/"));
589                coeffAttrs.addCDATAAttribute("denom", StringUtils.substringAfter(coeff, "/"));
590                XMLUtils.createElement(handler, "coefficient", coeffAttrs, coeff);
591            }
592            else
593            {
594                XMLUtils.createElement(handler, "coefficient", coeffAttrs, coeff);
595            }
596        }
597
598        // remarque
599        String remarques = sessionEntry.getValue("remarques", false, StringUtils.EMPTY);
600        if (StringUtils.isNotEmpty(remarques))
601        {
602            XMLUtils.createElement(handler, "remarques", remarques);
603        }
604    }
605
606    /**
607     * Extract the tree of Ametys object of interested.
608     * @param ou the orgunit to extract the MCC from
609     * @param programs the list of associated programs
610     * @return {@link MCCAmetysObjectTree}
611     */
612    protected MCCAmetysObjectTree extractMCCAmetysObjectTree(OrgUnit ou, List<Program> programs)
613    {
614        MCCAmetysObjectTree tree = new MCCAmetysObjectTree(ou);
615        for (Program program : programs)
616        {
617            addProgram2MCCAmetysObjectTree(tree, program);
618        }
619        return tree;
620    }
621    
622    /**
623     * Add and populate the program to the {@link MCCAmetysObjectTree}
624     * @param tree The object tree
625     * @param program The program to add
626     */
627    protected void addProgram2MCCAmetysObjectTree(MCCAmetysObjectTree tree, Program program)
628    {
629        MCCAmetysObjectTree programTree = tree.addChild(program);
630        populateMCCAmetysObjectTree(programTree);
631    }
632    
633    /**
634     * Populate the MCC tree.
635     * @param tree The MCC tree
636     */
637    protected abstract void populateMCCAmetysObjectTree(MCCAmetysObjectTree tree);
638    
639    /**
640     * Recurse through containers and courses to find a code anu.
641     * @param tree The MCC tree
642     * @return Return the Code ANU
643     */
644    private Long _getCodeAnu(MCCAmetysObjectTree tree)
645    {
646        Long codeAnu = null;
647        
648        Iterator<MCCAmetysObjectTree> iterator = tree.getChildren().iterator();
649        while (iterator.hasNext() && codeAnu == null)
650        {
651            AmetysObject child = iterator.next().getCurrent();
652            long code = -1;
653            
654            if (child instanceof Container)
655            {
656                Container container = (Container) child;
657                code = container.getValue("CodeAnu", false, 0L);
658            }
659            else if (child instanceof Course)
660            {
661                Course course = (Course) child;
662                code = course.getValue("CodeAnu", false, 0L);
663            }
664            
665            if (code > 0)
666            {
667                codeAnu = code;
668            }
669        }
670        
671        if (codeAnu == null)
672        {
673            iterator = tree.getChildren().iterator();
674            while (iterator.hasNext() && codeAnu == null)
675            {
676                codeAnu = _getCodeAnu(iterator.next());
677            }
678        }
679        
680        return codeAnu;
681    }
682    
683    /**
684     * Get the report filename for a given program
685     * @param program The program
686     * @return the file name
687     */
688    protected String _getReportFileName(Program program)
689    {
690        StringBuilder sb = new StringBuilder();
691
692        sb.append(getType());
693        sb.append("-");
694        
695        // Catalog
696        sb.append(program.getCatalog());
697        sb.append("-");
698        
699        // Lang
700        sb.append(program.getLanguage());
701        sb.append("-");
702        
703        // Domain
704        String[] domains = program.getDomain();
705        for (String domainId : domains)
706        {
707            String domain = _refTableHelper.getItemCode(domainId);
708            if (StringUtils.isNotEmpty(domain))
709            {
710                sb.append(domain);
711                sb.append('-');
712            }
713        }
714
715        // Mention or title
716        String mentionId = program.getMention();
717        if (StringUtils.isBlank(mentionId))
718        {
719            sb.append(program.getTitle());
720        }
721        else
722        {
723            // Degree + mention
724            sb.append(_refTableHelper.getItemCode(program.getDegree()));
725            sb.append("-");
726            sb.append(_refTableHelper.getItemLabel(mentionId, program.getLanguage()));
727        }
728        
729        // CDM code
730        sb.append("-");
731        sb.append(program.getCode());
732        
733        // Date
734        sb.append("-");
735        sb.append(_currentFormattedDate);
736
737        return FilterNameHelper.filterName(sb.toString());
738    }
739    
740    /**
741     * Sax the additional global informations of the report.
742     * @param handler The transformer handler
743     * @param program The program on which the report is launched
744     * @throws SAXException If an error occurs
745     */
746    protected abstract void saxGlobalInformations(TransformerHandler handler, Program program) throws SAXException;
747}