--- /dev/null
+/*
+ * Copyright © FYLAB and the Conseil Régional d'Île-de-France, 2009
+ * This file is part of L'Interface Libre et Interactive de l'Enseignement (Lilie).
+ *
+ * Lilie is free software. You can redistribute it and/or modify since
+ * you respect the terms of either (at least one of the both license) :
+ * - under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * - the CeCILL-C as published by CeCILL-C; either version 1 of the
+ * License, or any later version
+ *
+ * There are special exceptions to the terms and conditions of the
+ * licenses as they are applied to this software. View the full text of
+ * the exception in file LICENSE.txt in the directory of this software
+ * distribution.
+ *
+ * Lilie is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Licenses for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the CeCILL-C along with Lilie. If not, see :
+ * <http://www.gnu.org/licenses/> and
+ * <http://www.cecill.info/licences.fr.html>.
+ */
+
+package org.lilie.services.eliot.applications.absences.bilan
+
+import org.lilie.services.eliot.absences.PreferencesEtablissementAbsences
+import org.lilie.services.eliot.absences.PlageHoraire
+import org.lilie.services.eliot.applications.absences.StatAbsences
+import org.lilie.services.eliot.scolarite.Personne
+import org.lilie.services.eliot.applications.scolarite.calendrier.PlageHoraireService
+import org.lilie.services.eliot.absences.Appel
+import org.lilie.services.eliot.absences.AppelLigne
+import org.lilie.services.eliot.temps.DateUtil
+
+/**
+ * Service de calcul pour bilanservice
+ */
+class BilanCalculeService {
+
+ protected static final Byte AUCUN = 0
+ protected static final Byte MATIN = 1
+ protected static final Byte APRES_MIDI = 2
+ protected static final Byte MATIN_ET_APRES_MIDI = 3
+
+ PlageHoraireService plageHoraireService
+ BilanPasDecompteService bilanPasDecompteService
+
+ /**
+ * Retourne la liste des absences avec motif, hors demi-pension et internat
+ * @return List < StatAbsences >
+ */
+ public calculeAbsencesExcusees(
+ BilanAbsencesParPersonneInfo absencesExcusees,
+ PreferencesEtablissementAbsences preferences,
+ List<StatAbsences> listEleveStats) {
+
+ Map calculsAbsencesExcuses = calculeNbAbsencesParEleve(
+ absencesExcusees,
+ preferences
+ )
+
+ calculsAbsencesExcuses.each { Personne eleve, Float nbAbsencesExcusees ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesExcusees =
+ nbAbsencesExcusees
+ }
+ }
+
+ public calculeAbsencesDemiPension(
+ BilanAbsencesParPersonneInfo absencesDemiPension,
+ List<StatAbsences> listEleveStats) {
+
+ absencesDemiPension.eachAbsence {
+ Personne eleve, BilanAbsenceInfo absenceInfo ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesDemiPension =
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesDemiPension + 1
+ }
+ }
+
+ public calculeAbsencesInternat(
+ BilanAbsencesParPersonneInfo absencesDemiPension,
+ List<StatAbsences> listEleveStats) {
+
+ absencesDemiPension.eachAbsence {
+ Personne eleve, BilanAbsenceInfo absenceInfo ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesInternat =
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesInternat + 1
+ }
+ }
+
+ public calculeAbsencesTotales(
+ BilanAbsencesParPersonneInfo absences,
+ PreferencesEtablissementAbsences preferences,
+ List<StatAbsences> listEleveStats) {
+
+ Map calculsAbsencesTotales = calculeNbAbsencesParEleve(
+ absences,
+ preferences
+ )
+
+ // Ajoute pour chaque élève son nombre d'absences total
+ calculsAbsencesTotales.each { Personne eleve, Float nbAbsencesTotal ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesTotal =
+ nbAbsencesTotal
+ }
+ }
+
+ /**
+ * Retourne la ligne de stats correspondant à l'élève en la créant si nécessaire
+ * @return une ligne de stats : StatAbsences
+ */
+ public StatAbsences getStatsForEleve(Personne eleve,
+ List<StatAbsences> listEleveStats) {
+ StatAbsences statEleve = (StatAbsences) listEleveStats.find {
+ it.eleve == eleve
+ }
+ if (!statEleve) {
+ statEleve = new StatAbsences()
+ listEleveStats << statEleve
+ statEleve.eleve = eleve
+ }
+ return statEleve
+ }
+
+ /**
+ * Extrait les absences sans motif, hors demi-pension et internat
+ */
+ public calculeAbsencesNonExcusees(
+ BilanAbsencesParPersonneInfo absencesNonExcusees,
+ PreferencesEtablissementAbsences preferences,
+ List<StatAbsences> listEleveStats) {
+
+ Map calculsAbsencesNonExcuses = calculeNbAbsencesParEleve(
+ absencesNonExcusees,
+ preferences
+ )
+
+ calculsAbsencesNonExcuses.each { Personne eleve, Float nbAbsencesNonExcusees ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesNonExcusees =
+ nbAbsencesNonExcusees
+ }
+ }
+
+ /**
+ * Extrait les absences recevables avec motif
+ */
+ public calculeAbsencesJustifieesRecevables(
+ BilanAbsencesParPersonneInfo absencesJustifieesRecevables,
+ PreferencesEtablissementAbsences preferences,
+ List<StatAbsences> listEleveStats) {
+
+ Map calculsAbsencesJustifieesRecevables = calculeNbAbsencesParEleve(
+ absencesJustifieesRecevables,
+ preferences
+ )
+ calculsAbsencesJustifieesRecevables.each { Personne eleve, Float nbAbsences ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesJustifieesRecevables =
+ nbAbsences
+ }
+ }
+
+ /**
+ * Extrait les absences non recevables avec motif
+ */
+ public calculeAbsencesJustifieesNonRecevables(
+ BilanAbsencesParPersonneInfo absencesJustifieesNonRecevables,
+ PreferencesEtablissementAbsences preferences,
+ List<StatAbsences> listEleveStats) {
+
+ Map calculsAbsencesJustifieesNonRecevables = calculeNbAbsencesParEleve(
+ absencesJustifieesNonRecevables,
+ preferences
+ )
+
+ calculsAbsencesJustifieesNonRecevables.each { Personne eleve, Float nbAbsences ->
+ getStatsForEleve(eleve, listEleveStats).nbAbsencesJustifieesNonRecevables =
+ nbAbsences
+ }
+ }
+
+ /**
+ * Retourne une map associant à chaque élève le nombre
+ */
+ public Map calculeNbAbsencesParEleve(BilanAbsencesParPersonneInfo datas,
+ PreferencesEtablissementAbsences pref
+ ) {
+ List<PlageHoraire> plages = plageHoraireService.getPlageHoraires(pref)
+ switch (pref.pasDecompte) {
+ case PreferencesEtablissementAbsences.DECOMPTE_HEURE:
+ return calculeNbAbsencesParEleveEnHeures(datas, pref, plages)
+ case PreferencesEtablissementAbsences.DECOMPTE_DEMIJOUR:
+ return calculeNbAbsencesParEleveEnDemiJours(datas, plages)
+ case PreferencesEtablissementAbsences.DECOMPTE_JOUR:
+ return calculeNbAbsencesParEleveEnJours(datas)
+ }
+ }
+
+ /**
+ * Calcule le nombre d'absences en heures par élève en fonction des données
+ * passées
+ * @param datas : liste dont chaque élément contient
+ * - Appel
+ * - AppelLigne
+ * - StructureEnseignement
+ * - Personne
+ * @param pref : les PreferencesEtablissementAbsences
+ * @return une Map associant une personne (élève) au nombre de ces absences
+ */
+ private Map calculeNbAbsencesParEleveEnHeures(BilanAbsencesParPersonneInfo datas,
+ PreferencesEtablissementAbsences pref,
+ List<PlageHoraire> plages) {
+ Float count = 0.0
+ Set<Appel> appels = datas.appels
+ Map nbPlagesParAppel = bilanPasDecompteService.findPlagesCountForAllAppels(appels*.id)
+
+ Map result = [:]
+ datas.eachAbsence { Personne personne, BilanAbsenceInfo bilanAbsenceInfo ->
+ Appel appel = bilanAbsenceInfo.appelLigne.appel
+ AppelLigne appelLigne = bilanAbsenceInfo.appelLigne
+ Personne eleve = personne
+ Integer nbPlages
+ if (appel) {
+ nbPlages = (Integer) nbPlagesParAppel.getAt(appel.id)
+ } else {
+ nbPlages = BilanUtils.calculeNbPlagesForAppelLigne(plages, appelLigne)
+ }
+ count = result.get(eleve) ?: 0
+ count += (Float) nbPlages * pref.longueurPlage
+ result.put(eleve, count)
+ }
+ return result
+ }
+
+ /**
+ * Calcule le nombre d'absences en demi-journées par élève en fonction des
+ * données passées
+ * @param datas : liste dont chaque élément contient
+ * - Appel
+ * - AppelLigne
+ * - StructureEnseignement
+ * - Personne
+ * @return une map associant à chaque Personne élève le nombre de ses
+ * demi-journées d'absence
+ */
+ private Map calculeNbAbsencesParEleveEnDemiJours(BilanAbsencesParPersonneInfo datas,
+ List<PlageHoraire> plages) {
+ Set<Appel> appels = datas.appels
+ Map demiJoursParAppel = bilanPasDecompteService.findAllDemiJoursForAllAppels(appels*.id)
+
+ Map result = [:]
+ Calendar cal = Calendar.getInstance()
+
+ datas.eachListeTrieeAbsencesParPersonne {
+ Personne personne, List<BilanAbsenceInfo> listeBilanAbsencesInfo ->
+ Date lastJour = null
+ Byte lastDemiJour = null
+ Date currentJour = null
+ Byte currentDemiJour = null
+ Byte nbDemiJours
+ Byte demiJours
+ Integer count = 0
+
+ listeBilanAbsencesInfo.sort {
+ BilanAbsenceInfo b1, BilanAbsenceInfo b2 -> b1.heureDebut <=> b2.heureDebut
+ }.each {
+ BilanAbsenceInfo info ->
+ AppelLigne appelLigne = info.appelLigne
+ Appel appel = info.appelLigne.appel
+
+ if (appel) {
+ cal.setTime(appel.dateHeureDebut)
+ demiJours = (Byte) demiJoursParAppel.getAt(appel.id)
+ nbDemiJours = 1
+ if (demiJours == MATIN_ET_APRES_MIDI) {
+ nbDemiJours = 2
+ }
+ } else {
+ nbDemiJours = bilanPasDecompteService.getNbDemiJoursForAppelLigne(appelLigne, plages)
+ demiJours = bilanPasDecompteService.getLastDemiJourForAppelLigne(appelLigne, plages)
+ cal.setTime(appelLigne.absenceJournee.date)
+ }
+
+ DateUtil.setDebutJour(cal)
+ currentJour = cal.getTime()
+ cal.clear()
+ currentDemiJour = demiJours
+ if (currentDemiJour == MATIN_ET_APRES_MIDI) {
+ currentDemiJour = APRES_MIDI
+ } else {
+ if (currentDemiJour == AUCUN) {
+ throw new IllegalStateException(
+ "L'appel ne porte ni sur le matin, ni sur l'après-midi."
+ )
+ }
+ }
+
+ if (currentJour != lastJour ||
+ currentDemiJour != lastDemiJour) {
+
+ /**
+ * Pour une absence qui s'étale sur 2 1/2 journées (matin et après-midi),
+ * on vérifie que l'absence précédente ne date pas du matin même,
+ * auquel cas, la nouvelle absence ne compte que pour l'après-midi
+ */
+ if (nbDemiJours == 2) {
+ if (currentJour == lastJour && lastDemiJour == MATIN) {
+ count += 1
+ } else {
+ count += 2
+ }
+ } else {
+ count += nbDemiJours
+ }
+ }
+
+ lastJour = currentJour
+ lastDemiJour = currentDemiJour
+
+ }
+ result.put(personne, count)
+ }
+ return result
+ }
+
+ /**
+ * Calcule le nombre d'absences en journées par élève en fonction des
+ * données passées
+ * @param datas : liste dont chaque élément contient
+ * - Appel
+ * - AppelLigne
+ * - StructureEnseignement
+ * - Personne
+ * @return une map associant à chaque Personne élève le nombre de ses
+ * journées d'absence
+ */
+ private Map calculeNbAbsencesParEleveEnJours(BilanAbsencesParPersonneInfo datas) {
+ Integer count = 0
+
+ Map result = [:]
+ Date lastJour = null
+ Date currentJour = null
+ Calendar cal = Calendar.getInstance()
+
+ datas.eachListeTrieeAbsencesParPersonne {
+ Personne personne, List<BilanAbsenceInfo> listeBilanAbsencesInfo ->
+ count = 0
+ listeBilanAbsencesInfo.each {
+ BilanAbsenceInfo info ->
+ AppelLigne appelLigne = info.appelLigne
+ Appel appel = info.appelLigne.appel
+
+ if (appel) {
+ cal.setTime(appel.dateHeureDebut)
+ } else {
+ cal.setTime(appelLigne.absenceJournee.date)
+ }
+ DateUtil.setDebutJour(cal)
+ currentJour = cal.getTime()
+ cal.clear()
+
+ if (currentJour != lastJour) {
+ count += 1
+ }
+ lastJour = currentJour
+ }
+ result.put(personne, count)
+ }
+ return result
+ }
+
+}