-package org.lilie.services.eliot.notes.brevet\r
-\r
-import org.lilie.services.eliot.brevet.BrevetNote\r
-import org.hibernate.FetchMode\r
-import org.lilie.services.eliot.scolarite.Personne\r
-import org.lilie.services.eliot.brevet.BrevetEpreuve\r
-\r
-import org.lilie.services.eliot.scolarite.Etablissement\r
-import org.lilie.services.eliot.scolarite.StructureEnseignement\r
-import org.hibernate.SessionFactory\r
-import org.lilie.services.eliot.scolarite.structureenseignement.LocalStructureEnseignementService\r
-import org.lilie.services.eliot.brevet.BrevetRelEpreuveMatiere\r
-import org.lilie.services.eliot.scolarite.Matiere\r
-import org.lilie.services.eliot.notes.scolarite.NotesServiceService\r
-import org.lilie.services.eliot.scolarite.Periode\r
-import org.lilie.services.eliot.notes.scolarite.NotesPeriodeService\r
-import org.lilie.services.eliot.scolarite.IntervalleEnum\r
-import org.lilie.services.eliot.scolarite.Service\r
-import org.lilie.services.eliot.notes.resultat.eleve.ResultatEleveServicePeriodeService\r
-import org.lilie.services.eliot.notes.ResultatEleveServicePeriode\r
-import org.lilie.services.eliot.notes.resultat.eleve.AppreciationEleveEnseignementPeriodeService\r
-import org.lilie.services.eliot.notes.resultat.NoteInfo\r
-import org.lilie.services.eliot.notes.resultat.MoyenneService\r
-import org.lilie.services.eliot.brevet.BrevetSerie\r
-import org.lilie.services.eliot.notes.scolarite.NotesStructureEnseignementService\r
-import org.lilie.services.eliot.notes.resultat.CalculationService\r
-import org.lilie.services.eliot.scolarite.anneescolaire.AnneeScolaireService\r
-import org.lilie.services.eliot.brevet.BrevetFiche\r
-import org.lilie.services.eliot.brevet.BrevetModeCalculNote\r
-import org.lilie.services.eliot.notes.Note\r
-import org.lilie.services.eliot.scolarite.NaturePeriode\r
-import org.lilie.services.eliot.constantes.ConstFonct\r
-\r
-/**\r
- * Gestion des notes de brevet\r
- * @author msan\r
- * @author bper\r
- */\r
-class BrevetNoteService {\r
-\r
- LocalStructureEnseignementService localStructureEnseignementService\r
- NotesServiceService notesServiceService\r
- NotesPeriodeService notesPeriodeService\r
- ResultatEleveServicePeriodeService resultatEleveServicePeriodeService\r
- AppreciationEleveEnseignementPeriodeService appreciationEleveEnseignementPeriodeService\r
- MoyenneService moyenneService\r
- BrevetRelEpreuveMatiereService brevetRelEpreuveMatiereService\r
- BrevetSerieService brevetSerieService\r
- NotesStructureEnseignementService notesStructureEnseignementService\r
- CalculationService calculationService\r
-\r
- AnneeScolaireService anneeScolaireService\r
-\r
- SessionFactory sessionFactory\r
-\r
- static transactional = true\r
-\r
-\r
- /**\r
- * Initialise les notes des brevets\r
- * @author msan\r
- */\r
- Boolean initialiseNotesBrevet(List<StructureEnseignement> classes) {\r
-\r
- Boolean initialisationOk = true\r
-\r
- // recupere notes\r
- List<BrevetNoteInfo> notesInitialises = actualiseNotes(classes, true)\r
-\r
- // enregsitre les changements\r
- notesInitialises.each { BrevetNoteInfo noteInfo ->\r
- BrevetNote brevetNote = BrevetNote.get(noteInfo.noteId)\r
- if (noteInfo.appreciation != null || noteInfo.valeurNumerique != null || noteInfo.noteValeur) {\r
-\r
- if (noteInfo.appreciation != null) {\r
- brevetNote.appreciation = noteInfo.appreciation\r
- }\r
-\r
- if (noteInfo.valeurNumerique != null) {\r
- brevetNote.valeurNumerique = noteInfo.valeurNumerique\r
- }\r
- else if (noteInfo.noteValeur != null) {\r
- brevetNote.setValeurTextuelle(noteInfo.noteValeur)\r
- }\r
-\r
- brevetNote.save(failOnError: true)\r
- }\r
- }\r
- sessionFactory.currentSession.flush()\r
-\r
- return initialisationOk\r
- }\r
-\r
- /**\r
- * Recupere BrevetNotes avec les valeurs actualisees avec les resultats reels\r
- * d'eleves.\r
- * La liste ne contiendra tout les eleves car il n'est pas necessaire\r
- * que tout les eleves d'une classe suivent la meme serie.\r
- * @author msan\r
- */\r
- List<BrevetNoteInfo> actualiseNotes(StructureEnseignement classe,\r
- List<Personne> eleves) {\r
-\r
- Map eleveMap = [:]\r
- eleves.each {\r
- eleveMap.put(it, classe)\r
- }\r
-\r
- List<BrevetNoteInfo> noteInfos = actualiseNotes([classe], eleveMap)\r
-\r
- majAppreciations(noteInfos)\r
-\r
- return noteInfos\r
- }\r
-\r
- /**\r
- * Met à jour les appréciations des notes de Brevet à partir des appréciations\r
- * annuelles récupérées lors de l'actualisation des notes.\r
- * @author bper\r
- */\r
- private void majAppreciations(List<BrevetNoteInfo> noteInfos) {\r
- noteInfos.each {\r
- BrevetNote note = BrevetNote.get(it.noteId)\r
- if (note && note.appreciation != it.appreciation) {\r
- note.appreciation = it.appreciation\r
- note.save(failOnError: true, flush: true)\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Toutes les BrevetNotes pour les eleves avec les associantions fetches.\r
- * @author msan\r
- */\r
- List<BrevetNote> findAllNotes(List<Personne> eleves) {\r
- return BrevetNote.createCriteria().listDistinct {\r
- fiche {\r
- 'in'('eleve', eleves)\r
- eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())\r
- }\r
- fetchMode('valeurTextuelle', FetchMode.JOIN) // AB,DI,NV ..\r
- fetchMode('epreuve', FetchMode.JOIN)\r
- fetchMode('epreuve.serie', FetchMode.JOIN)\r
- fetchMode('epreuve.valeurTextuelleAutorisees', FetchMode.JOIN)\r
- fetchMode('fiche', FetchMode.JOIN)\r
- fetchMode('fiche.eleve', FetchMode.JOIN)\r
- }\r
- }\r
-\r
- /**\r
- * Recupere BrevetNotes avec les valeurs actualisees avec les resultats reels\r
- * d'eleves. A utiliser pour l'actualisation ou l'initialisation.\r
- * @param eleveMap une map Eleve -> Sa classe\r
- * @param initialisation true s'il s'agit d'initiaisation\r
- * @author msan\r
- */\r
- List<BrevetNoteInfo> actualiseNotes(List<StructureEnseignement> classes,\r
- Map<Personne, StructureEnseignement> eleveMap,\r
- Boolean initialisation = false) {\r
-\r
- List<BrevetNoteInfo> notesActualisess = []\r
-\r
- List<Personne> eleves = eleveMap.keySet().toList()\r
- Etablissement etablissement = classes.first().etablissement\r
-\r
- // get BrevetNotes\r
- List<BrevetNote> notes = findAllNotes(eleves)\r
- List<BrevetEpreuve> epreuves = notes.collect { it.epreuve }.unique { it.id }\r
- epreuves = epreuves.findAll {it.notee}\r
-\r
- // get relations Epreuve - Metieres fixes\r
- List<BrevetRelEpreuveMatiere> relFixes = brevetRelEpreuveMatiereService.findAllRel(epreuves)\r
-\r
- Map<StructureEnseignement, Periode> periodesAnnee =\r
- findAllPeriodeAnneeByClasses(classes)\r
-\r
- notes.groupBy{it.fiche.eleve}.each { Personne eleve, List<BrevetNote> notesEleve ->\r
- List<BrevetEpreuve> eleveEpreuves = notesEleve.collect { it.epreuve }\r
- eleveEpreuves = eleveEpreuves.findAll {it.notee == true}\r
-\r
- StructureEnseignement classe = eleveMap.get(eleve)\r
- Periode periodeAnnee = periodesAnnee.get(classe)\r
-\r
- if (initialisation) {\r
- notesEleve = incrisEleveAuxEpreuvesOptionnellesEtPersonnanlisables(notesEleve)\r
- }\r
-\r
- List<EpreuveInfo> epreuveInfos = findAllCorrespondanceEpreuveMatiere(\r
- eleveEpreuves,\r
- eleve,\r
- etablissement,\r
- relFixes,\r
- notesEleve,\r
- periodeAnnee,\r
- initialisation\r
- )\r
-\r
- if (initialisation) {\r
- notesEleve = majNotesAuxEpreuvesOptionnellesEtPersonnanlisables(notesEleve)\r
- }\r
-\r
- // on peut avoir plusieurs services pour une epreuve dans le cas de partage des matieres\r
- ajouteInfoServices(epreuveInfos, eleve, periodeAnnee)\r
-\r
- ajouteInfoMoyennes(epreuveInfos, eleve, periodeAnnee)\r
-\r
- ajouteInfoAppreciations(epreuveInfos, eleve, periodeAnnee)\r
-\r
- // actualise les notes de brevet avec les resultats reels\r
- notesEleve.each { BrevetNote brevetNote ->\r
- EpreuveInfo epreuveInfo = epreuveInfos.find {\r
- it.epreuve.id == brevetNote.epreuveId }\r
-\r
- notesActualisess += actualiseNote(\r
- epreuveInfo.moyenne,\r
- epreuveInfo.appreciation,\r
- brevetNote\r
- )\r
- }\r
- }\r
-\r
- return notesActualisess\r
- }\r
-\r
- /**\r
- * Crée des notes de brevet pour les épreuves à la fois optionnelles et personnalisables sans les enregistrer.\r
- * Ces notes seront enregistrées si on trouve une matière correspondante pour cet élève.\r
- */\r
- private List<BrevetNote> incrisEleveAuxEpreuvesOptionnellesEtPersonnanlisables(List<BrevetNote> notesEleve) {\r
-\r
- BrevetSerie serie = notesEleve.first().epreuve.serie\r
- BrevetFiche fiche = notesEleve.first().fiche\r
-\r
- List<BrevetEpreuve> epreuvesOptEtPers =\r
- serie.epreuves.findAll {it.personnalisable && it.optionnelle}.toList()\r
-\r
- epreuvesOptEtPers.each {BrevetEpreuve epreuveOptEtPers ->\r
- if (!notesEleve.any {it.epreuveId == epreuveOptEtPers.id}) {\r
- notesEleve << new BrevetNote(fiche: fiche, epreuve: epreuveOptEtPers)\r
- }\r
- }\r
-\r
- return notesEleve\r
- }\r
-\r
- /**\r
- * Enregistre les notes de brevet pour les épreuves à la fois optionnelles et\r
- * personnalisables si la matière attaché n'est pas null.\r
- */\r
- private List<BrevetNote> majNotesAuxEpreuvesOptionnellesEtPersonnanlisables(List<BrevetNote> notesEleve) {\r
- List<BrevetNote> notesEleveAEnlever = []\r
-\r
- notesEleve.each {\r
- if (it.epreuve.personnalisable && it.epreuve.optionnelle) {\r
- if (it.matiereId == null) {\r
- notesEleveAEnlever << it\r
- } else {\r
- it.save(failOnError: true)\r
- }\r
- }\r
- }\r
-\r
- notesEleve.removeAll(notesEleveAEnlever)\r
- notesEleveAEnlever*.delete()\r
-\r
- return notesEleve\r
- }\r
-\r
- /**\r
- * Ajoute les appreciations dans epreuveInfos\r
- * @author msan\r
- */\r
- void ajouteInfoAppreciations(List<EpreuveInfo> epreuveInfos,\r
- Personne eleve,\r
- Periode periode) {\r
-\r
- List<Service> services = (List) epreuveInfos.collect { it.services }.flatten().\r
- findAll { it != null }.unique { it.id }\r
-\r
- Map<Service, List<String>> apps =\r
- appreciationEleveEnseignementPeriodeService.findAll(eleve, periode, services)\r
-\r
- epreuveInfos.each { EpreuveInfo epreuveInfo ->\r
- if (epreuveInfo.services?.size() > 0) {\r
- List<String> appsEpreuve = (List) epreuveInfo.services.collect { Service service ->\r
- List<String> serviceApps = apps.get(service)\r
- if (serviceApps?.size() > 0) {\r
- return serviceApps\r
- } else {\r
- return []\r
- }\r
- }.flatten().findAll { it != null }\r
- if (appsEpreuve?.size() > 0) {\r
- // scotche les appreciations\r
- epreuveInfo.appreciation = appsEpreuve.join(' ')\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Ajoute les moyennes dans epreuveInfos.\r
- * Re-initialise les listes des matières et des services en fonction des moyennes utilisées.\r
- * On ne garde que les matières et les services pour lesquels les moyennes ont été trouvées.\r
- * @author msan\r
- * @author bper\r
- */\r
- void ajouteInfoMoyennes(List<EpreuveInfo> epreuveInfos,\r
- Personne eleve,\r
- Periode periode) {\r
-\r
- List<Service> services = (List) epreuveInfos.collect { it.services }.flatten().\r
- findAll { it != null }.unique { it.id }\r
-\r
- Map<Long, Integer> mapMatiereIdOrdre = getMapMatiereIdOrdre(services, periode)\r
-\r
- List<ResultatEleveServicePeriode> resultats = resultatEleveServicePeriodeService.\r
- findAll(\r
- [eleve.autorite],\r
- services,\r
- [periode],\r
- [NaturePeriode.NOTATION]\r
- )\r
-\r
- resultats = (List) resultats.findAll {it.moyenne != null}\r
- List<Service> serviceAvecMoyennes = resultats*.service\r
-\r
- if (resultats.size() == 0) {\r
- epreuveInfos*.matieres = []\r
- epreuveInfos*.services = []\r
- return\r
- }\r
-\r
- Map resultatsParMatiere = resultats.groupBy {it.service.matiere}\r
-\r
- Map moyenneParMatiere = [:]\r
-\r
- // Calcule les moyennes des matières. Si une matières correspond à plusieurs services,\r
- // on calcule la moyenne des moyennes des services pondérée par les coeffs\r
- // des services pour la période donnée .\r
- resultatsParMatiere.each {Matiere matiere, List<ResultatEleveServicePeriode> ress ->\r
- BigDecimal moyenne = ress.size() == 1 ?\r
- ress.first().moyenne : calculeMoyenneResultats(ress, periode)\r
-\r
- moyenneParMatiere.put(matiere, moyenne)\r
- }\r
-\r
- epreuveInfos.each {EpreuveInfo epreuveInfo ->\r
- Map moyenneParMatierePourEpreuve =\r
- (Map) moyenneParMatiere.findAll {Matiere matiere, BigDecimal moyenne ->\r
- epreuveInfo.matieres*.id.contains(matiere.id)\r
- }\r
-\r
- if (moyenneParMatierePourEpreuve.size() == 0) {\r
- epreuveInfo.matieres = []\r
- epreuveInfo.services = []\r
- return\r
- }\r
-\r
- switch (epreuveInfo.epreuve.modeCalculNote) {\r
- case BrevetModeCalculNote.PREMIERE_TROUVEE :\r
- List<Matiere> matieres = moyenneParMatierePourEpreuve.keySet().toList()\r
- matieres.sort{m1,m2 -> mapMatiereIdOrdre.get(m1.id) <=> mapMatiereIdOrdre.get(m2.id)}\r
- Matiere matiere = (Matiere) matieres.first()\r
- epreuveInfo.moyenne = (BigDecimal) moyenneParMatierePourEpreuve.get(matiere)\r
- epreuveInfo.matieres = [matiere]\r
- break\r
-\r
- case BrevetModeCalculNote.MOYENNE :\r
- List<BigDecimal> moyennes = (List<BigDecimal>) moyenneParMatierePourEpreuve.values().toList()\r
- epreuveInfo.moyenne = calculationService.calculeMoyenne(moyennes, true)?.valeurNumerique\r
- epreuveInfo.matieres = moyenneParMatierePourEpreuve.keySet().toList()\r
- break\r
-\r
- case BrevetModeCalculNote.MOYENNE_SPORT :\r
- Service serviceSport = findServiceSport(periode.classe)\r
- epreuveInfo.moyenne = calculeMoyenneSport(serviceSport, eleve)\r
- epreuveInfo.matieres = serviceSport ? [serviceSport.matiere] : []\r
- break\r
-\r
- default:\r
- throw new IllegalStateException(\r
- "Le mode de calcul de note ${epreuveInfo.epreuve.modeCalculNote} " +\r
- "pour l'épreuve ${epreuveInfo.epreuve} n'est pris en compte")\r
- }\r
-\r
- epreuveInfo.services = (List) serviceAvecMoyennes.findAll {\r
- epreuveInfo.matieres.contains(it.matiere)\r
- }\r
- }\r
- }\r
-\r
- private Map<Long, Integer> getMapMatiereIdOrdre(List<Service> services, Periode periode) {\r
- services = notesServiceService.trieServices(services, periode)\r
-\r
- Map<Service, Integer> mapServiceOrdre = [:]\r
-\r
- services.eachWithIndex {Service service, Integer ordre ->\r
- mapServiceOrdre.put(service, ordre)\r
- }\r
-\r
- Map<Long, Integer> mapMatiereIdOrdre = [:]\r
-\r
- services.groupBy{it.matiere}.each{Matiere matiere, List<Service> servicesMatiere ->\r
- List<Integer> ordres = servicesMatiere.collect{mapServiceOrdre.get(it)}\r
- mapMatiereIdOrdre.put(matiere.id, ordres.min())\r
- }\r
-\r
- return mapMatiereIdOrdre\r
- }\r
-\r
- private findServiceSport(StructureEnseignement classe) {\r
- return Service.findByStructureEnseignementAndChoisiPourSport(classe, true)\r
- }\r
-\r
- private BigDecimal calculeMoyenneSport(Service serviceSport, Personne eleve) {\r
-\r
- if (!serviceSport) {\r
- return null\r
- }\r
-\r
- List<Note> notes = Note.createCriteria().listDistinct {\r
- eq('eleve', eleve.autorite)\r
- evaluation {\r
- enseignement {\r
- eq(ConstFonct.SERVICE, serviceSport)\r
- }\r
- periodes {\r
- typePeriode {\r
- eq('nature', NaturePeriode.NOTATION)\r
- }\r
- }\r
- }\r
-\r
- eq('choisiePourSport', true)\r
- }\r
-\r
- if (notes.size() < 3) {\r
- return null\r
- }\r
-\r
- if (notes.size() > 3) {\r
- throw new IllegalStateException(\r
- "L'élève $eleve a plus de 3 notes sélectionnées sport dans le service $serviceSport")\r
- }\r
-\r
- return calculationService.calculeMoyenne(notes*.valeurSur20, true)?.valeurNumerique\r
- }\r
-\r
- /**\r
- * Moyenne ponderee des resultats\r
- * @author msan\r
- */\r
- BigDecimal calculeMoyenneResultats(List<ResultatEleveServicePeriode> resultats,\r
- Periode periode) {\r
- // calcule la moyenne ponderee\r
- List<NoteInfo> noteInfos = resultats.collect {\r
- new NoteInfo(\r
- it.getObjetMoyenne(),\r
- it.service.getCoeffPourPeriode(periode),\r
- false)\r
- }\r
- return calculationService.calculeMoyennePonderee(noteInfos)?.valeurNumerique\r
- }\r
-\r
- /**\r
- * Ajoute la correspondence entre les epreuves et les services dans epreuve info\r
- * @author msan\r
- * @author bper\r
- */\r
- void ajouteInfoServices(List<EpreuveInfo> epreuveInfos,\r
- Personne eleve,\r
- Periode periode) {\r
-\r
- List<Matiere> matieres = epreuveInfos*.matieres.flatten().toList()\r
-\r
- // get services\r
- List<Service> services = notesServiceService.\r
- findAllServiceByElevePeriodeMatieres(\r
- eleve,\r
- periode,\r
- matieres\r
- )\r
-\r
- // Affecte les services en fonction des matières affectées\r
- services.each {Service service ->\r
- epreuveInfos.each {EpreuveInfo epreuveInfo ->\r
- if (epreuveInfo.matieres*.id.contains(service.matiereId)) {\r
- epreuveInfo.services.add(service)\r
- }\r
- }\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Actualise note si necessaire et met la dans la liste noteActualisees.\r
- * S'il existe déjà une valeur textuelle saisie pour une note de brevet\r
- * la valeur numérique de cette note n'est pas mise à jour.\r
- * @author msan\r
- */\r
- private List<BrevetNoteInfo> actualiseNote(BigDecimal note,\r
- String appreciation,\r
- BrevetNote brevetNote) {\r
-\r
- List<BrevetNoteInfo> notesActualises = []\r
-\r
- // si la moyenne ou l'appr existe, ajoute la note\r
- // dans la liste des resultats envoyes\r
- if (note != null || appreciation != null ||\r
- (brevetNote.epreuve.isSport() && brevetNote.valeur == null)) {\r
-\r
- BrevetNoteInfo brevetNoteInfo = new BrevetNoteInfo(\r
- noteId: brevetNote.id,\r
- eleveId: brevetNote.fiche.eleveId,\r
- epreuveId: brevetNote.epreuveId,\r
- isSport: brevetNote.epreuve.isSport()\r
- )\r
-\r
- if (brevetNote.valeurTextuelle == null && note != null) {\r
- brevetNoteInfo.valeurNumerique = note\r
- brevetNoteInfo.noteValeur = BrevetNote.NOTE_FORMATEUR.format(note)\r
- }\r
-\r
- // Cas particulier de l'épreuve de Sport (EPS) :\r
- // La note textuelle "DI" pour cette épreuve peut etre remplacée par\r
- // la note numérique. Si la note numérique est null on lui affecte\r
- // la note textuelle "DI" comme valeur par défaut.\r
- if (brevetNote.epreuve.isSport()) {\r
- if (brevetNote.valeurTextuelle == null ||\r
- brevetNote.valeurTextuelle.valeur == NoteTextuellesEnum.DI.toString()) {\r
-\r
- if (note == null) {\r
- brevetNoteInfo.valeurNumerique = null\r
- brevetNoteInfo.noteValeur = NoteTextuellesEnum.DI\r
- }\r
- else {\r
- brevetNoteInfo.valeurNumerique = note\r
- brevetNoteInfo.noteValeur = BrevetNote.NOTE_FORMATEUR.format(note)\r
- }\r
- }\r
- }\r
-\r
- if (appreciation != null) {\r
- brevetNoteInfo.appreciation = coupeAppreciation(appreciation)\r
- }\r
-\r
- notesActualises.add(brevetNoteInfo)\r
- }\r
-\r
- return notesActualises\r
- }\r
-\r
- /**\r
- * Coupe une appreciation a la taille de 255 characteres\r
- * @author msan\r
- */\r
- String coupeAppreciation(String appreciation) {\r
- return appreciation.length()>255 ? appreciation.substring(0, 255) : appreciation\r
- }\r
-\r
- /**\r
- * Une map des periodes annees, indexee par les classes\r
- * @author msan\r
- */\r
- Map<StructureEnseignement, Periode> findAllPeriodeAnneeByClasses(List<StructureEnseignement> classes) {\r
- Map classesPeriodes = [:]\r
- List<Periode> periodesAnnees = notesPeriodeService.\r
- findAllPeriodeByClasses(\r
- classes,\r
- IntervalleEnum.ANNEE\r
- )\r
- periodesAnnees.each { Periode periodeAnnee ->\r
- classesPeriodes.put(periodeAnnee.classe, periodeAnnee)\r
- }\r
- return classesPeriodes\r
- }\r
-\r
- /**\r
- * Cherche les matieres correspondant a une epreuve pour un eleve donne\r
- * @author msan\r
- * @author bper\r
- */\r
- List<EpreuveInfo> findAllCorrespondanceEpreuveMatiere(List<BrevetEpreuve> epreuves,\r
- Personne eleve,\r
- Etablissement etablissement,\r
- List<BrevetRelEpreuveMatiere> relFixes,\r
- List<BrevetNote> notes,\r
- Periode periodeAnnee,\r
- Boolean initialisation) {\r
- List<EpreuveInfo> epreuveInfos = []\r
- notes.each { BrevetNote note ->\r
- List<Matiere> matieres = findCorrespondanceEpreuveMatiere(\r
- note.epreuve,\r
- eleve,\r
- etablissement,\r
- relFixes,\r
- note,\r
- periodeAnnee,\r
- initialisation\r
- )\r
- epreuveInfos.add(new EpreuveInfo(\r
- epreuve: note.epreuve,\r
- matieres: matieres\r
- ))\r
- }\r
- return epreuveInfos\r
- }\r
-\r
- /**\r
- * Cherche le matiere correspondant a une epreuve pour un eleve donne\r
- * @author msan\r
- * @author bper\r
- */\r
- private List<Matiere> findCorrespondanceEpreuveMatiere(BrevetEpreuve epreuve,\r
- Personne eleve,\r
- Etablissement etablissement,\r
- List<BrevetRelEpreuveMatiere> relFixes,\r
- BrevetNote note,\r
- Periode periodeAnnee,\r
- Boolean initialisation) {\r
- List<Matiere> matieres = null\r
-\r
- if (epreuve.personnalisable) {\r
- // epreuve personalisable - impactee par les options d'eleve\r
-\r
- if (isMatiereAinitialiser(initialisation, note.matiere)) {\r
- initialiseMatiere(\r
- eleve,\r
- periodeAnnee,\r
- relFixes,\r
- note)\r
- }\r
- matieres = [note.matiere]\r
- } else {\r
- // epreuve avec les matieres fixes\r
- matieres = relFixes.findAll {\r
- it.epreuveId == epreuve.id &&\r
- it.matiere.etablissementId == etablissement.id\r
- }?.collect { it.matiere }\r
- }\r
-\r
- // traitement HiGeo - EduCivique.\r
- // si EduCivique n'est pas liee au matiere, on prend le rsultat de HiGeo\r
- if (epreuve.epreuveMatieresAHeriter != null && (matieres == null || matieres.size()==0)) {\r
- matieres = relFixes.findAll {\r
- it.epreuve.id == epreuve.epreuveMatieresAHeriterId &&\r
- it.matiere.etablissementId == etablissement.id\r
- }?.collect { it.matiere }\r
- }\r
-\r
- return matieres\r
- }\r
-\r
- /**\r
- * Initialise la matiere de BrevetNote par la premiere matiere ou on trouve\r
- * une note pour l'eleve donne\r
- * @author msan\r
- */\r
- void initialiseMatiere(Personne eleve,\r
- Periode periodeAnnee,\r
- List<BrevetRelEpreuveMatiere> relFixes,\r
- BrevetNote note) {\r
- // Spec: Si une épreuve de type « LV1 ou LV2 ou Opt. facultative » (== personnalisable)\r
- // est liée à plusieurs matières, le module Notes affecte à l'élève la\r
- // première matière trouvée où l'élève a une note.\r
- note.matiere = findPremiereMatiereAvecNote(\r
- eleve,\r
- periodeAnnee,\r
- relFixes.findAll {\r
- it.epreuveId == note.epreuveId\r
- }?.collect { it.matiere }\r
- )\r
- if (log.isDebugEnabled()) {\r
- log.debug("BrevetNote ${note.id} initialisee avec matiere ${note.matiere}")\r
- }\r
- }\r
-\r
- /**\r
- * S'il faut initialiser la matiere\r
- * @author msan\r
- */\r
- Boolean isMatiereAinitialiser(boolean initialisation, Matiere matiere) {\r
- return (initialisation && matiere == null)\r
- }\r
-\r
- /**\r
- * Cherche une matiere qui est note pour l'eleve donne\r
- * @author msan\r
- */\r
- Matiere findPremiereMatiereAvecNote(Personne eleve,\r
- Periode periodeAnnee,\r
- List<Matiere> matieres) {\r
-\r
- // si note.matiere n'est pas saisie il faut chercher les resultats d'eleve\r
- // pour les matieres fixes de cette epreuve et prendre la matiere ou l'eleve\r
- // a une note + positionner la matiere de note\r
-\r
- List<Service> services = notesServiceService.findAllServiceByElevePeriodeMatieres(\r
- eleve,\r
- periodeAnnee,\r
- matieres)\r
-\r
- List<ResultatEleveServicePeriode> resultats = resultatEleveServicePeriodeService.findAll(\r
- [eleve.autorite],\r
- services,\r
- [periodeAnnee],\r
- [NaturePeriode.NOTATION]\r
- )\r
-\r
- resultats = (List) resultats.findAll {it.moyenne != null}\r
-\r
- if (resultats?.size()>0) {\r
- return resultats.first().service.matiere\r
- } else {\r
- return null\r
- }\r
- }\r
-\r
- /**\r
- * Recupere BrevetNotes avec les valeurs actualisees avec les resultats reels\r
- * d'eleves.\r
- * @author msan\r
- */\r
- List<BrevetNoteInfo> actualiseNotes(List<StructureEnseignement> classes,\r
- Boolean initialisation = false) {\r
-\r
- if (classes == null || classes.size() == 0) {\r
- return []\r
- }\r
-\r
- // prepare la liste des eleves\r
- Map<Personne, StructureEnseignement> eleveMap = [:]\r
- List elevesClasses = localStructureEnseignementService.\r
- findAllPersonneEleveAndClasseForAllClasses(classes)\r
-\r
- elevesClasses.each { def eleveClasse ->\r
- Personne eleve = eleveClasse[0].personne\r
- StructureEnseignement classe = eleveClasse[1]\r
- eleveMap.put(eleve, classe)\r
- }\r
-\r
- return actualiseNotes(classes, eleveMap, initialisation)\r
-\r
- }\r
-\r
- /**\r
- * Retourne le mapping personnalisée Epreuve -> Matiere.\r
- * @author bper\r
- */\r
- Map<Personne,Map<BrevetEpreuve,Matiere>> findAllMapEleveEpreuveMatierePourEleves(List<Personne> eleves) {\r
-\r
- List<BrevetNote> notes = BrevetNote.withCriteria {\r
- fiche {\r
- 'in'('eleve',eleves)\r
- eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())\r
- }\r
- isNotNull(ConstFonct.MATIERE)\r
- epreuve {\r
- eq('personnalisable', true)\r
- }\r
- }\r
-\r
- Map<Personne,Map<BrevetEpreuve,Matiere>> map = [:]\r
-\r
- eleves.each {Personne eleve ->\r
- Map mapEpreuveMatiere = [:]\r
- notes.each {\r
- if (it.fiche.eleveId == eleve.id) {\r
- mapEpreuveMatiere.put(it.epreuve, it.matiere)\r
- }\r
- }\r
- map.put(eleve,mapEpreuveMatiere)\r
- }\r
-\r
- return map\r
- }\r
-\r
-\r
- /**\r
- * Met à jour la les choix de matières pour les épreuves personnalisables\r
- * pour un élève donné.\r
- * @author bper\r
- */\r
- void majChoixMateiresPourEleve(Personne eleve,\r
- Map<BrevetEpreuve,Matiere> mapEpreuveMatiere) {\r
-\r
- List<BrevetNote> notes = BrevetNote.withCriteria {\r
- fiche {\r
- eq('eleve',eleve)\r
- eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())\r
- }\r
- epreuve {\r
- eq('personnalisable', true)\r
- }\r
- }\r
-\r
- mapEpreuveMatiere.each {BrevetEpreuve epreuve, Matiere matiere ->\r
- if (!epreuve.personnalisable) {\r
- throw new IllegalArgumentException("L'épreuve $epreuve n'est pas pesronnalisable")\r
- }\r
-\r
- BrevetNote note = notes.find {it.epreuveId == epreuve.id}\r
-\r
- if (note) {\r
- note.matiere = matiere\r
- note.save(failOnError: true)\r
- } else if (matiere) {\r
- throw new IllegalArgumentException(\r
- "Impossible d'assoceier la matière $matiere à l'épreuve $epreuve pour " +\r
- "l L'élève $eleve. Il n'est pas inscrit pour cette épreuve."\r
- )\r
- }\r
- }\r
-\r
- sessionFactory.currentSession.flush()\r
- }\r
-\r
-\r
- /**\r
- * Inscrit l'élève pour les épreuves qui ont des épreuves exclusives.\r
- * @author bper\r
- */\r
- void majInscriptionEpreuveExclusives(Personne eleve,\r
- List<BrevetEpreuve> epreuves) {\r
-\r
- if (!epreuves) {\r
- return\r
- }\r
-\r
- BrevetSerie serie = brevetSerieService.findSeriePourEleve(eleve)\r
-\r
- if (!serie.id) {\r
- throw new IllegalArgumentException("L'élève $eleve n'est pas inscrit au Brevet")\r
- }\r
-\r
- BrevetFiche fiche = BrevetFiche.findByEleveAndAnneeScolaire(\r
- eleve,\r
- anneeScolaireService.anneeScolaireEnCours()\r
- )\r
-\r
- // Suppime les notes aux épreuves exclusives\r
- List<BrevetNote> noteToDeletes = BrevetNote.withCriteria {\r
- eq('fiche',fiche)\r
- 'in'('epreuve',epreuves*.epreuveExclusive)\r
- }\r
-\r
- noteToDeletes.each {\r
- fiche.removeFromNotes(it)\r
- it.delete()\r
- }\r
- sessionFactory.currentSession.flush()\r
-\r
- List<Long> epreuveDejaInscriteIds = (List) BrevetNote.withCriteria {\r
- eq('fiche',fiche)\r
- 'in'('epreuve',epreuves)\r
- epreuve {\r
- projections {\r
- property('id')\r
- }\r
- }\r
- }\r
-\r
- epreuves.each {BrevetEpreuve epreuve ->\r
- if (epreuve.serieId != serie.id) {\r
- throw new IllegalArgumentException(\r
- "La série $serie de l'élève $eleve ne correspond pas à celle de l'épreuve $epreuve"\r
- )\r
- }\r
-\r
- if (!epreuve.epreuveExclusive) {\r
- throw new IllegalArgumentException("L'épreuve $eleve n'a d'épreuve exclusive")\r
- }\r
-\r
- if (!epreuveDejaInscriteIds.any {it == epreuve.id}) {\r
- BrevetNote note = new BrevetNote(fiche: fiche, epreuve: epreuve)\r
- fiche.addToNotes(note)\r
- }\r
- }\r
- fiche.save(failOnError: true, flush: true)\r
- }\r
-\r
-\r
- /**\r
- * Inscrit l'élève aux épreuves optionnelles passées en paramétres et\r
- * le desinscrit des épreuves optionnelles qui ne sont pas dans la liste.\r
- * @author bper\r
- */\r
- void majInscriptionEpreuveOptionnelles(Personne eleve,\r
- List<BrevetEpreuve> epreuves) {\r
-\r
- BrevetSerie serie = brevetSerieService.findSeriePourEleve(eleve)\r
-\r
- if (!serie.id) {\r
- throw new IllegalArgumentException("L'élève $eleve n'est pas inscrit au Brevet")\r
- }\r
-\r
- BrevetFiche fiche = BrevetFiche.findByEleveAndAnneeScolaire(\r
- eleve,\r
- anneeScolaireService.anneeScolaireEnCours()\r
- )\r
-\r
- // Suppime les notes aux épreuves optionnelles qui ne sont pas dans la liste\r
- BrevetNote.withCriteria {\r
- eq('fiche',fiche)\r
- epreuve {\r
- eq('optionnelle',true)\r
- }\r
- if (epreuves) {\r
- not{'in'('epreuve',epreuves)}\r
- }\r
- }*.delete()\r
-\r
- List<Long> epreuveDejaInscriteIds = (List) BrevetNote.withCriteria {\r
- eq('fiche', fiche)\r
- if (epreuves) {\r
- 'in'('epreuve', epreuves)\r
- }\r
- epreuve {\r
- eq('optionnelle',true)\r
- projections {\r
- property('id')\r
- }\r
- }\r
- }\r
-\r
- epreuves.each {BrevetEpreuve epreuve ->\r
- if (serie?.id != epreuve.serieId) {\r
- throw new IllegalArgumentException(\r
- "La série $serie de l'élève $eleve ne correspond pas à celle de l'épreuve $epreuve"\r
- )\r
- }\r
-\r
- if (!epreuve.optionnelle) {\r
- throw new IllegalArgumentException("L'épreuve $eleve n'est pas une épreuve optionnelle")\r
- }\r
-\r
- if (!epreuveDejaInscriteIds.any {it == epreuve.id}) {\r
- new BrevetNote(fiche: fiche, epreuve: epreuve).save(failOnError: true)\r
- }\r
- }\r
- sessionFactory.currentSession.flush()\r
- }\r
-\r
- BrevetNote findByEleveAndEpreuve(Personne eleve,\r
- BrevetEpreuve epreuve) {\r
-\r
- return BrevetNote.createCriteria().get {\r
- fiche {\r
- eq('eleve', eleve)\r
- eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())\r
- }\r
- eq('epreuve', epreuve)\r
- }\r
- }\r
-\r
-\r
-}\r
+package org.lilie.services.eliot.notes.brevet
+
+import org.lilie.services.eliot.brevet.BrevetNote
+import org.hibernate.FetchMode
+import org.lilie.services.eliot.scolarite.Personne
+import org.lilie.services.eliot.brevet.BrevetEpreuve
+
+import org.lilie.services.eliot.scolarite.Etablissement
+import org.lilie.services.eliot.scolarite.StructureEnseignement
+import org.hibernate.SessionFactory
+import org.lilie.services.eliot.scolarite.structureenseignement.LocalStructureEnseignementService
+import org.lilie.services.eliot.brevet.BrevetRelEpreuveMatiere
+import org.lilie.services.eliot.scolarite.Matiere
+import org.lilie.services.eliot.notes.scolarite.NotesServiceService
+import org.lilie.services.eliot.scolarite.Periode
+import org.lilie.services.eliot.notes.scolarite.NotesPeriodeService
+import org.lilie.services.eliot.scolarite.IntervalleEnum
+import org.lilie.services.eliot.scolarite.Service
+import org.lilie.services.eliot.applications.notes.resultat.eleve.ResultatEleveServicePeriodeService
+import org.lilie.services.eliot.notes.ResultatEleveServicePeriode
+import org.lilie.services.eliot.applications.notes.resultat.eleve.AppreciationEleveEnseignementPeriodeService
+import org.lilie.services.eliot.notes.resultat.NoteInfo
+import org.lilie.services.eliot.notes.resultat.MoyenneService
+import org.lilie.services.eliot.brevet.BrevetSerie
+import org.lilie.services.eliot.notes.scolarite.NotesStructureEnseignementService
+import org.lilie.services.eliot.notes.resultat.CalculationService
+import org.lilie.services.eliot.scolarite.anneescolaire.AnneeScolaireService
+import org.lilie.services.eliot.brevet.BrevetFiche
+import org.lilie.services.eliot.brevet.BrevetModeCalculNote
+import org.lilie.services.eliot.notes.Note
+import org.lilie.services.eliot.scolarite.NaturePeriode
+import org.lilie.services.eliot.constantes.ConstFonct
+
+/**
+ * Gestion des notes de brevet
+ * @author msan
+ * @author bper
+ */
+class BrevetNoteService {
+
+ LocalStructureEnseignementService localStructureEnseignementService
+ NotesServiceService notesServiceService
+ NotesPeriodeService notesPeriodeService
+ ResultatEleveServicePeriodeService resultatEleveServicePeriodeService
+ AppreciationEleveEnseignementPeriodeService appreciationEleveEnseignementPeriodeService
+ MoyenneService moyenneService
+ BrevetRelEpreuveMatiereService brevetRelEpreuveMatiereService
+ BrevetSerieService brevetSerieService
+ NotesStructureEnseignementService notesStructureEnseignementService
+ CalculationService calculationService
+
+ AnneeScolaireService anneeScolaireService
+
+ SessionFactory sessionFactory
+
+ static transactional = true
+
+
+ /**
+ * Initialise les notes des brevets
+ * @author msan
+ */
+ Boolean initialiseNotesBrevet(List<StructureEnseignement> classes) {
+
+ Boolean initialisationOk = true
+
+ // recupere notes
+ List<BrevetNoteInfo> notesInitialises = actualiseNotes(classes, true)
+
+ // enregsitre les changements
+ notesInitialises.each { BrevetNoteInfo noteInfo ->
+ BrevetNote brevetNote = BrevetNote.get(noteInfo.noteId)
+ if (noteInfo.appreciation != null || noteInfo.valeurNumerique != null || noteInfo.noteValeur) {
+
+ if (noteInfo.appreciation != null) {
+ brevetNote.appreciation = noteInfo.appreciation
+ }
+
+ if (noteInfo.valeurNumerique != null) {
+ brevetNote.valeurNumerique = noteInfo.valeurNumerique
+ }
+ else if (noteInfo.noteValeur != null) {
+ brevetNote.setValeurTextuelle(noteInfo.noteValeur)
+ }
+
+ brevetNote.save(failOnError: true)
+ }
+ }
+ sessionFactory.currentSession.flush()
+
+ return initialisationOk
+ }
+
+ /**
+ * Recupere BrevetNotes avec les valeurs actualisees avec les resultats reels
+ * d'eleves.
+ * La liste ne contiendra tout les eleves car il n'est pas necessaire
+ * que tout les eleves d'une classe suivent la meme serie.
+ * @author msan
+ */
+ List<BrevetNoteInfo> actualiseNotes(StructureEnseignement classe,
+ List<Personne> eleves) {
+
+ Map eleveMap = [:]
+ eleves.each {
+ eleveMap.put(it, classe)
+ }
+
+ List<BrevetNoteInfo> noteInfos = actualiseNotes([classe], eleveMap)
+
+ majAppreciations(noteInfos)
+
+ return noteInfos
+ }
+
+ /**
+ * Met à jour les appréciations des notes de Brevet à partir des appréciations
+ * annuelles récupérées lors de l'actualisation des notes.
+ * @author bper
+ */
+ private void majAppreciations(List<BrevetNoteInfo> noteInfos) {
+ noteInfos.each {
+ BrevetNote note = BrevetNote.get(it.noteId)
+ if (note && note.appreciation != it.appreciation) {
+ note.appreciation = it.appreciation
+ note.save(failOnError: true, flush: true)
+ }
+ }
+ }
+
+ /**
+ * Toutes les BrevetNotes pour les eleves avec les associantions fetches.
+ * @author msan
+ */
+ List<BrevetNote> findAllNotes(List<Personne> eleves) {
+ return BrevetNote.createCriteria().listDistinct {
+ fiche {
+ 'in'('eleve', eleves)
+ eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())
+ }
+ fetchMode('valeurTextuelle', FetchMode.JOIN) // AB,DI,NV ..
+ fetchMode('epreuve', FetchMode.JOIN)
+ fetchMode('epreuve.serie', FetchMode.JOIN)
+ fetchMode('epreuve.valeurTextuelleAutorisees', FetchMode.JOIN)
+ fetchMode('fiche', FetchMode.JOIN)
+ fetchMode('fiche.eleve', FetchMode.JOIN)
+ }
+ }
+
+ /**
+ * Recupere BrevetNotes avec les valeurs actualisees avec les resultats reels
+ * d'eleves. A utiliser pour l'actualisation ou l'initialisation.
+ * @param eleveMap une map Eleve -> Sa classe
+ * @param initialisation true s'il s'agit d'initiaisation
+ * @author msan
+ */
+ List<BrevetNoteInfo> actualiseNotes(List<StructureEnseignement> classes,
+ Map<Personne, StructureEnseignement> eleveMap,
+ Boolean initialisation = false) {
+
+ List<BrevetNoteInfo> notesActualisess = []
+
+ List<Personne> eleves = eleveMap.keySet().toList()
+ Etablissement etablissement = classes.first().etablissement
+
+ // get BrevetNotes
+ List<BrevetNote> notes = findAllNotes(eleves)
+ List<BrevetEpreuve> epreuves = notes.collect { it.epreuve }.unique { it.id }
+ epreuves = epreuves.findAll {it.notee}
+
+ // get relations Epreuve - Metieres fixes
+ List<BrevetRelEpreuveMatiere> relFixes = brevetRelEpreuveMatiereService.findAllRel(epreuves)
+
+ Map<StructureEnseignement, Periode> periodesAnnee =
+ findAllPeriodeAnneeByClasses(classes)
+
+ notes.groupBy{it.fiche.eleve}.each { Personne eleve, List<BrevetNote> notesEleve ->
+ List<BrevetEpreuve> eleveEpreuves = notesEleve.collect { it.epreuve }
+ eleveEpreuves = eleveEpreuves.findAll {it.notee == true}
+
+ StructureEnseignement classe = eleveMap.get(eleve)
+ Periode periodeAnnee = periodesAnnee.get(classe)
+
+ if (initialisation) {
+ notesEleve = incrisEleveAuxEpreuvesOptionnellesEtPersonnanlisables(notesEleve)
+ }
+
+ List<EpreuveInfo> epreuveInfos = findAllCorrespondanceEpreuveMatiere(
+ eleveEpreuves,
+ eleve,
+ etablissement,
+ relFixes,
+ notesEleve,
+ periodeAnnee,
+ initialisation
+ )
+
+ if (initialisation) {
+ notesEleve = majNotesAuxEpreuvesOptionnellesEtPersonnanlisables(notesEleve)
+ }
+
+ // on peut avoir plusieurs services pour une epreuve dans le cas de partage des matieres
+ ajouteInfoServices(epreuveInfos, eleve, periodeAnnee)
+
+ ajouteInfoMoyennes(epreuveInfos, eleve, periodeAnnee)
+
+ ajouteInfoAppreciations(epreuveInfos, eleve, periodeAnnee)
+
+ // actualise les notes de brevet avec les resultats reels
+ notesEleve.each { BrevetNote brevetNote ->
+ EpreuveInfo epreuveInfo = epreuveInfos.find {
+ it.epreuve.id == brevetNote.epreuveId }
+
+ notesActualisess += actualiseNote(
+ epreuveInfo.moyenne,
+ epreuveInfo.appreciation,
+ brevetNote
+ )
+ }
+ }
+
+ return notesActualisess
+ }
+
+ /**
+ * Crée des notes de brevet pour les épreuves à la fois optionnelles et personnalisables sans les enregistrer.
+ * Ces notes seront enregistrées si on trouve une matière correspondante pour cet élève.
+ */
+ private List<BrevetNote> incrisEleveAuxEpreuvesOptionnellesEtPersonnanlisables(List<BrevetNote> notesEleve) {
+
+ BrevetSerie serie = notesEleve.first().epreuve.serie
+ BrevetFiche fiche = notesEleve.first().fiche
+
+ List<BrevetEpreuve> epreuvesOptEtPers =
+ serie.epreuves.findAll {it.personnalisable && it.optionnelle}.toList()
+
+ epreuvesOptEtPers.each {BrevetEpreuve epreuveOptEtPers ->
+ if (!notesEleve.any {it.epreuveId == epreuveOptEtPers.id}) {
+ notesEleve << new BrevetNote(fiche: fiche, epreuve: epreuveOptEtPers)
+ }
+ }
+
+ return notesEleve
+ }
+
+ /**
+ * Enregistre les notes de brevet pour les épreuves à la fois optionnelles et
+ * personnalisables si la matière attaché n'est pas null.
+ */
+ private List<BrevetNote> majNotesAuxEpreuvesOptionnellesEtPersonnanlisables(List<BrevetNote> notesEleve) {
+ List<BrevetNote> notesEleveAEnlever = []
+
+ notesEleve.each {
+ if (it.epreuve.personnalisable && it.epreuve.optionnelle) {
+ if (it.matiereId == null) {
+ notesEleveAEnlever << it
+ } else {
+ it.save(failOnError: true)
+ }
+ }
+ }
+
+ notesEleve.removeAll(notesEleveAEnlever)
+ notesEleveAEnlever*.delete()
+
+ return notesEleve
+ }
+
+ /**
+ * Ajoute les appreciations dans epreuveInfos
+ * @author msan
+ */
+ void ajouteInfoAppreciations(List<EpreuveInfo> epreuveInfos,
+ Personne eleve,
+ Periode periode) {
+
+ List<Service> services = (List) epreuveInfos.collect { it.services }.flatten().
+ findAll { it != null }.unique { it.id }
+
+ Map<Service, List<String>> apps =
+ appreciationEleveEnseignementPeriodeService.findAll(eleve, periode, services)
+
+ epreuveInfos.each { EpreuveInfo epreuveInfo ->
+ if (epreuveInfo.services?.size() > 0) {
+ List<String> appsEpreuve = (List) epreuveInfo.services.collect { Service service ->
+ List<String> serviceApps = apps.get(service)
+ if (serviceApps?.size() > 0) {
+ return serviceApps
+ } else {
+ return []
+ }
+ }.flatten().findAll { it != null }
+ if (appsEpreuve?.size() > 0) {
+ // scotche les appreciations
+ epreuveInfo.appreciation = appsEpreuve.join(' ')
+ }
+ }
+ }
+ }
+
+ /**
+ * Ajoute les moyennes dans epreuveInfos.
+ * Re-initialise les listes des matières et des services en fonction des moyennes utilisées.
+ * On ne garde que les matières et les services pour lesquels les moyennes ont été trouvées.
+ * @author msan
+ * @author bper
+ */
+ void ajouteInfoMoyennes(List<EpreuveInfo> epreuveInfos,
+ Personne eleve,
+ Periode periode) {
+
+ List<Service> services = (List) epreuveInfos.collect { it.services }.flatten().
+ findAll { it != null }.unique { it.id }
+
+ Map<Long, Integer> mapMatiereIdOrdre = getMapMatiereIdOrdre(services, periode)
+
+ List<ResultatEleveServicePeriode> resultats = resultatEleveServicePeriodeService.
+ findAll(
+ [eleve.autorite],
+ services,
+ [periode],
+ [NaturePeriode.NOTATION]
+ )
+
+ resultats = (List) resultats.findAll {it.moyenne != null}
+ List<Service> serviceAvecMoyennes = resultats*.service
+
+ if (resultats.size() == 0) {
+ epreuveInfos*.matieres = []
+ epreuveInfos*.services = []
+ return
+ }
+
+ Map resultatsParMatiere = resultats.groupBy {it.service.matiere}
+
+ Map moyenneParMatiere = [:]
+
+ // Calcule les moyennes des matières. Si une matières correspond à plusieurs services,
+ // on calcule la moyenne des moyennes des services pondérée par les coeffs
+ // des services pour la période donnée .
+ resultatsParMatiere.each {Matiere matiere, List<ResultatEleveServicePeriode> ress ->
+ BigDecimal moyenne = ress.size() == 1 ?
+ ress.first().moyenne : calculeMoyenneResultats(ress, periode)
+
+ moyenneParMatiere.put(matiere, moyenne)
+ }
+
+ epreuveInfos.each {EpreuveInfo epreuveInfo ->
+ Map moyenneParMatierePourEpreuve =
+ (Map) moyenneParMatiere.findAll {Matiere matiere, BigDecimal moyenne ->
+ epreuveInfo.matieres*.id.contains(matiere.id)
+ }
+
+ if (moyenneParMatierePourEpreuve.size() == 0) {
+ epreuveInfo.matieres = []
+ epreuveInfo.services = []
+ return
+ }
+
+ switch (epreuveInfo.epreuve.modeCalculNote) {
+ case BrevetModeCalculNote.PREMIERE_TROUVEE :
+ List<Matiere> matieres = moyenneParMatierePourEpreuve.keySet().toList()
+ matieres.sort{m1,m2 -> mapMatiereIdOrdre.get(m1.id) <=> mapMatiereIdOrdre.get(m2.id)}
+ Matiere matiere = (Matiere) matieres.first()
+ epreuveInfo.moyenne = (BigDecimal) moyenneParMatierePourEpreuve.get(matiere)
+ epreuveInfo.matieres = [matiere]
+ break
+
+ case BrevetModeCalculNote.MOYENNE :
+ List<BigDecimal> moyennes = (List<BigDecimal>) moyenneParMatierePourEpreuve.values().toList()
+ epreuveInfo.moyenne = calculationService.calculeMoyenne(moyennes, true)?.valeurNumerique
+ epreuveInfo.matieres = moyenneParMatierePourEpreuve.keySet().toList()
+ break
+
+ case BrevetModeCalculNote.MOYENNE_SPORT :
+ Service serviceSport = findServiceSport(periode.classe)
+ epreuveInfo.moyenne = calculeMoyenneSport(serviceSport, eleve)
+ epreuveInfo.matieres = serviceSport ? [serviceSport.matiere] : []
+ break
+
+ default:
+ throw new IllegalStateException(
+ "Le mode de calcul de note ${epreuveInfo.epreuve.modeCalculNote} " +
+ "pour l'épreuve ${epreuveInfo.epreuve} n'est pris en compte")
+ }
+
+ epreuveInfo.services = (List) serviceAvecMoyennes.findAll {
+ epreuveInfo.matieres.contains(it.matiere)
+ }
+ }
+ }
+
+ private Map<Long, Integer> getMapMatiereIdOrdre(List<Service> services, Periode periode) {
+ services = notesServiceService.trieServices(services, periode)
+
+ Map<Service, Integer> mapServiceOrdre = [:]
+
+ services.eachWithIndex {Service service, Integer ordre ->
+ mapServiceOrdre.put(service, ordre)
+ }
+
+ Map<Long, Integer> mapMatiereIdOrdre = [:]
+
+ services.groupBy{it.matiere}.each{Matiere matiere, List<Service> servicesMatiere ->
+ List<Integer> ordres = servicesMatiere.collect{mapServiceOrdre.get(it)}
+ mapMatiereIdOrdre.put(matiere.id, ordres.min())
+ }
+
+ return mapMatiereIdOrdre
+ }
+
+ private findServiceSport(StructureEnseignement classe) {
+ return Service.findByStructureEnseignementAndChoisiPourSport(classe, true)
+ }
+
+ private BigDecimal calculeMoyenneSport(Service serviceSport, Personne eleve) {
+
+ if (!serviceSport) {
+ return null
+ }
+
+ List<Note> notes = Note.createCriteria().listDistinct {
+ eq('eleve', eleve.autorite)
+ evaluation {
+ enseignement {
+ eq(ConstFonct.SERVICE, serviceSport)
+ }
+ periodes {
+ typePeriode {
+ eq('nature', NaturePeriode.NOTATION)
+ }
+ }
+ }
+
+ eq('choisiePourSport', true)
+ }
+
+ if (notes.size() < 3) {
+ return null
+ }
+
+ if (notes.size() > 3) {
+ throw new IllegalStateException(
+ "L'élève $eleve a plus de 3 notes sélectionnées sport dans le service $serviceSport")
+ }
+
+ return calculationService.calculeMoyenne(notes*.valeurSur20, true)?.valeurNumerique
+ }
+
+ /**
+ * Moyenne ponderee des resultats
+ * @author msan
+ */
+ BigDecimal calculeMoyenneResultats(List<ResultatEleveServicePeriode> resultats,
+ Periode periode) {
+ // calcule la moyenne ponderee
+ List<NoteInfo> noteInfos = resultats.collect {
+ new NoteInfo(
+ it.getObjetMoyenne(),
+ it.service.getCoeffPourPeriode(periode),
+ false)
+ }
+ return calculationService.calculeMoyennePonderee(noteInfos)?.valeurNumerique
+ }
+
+ /**
+ * Ajoute la correspondence entre les epreuves et les services dans epreuve info
+ * @author msan
+ * @author bper
+ */
+ void ajouteInfoServices(List<EpreuveInfo> epreuveInfos,
+ Personne eleve,
+ Periode periode) {
+
+ List<Matiere> matieres = epreuveInfos*.matieres.flatten().toList()
+
+ // get services
+ List<Service> services = notesServiceService.
+ findAllServiceByElevePeriodeMatieres(
+ eleve,
+ periode,
+ matieres
+ )
+
+ // Affecte les services en fonction des matières affectées
+ services.each {Service service ->
+ epreuveInfos.each {EpreuveInfo epreuveInfo ->
+ if (epreuveInfo.matieres*.id.contains(service.matiereId)) {
+ epreuveInfo.services.add(service)
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Actualise note si necessaire et met la dans la liste noteActualisees.
+ * S'il existe déjà une valeur textuelle saisie pour une note de brevet
+ * la valeur numérique de cette note n'est pas mise à jour.
+ * @author msan
+ */
+ private List<BrevetNoteInfo> actualiseNote(BigDecimal note,
+ String appreciation,
+ BrevetNote brevetNote) {
+
+ List<BrevetNoteInfo> notesActualises = []
+
+ // si la moyenne ou l'appr existe, ajoute la note
+ // dans la liste des resultats envoyes
+ if (note != null || appreciation != null ||
+ (brevetNote.epreuve.isSport() && brevetNote.valeur == null)) {
+
+ BrevetNoteInfo brevetNoteInfo = new BrevetNoteInfo(
+ noteId: brevetNote.id,
+ eleveId: brevetNote.fiche.eleveId,
+ epreuveId: brevetNote.epreuveId,
+ isSport: brevetNote.epreuve.isSport()
+ )
+
+ if (brevetNote.valeurTextuelle == null && note != null) {
+ brevetNoteInfo.valeurNumerique = note
+ brevetNoteInfo.noteValeur = BrevetNote.NOTE_FORMATEUR.format(note)
+ }
+
+ // Cas particulier de l'épreuve de Sport (EPS) :
+ // La note textuelle "DI" pour cette épreuve peut etre remplacée par
+ // la note numérique. Si la note numérique est null on lui affecte
+ // la note textuelle "DI" comme valeur par défaut.
+ if (brevetNote.epreuve.isSport()) {
+ if (brevetNote.valeurTextuelle == null ||
+ brevetNote.valeurTextuelle.valeur == NoteTextuellesEnum.DI.toString()) {
+
+ if (note == null) {
+ brevetNoteInfo.valeurNumerique = null
+ brevetNoteInfo.noteValeur = NoteTextuellesEnum.DI
+ }
+ else {
+ brevetNoteInfo.valeurNumerique = note
+ brevetNoteInfo.noteValeur = BrevetNote.NOTE_FORMATEUR.format(note)
+ }
+ }
+ }
+
+ if (appreciation != null) {
+ brevetNoteInfo.appreciation = coupeAppreciation(appreciation)
+ }
+
+ notesActualises.add(brevetNoteInfo)
+ }
+
+ return notesActualises
+ }
+
+ /**
+ * Coupe une appreciation a la taille de 255 characteres
+ * @author msan
+ */
+ String coupeAppreciation(String appreciation) {
+ return appreciation.length()>255 ? appreciation.substring(0, 255) : appreciation
+ }
+
+ /**
+ * Une map des periodes annees, indexee par les classes
+ * @author msan
+ */
+ Map<StructureEnseignement, Periode> findAllPeriodeAnneeByClasses(List<StructureEnseignement> classes) {
+ Map classesPeriodes = [:]
+ List<Periode> periodesAnnees = notesPeriodeService.
+ findAllPeriodeByClasses(
+ classes,
+ IntervalleEnum.ANNEE
+ )
+ periodesAnnees.each { Periode periodeAnnee ->
+ classesPeriodes.put(periodeAnnee.classe, periodeAnnee)
+ }
+ return classesPeriodes
+ }
+
+ /**
+ * Cherche les matieres correspondant a une epreuve pour un eleve donne
+ * @author msan
+ * @author bper
+ */
+ List<EpreuveInfo> findAllCorrespondanceEpreuveMatiere(List<BrevetEpreuve> epreuves,
+ Personne eleve,
+ Etablissement etablissement,
+ List<BrevetRelEpreuveMatiere> relFixes,
+ List<BrevetNote> notes,
+ Periode periodeAnnee,
+ Boolean initialisation) {
+ List<EpreuveInfo> epreuveInfos = []
+ notes.each { BrevetNote note ->
+ List<Matiere> matieres = findCorrespondanceEpreuveMatiere(
+ note.epreuve,
+ eleve,
+ etablissement,
+ relFixes,
+ note,
+ periodeAnnee,
+ initialisation
+ )
+ epreuveInfos.add(new EpreuveInfo(
+ epreuve: note.epreuve,
+ matieres: matieres
+ ))
+ }
+ return epreuveInfos
+ }
+
+ /**
+ * Cherche le matiere correspondant a une epreuve pour un eleve donne
+ * @author msan
+ * @author bper
+ */
+ private List<Matiere> findCorrespondanceEpreuveMatiere(BrevetEpreuve epreuve,
+ Personne eleve,
+ Etablissement etablissement,
+ List<BrevetRelEpreuveMatiere> relFixes,
+ BrevetNote note,
+ Periode periodeAnnee,
+ Boolean initialisation) {
+ List<Matiere> matieres = null
+
+ if (epreuve.personnalisable) {
+ // epreuve personalisable - impactee par les options d'eleve
+
+ if (isMatiereAinitialiser(initialisation, note.matiere)) {
+ initialiseMatiere(
+ eleve,
+ periodeAnnee,
+ relFixes,
+ note)
+ }
+ matieres = [note.matiere]
+ } else {
+ // epreuve avec les matieres fixes
+ matieres = relFixes.findAll {
+ it.epreuveId == epreuve.id &&
+ it.matiere.etablissementId == etablissement.id
+ }?.collect { it.matiere }
+ }
+
+ // traitement HiGeo - EduCivique.
+ // si EduCivique n'est pas liee au matiere, on prend le rsultat de HiGeo
+ if (epreuve.epreuveMatieresAHeriter != null && (matieres == null || matieres.size()==0)) {
+ matieres = relFixes.findAll {
+ it.epreuve.id == epreuve.epreuveMatieresAHeriterId &&
+ it.matiere.etablissementId == etablissement.id
+ }?.collect { it.matiere }
+ }
+
+ return matieres
+ }
+
+ /**
+ * Initialise la matiere de BrevetNote par la premiere matiere ou on trouve
+ * une note pour l'eleve donne
+ * @author msan
+ */
+ void initialiseMatiere(Personne eleve,
+ Periode periodeAnnee,
+ List<BrevetRelEpreuveMatiere> relFixes,
+ BrevetNote note) {
+ // Spec: Si une épreuve de type « LV1 ou LV2 ou Opt. facultative » (== personnalisable)
+ // est liée à plusieurs matières, le module Notes affecte à l'élève la
+ // première matière trouvée où l'élève a une note.
+ note.matiere = findPremiereMatiereAvecNote(
+ eleve,
+ periodeAnnee,
+ relFixes.findAll {
+ it.epreuveId == note.epreuveId
+ }?.collect { it.matiere }
+ )
+ if (log.isDebugEnabled()) {
+ log.debug("BrevetNote ${note.id} initialisee avec matiere ${note.matiere}")
+ }
+ }
+
+ /**
+ * S'il faut initialiser la matiere
+ * @author msan
+ */
+ Boolean isMatiereAinitialiser(boolean initialisation, Matiere matiere) {
+ return (initialisation && matiere == null)
+ }
+
+ /**
+ * Cherche une matiere qui est note pour l'eleve donne
+ * @author msan
+ */
+ Matiere findPremiereMatiereAvecNote(Personne eleve,
+ Periode periodeAnnee,
+ List<Matiere> matieres) {
+
+ // si note.matiere n'est pas saisie il faut chercher les resultats d'eleve
+ // pour les matieres fixes de cette epreuve et prendre la matiere ou l'eleve
+ // a une note + positionner la matiere de note
+
+ List<Service> services = notesServiceService.findAllServiceByElevePeriodeMatieres(
+ eleve,
+ periodeAnnee,
+ matieres)
+
+ List<ResultatEleveServicePeriode> resultats = resultatEleveServicePeriodeService.findAll(
+ [eleve.autorite],
+ services,
+ [periodeAnnee],
+ [NaturePeriode.NOTATION]
+ )
+
+ resultats = (List) resultats.findAll {it.moyenne != null}
+
+ if (resultats?.size()>0) {
+ return resultats.first().service.matiere
+ } else {
+ return null
+ }
+ }
+
+ /**
+ * Recupere BrevetNotes avec les valeurs actualisees avec les resultats reels
+ * d'eleves.
+ * @author msan
+ */
+ List<BrevetNoteInfo> actualiseNotes(List<StructureEnseignement> classes,
+ Boolean initialisation = false) {
+
+ if (classes == null || classes.size() == 0) {
+ return []
+ }
+
+ // prepare la liste des eleves
+ Map<Personne, StructureEnseignement> eleveMap = [:]
+ List elevesClasses = localStructureEnseignementService.
+ findAllPersonneEleveAndClasseForAllClasses(classes)
+
+ elevesClasses.each { def eleveClasse ->
+ Personne eleve = eleveClasse[0].personne
+ StructureEnseignement classe = eleveClasse[1]
+ eleveMap.put(eleve, classe)
+ }
+
+ return actualiseNotes(classes, eleveMap, initialisation)
+
+ }
+
+ /**
+ * Retourne le mapping personnalisée Epreuve -> Matiere.
+ * @author bper
+ */
+ Map<Personne,Map<BrevetEpreuve,Matiere>> findAllMapEleveEpreuveMatierePourEleves(List<Personne> eleves) {
+
+ List<BrevetNote> notes = BrevetNote.withCriteria {
+ fiche {
+ 'in'('eleve',eleves)
+ eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())
+ }
+ isNotNull(ConstFonct.MATIERE)
+ epreuve {
+ eq('personnalisable', true)
+ }
+ }
+
+ Map<Personne,Map<BrevetEpreuve,Matiere>> map = [:]
+
+ eleves.each {Personne eleve ->
+ Map mapEpreuveMatiere = [:]
+ notes.each {
+ if (it.fiche.eleveId == eleve.id) {
+ mapEpreuveMatiere.put(it.epreuve, it.matiere)
+ }
+ }
+ map.put(eleve,mapEpreuveMatiere)
+ }
+
+ return map
+ }
+
+
+ /**
+ * Met à jour la les choix de matières pour les épreuves personnalisables
+ * pour un élève donné.
+ * @author bper
+ */
+ void majChoixMateiresPourEleve(Personne eleve,
+ Map<BrevetEpreuve,Matiere> mapEpreuveMatiere) {
+
+ List<BrevetNote> notes = BrevetNote.withCriteria {
+ fiche {
+ eq('eleve',eleve)
+ eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())
+ }
+ epreuve {
+ eq('personnalisable', true)
+ }
+ }
+
+ mapEpreuveMatiere.each {BrevetEpreuve epreuve, Matiere matiere ->
+ if (!epreuve.personnalisable) {
+ throw new IllegalArgumentException("L'épreuve $epreuve n'est pas pesronnalisable")
+ }
+
+ BrevetNote note = notes.find {it.epreuveId == epreuve.id}
+
+ if (note) {
+ note.matiere = matiere
+ note.save(failOnError: true)
+ } else if (matiere) {
+ throw new IllegalArgumentException(
+ "Impossible d'assoceier la matière $matiere à l'épreuve $epreuve pour " +
+ "l L'élève $eleve. Il n'est pas inscrit pour cette épreuve."
+ )
+ }
+ }
+
+ sessionFactory.currentSession.flush()
+ }
+
+
+ /**
+ * Inscrit l'élève pour les épreuves qui ont des épreuves exclusives.
+ * @author bper
+ */
+ void majInscriptionEpreuveExclusives(Personne eleve,
+ List<BrevetEpreuve> epreuves) {
+
+ if (!epreuves) {
+ return
+ }
+
+ BrevetSerie serie = brevetSerieService.findSeriePourEleve(eleve)
+
+ if (!serie.id) {
+ throw new IllegalArgumentException("L'élève $eleve n'est pas inscrit au Brevet")
+ }
+
+ BrevetFiche fiche = BrevetFiche.findByEleveAndAnneeScolaire(
+ eleve,
+ anneeScolaireService.anneeScolaireEnCours()
+ )
+
+ // Suppime les notes aux épreuves exclusives
+ List<BrevetNote> noteToDeletes = BrevetNote.withCriteria {
+ eq('fiche',fiche)
+ 'in'('epreuve',epreuves*.epreuveExclusive)
+ }
+
+ noteToDeletes.each {
+ fiche.removeFromNotes(it)
+ it.delete()
+ }
+ sessionFactory.currentSession.flush()
+
+ List<Long> epreuveDejaInscriteIds = (List) BrevetNote.withCriteria {
+ eq('fiche',fiche)
+ 'in'('epreuve',epreuves)
+ epreuve {
+ projections {
+ property('id')
+ }
+ }
+ }
+
+ epreuves.each {BrevetEpreuve epreuve ->
+ if (epreuve.serieId != serie.id) {
+ throw new IllegalArgumentException(
+ "La série $serie de l'élève $eleve ne correspond pas à celle de l'épreuve $epreuve"
+ )
+ }
+
+ if (!epreuve.epreuveExclusive) {
+ throw new IllegalArgumentException("L'épreuve $eleve n'a d'épreuve exclusive")
+ }
+
+ if (!epreuveDejaInscriteIds.any {it == epreuve.id}) {
+ BrevetNote note = new BrevetNote(fiche: fiche, epreuve: epreuve)
+ fiche.addToNotes(note)
+ }
+ }
+ fiche.save(failOnError: true, flush: true)
+ }
+
+
+ /**
+ * Inscrit l'élève aux épreuves optionnelles passées en paramétres et
+ * le desinscrit des épreuves optionnelles qui ne sont pas dans la liste.
+ * @author bper
+ */
+ void majInscriptionEpreuveOptionnelles(Personne eleve,
+ List<BrevetEpreuve> epreuves) {
+
+ BrevetSerie serie = brevetSerieService.findSeriePourEleve(eleve)
+
+ if (!serie.id) {
+ throw new IllegalArgumentException("L'élève $eleve n'est pas inscrit au Brevet")
+ }
+
+ BrevetFiche fiche = BrevetFiche.findByEleveAndAnneeScolaire(
+ eleve,
+ anneeScolaireService.anneeScolaireEnCours()
+ )
+
+ // Suppime les notes aux épreuves optionnelles qui ne sont pas dans la liste
+ BrevetNote.withCriteria {
+ eq('fiche',fiche)
+ epreuve {
+ eq('optionnelle',true)
+ }
+ if (epreuves) {
+ not{'in'('epreuve',epreuves)}
+ }
+ }*.delete()
+
+ List<Long> epreuveDejaInscriteIds = (List) BrevetNote.withCriteria {
+ eq('fiche', fiche)
+ if (epreuves) {
+ 'in'('epreuve', epreuves)
+ }
+ epreuve {
+ eq('optionnelle',true)
+ projections {
+ property('id')
+ }
+ }
+ }
+
+ epreuves.each {BrevetEpreuve epreuve ->
+ if (serie?.id != epreuve.serieId) {
+ throw new IllegalArgumentException(
+ "La série $serie de l'élève $eleve ne correspond pas à celle de l'épreuve $epreuve"
+ )
+ }
+
+ if (!epreuve.optionnelle) {
+ throw new IllegalArgumentException("L'épreuve $eleve n'est pas une épreuve optionnelle")
+ }
+
+ if (!epreuveDejaInscriteIds.any {it == epreuve.id}) {
+ new BrevetNote(fiche: fiche, epreuve: epreuve).save(failOnError: true)
+ }
+ }
+ sessionFactory.currentSession.flush()
+ }
+
+ BrevetNote findByEleveAndEpreuve(Personne eleve,
+ BrevetEpreuve epreuve) {
+
+ return BrevetNote.createCriteria().get {
+ fiche {
+ eq('eleve', eleve)
+ eq('anneeScolaire', anneeScolaireService.anneeScolaireEnCours())
+ }
+ eq('epreuve', epreuve)
+ }
+ }
+
+
+}