diff --git a/src/main/java/org/olat/modules/lecture/AbsenceNoticeSearchParameters.java b/src/main/java/org/olat/modules/lecture/AbsenceNoticeSearchParameters.java index 2bebf1e58d812eca178101b61c82f4d9e38dedc5..ea5d603f73db595c8d8217a67013acca817f6ef9 100644 --- a/src/main/java/org/olat/modules/lecture/AbsenceNoticeSearchParameters.java +++ b/src/main/java/org/olat/modules/lecture/AbsenceNoticeSearchParameters.java @@ -70,6 +70,7 @@ public class AbsenceNoticeSearchParameters { if(types == null || types.isEmpty()) { typesList.clear(); } else { + typesList.clear(); typesList.addAll(types); } } diff --git a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java index 516718c238a68a0e20407f41a3dcc21be2b1636a..e98615051a5b892fd40165e999027df4e4747274 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java @@ -225,9 +225,11 @@ public class AbsenceNoticeDAO { .append("(notice.startDate>=:startDate and notice.startDate<=:endDate)") .append(" or ") .append("(notice.endDate>=:startDate and notice.endDate<=:endDate)") - .append(" or ") - .append("(notice.startDate is null and notice.endDate is null)") .append(")"); + } else if(searchParams.getStartDate() != null) { + sb.and().append(" notice.startDate>=:startDate"); + } else if(searchParams.getEndDate() != null) { + sb.and().append(" notice.endDate<=:endDate"); } if(searchParams.getParticipant() != null) { diff --git a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java index 0044ee5ba8a5ac87696af96bc766ae11c047c55e..ef8a58fa2736a233878c36976ddb2c52a551d3f1 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java @@ -53,16 +53,18 @@ public class AbsenceNoticeToLectureBlockDAO { return rel; } - public List<LectureBlockRollCall> getRollCallsByLectureBlock(AbsenceNotice notice) { + public List<LectureBlockRollCall> searchRollCallsByLectureBlock(AbsenceNotice notice) { QueryBuilder sb = new QueryBuilder(); sb.append("select rollCall from absencenoticetolectureblock noticeToBlock") .append(" inner join noticeToBlock.lectureBlock as block") .append(" inner join lectureblockrollcall as rollCall on (rollCall.lectureBlock.key=block.key)") - .append(" where noticeToBlock.absenceNotice.key=:noticeKey"); + .append(" left join fetch rollCall.absenceNotice as currentNotice") + .append(" where rollCall.identity.key=:identityKey and noticeToBlock.absenceNotice.key=:noticeKey"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), LectureBlockRollCall.class) .setParameter("noticeKey", notice.getKey()) + .setParameter("identityKey", notice.getIdentity().getKey()) .getResultList(); } diff --git a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java index 931ab2b22b8b4f9d8ba4eb001a174dd1b1746785..588fce843cfb7f5c1627ba8b64f4dbeb255c2618 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java @@ -24,6 +24,7 @@ import java.util.List; import javax.persistence.TemporalType; +import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.QueryBuilder; import org.olat.modules.lecture.AbsenceNotice; @@ -82,20 +83,29 @@ public class AbsenceNoticeToRepositoryEntryDAO { * @param notice * @return */ - public List<LectureBlockRollCall> getRollCallsByRepositoryEntry(AbsenceNotice notice) { + public List<LectureBlockRollCall> searchRollCallsByRepositoryEntry(AbsenceNotice notice) { QueryBuilder sb = new QueryBuilder(); sb.append("select rollCall from absencenoticetoentry noticeToEntry") .append(" inner join noticeToEntry.entry as entry") .append(" inner join noticeToEntry.absenceNotice as notice") .append(" inner join lectureblock as block on (block.entry.key=entry.key)") .append(" inner join lectureblockrollcall as rollCall on (rollCall.lectureBlock.key=block.key)") - .append(" inner join fetch rollCall.absenceNotice as currentNotice") - .append(" where notice.key=:noticeKey and "); - AbsenceNoticeDAO.noticeBlockDates(sb); + .append(" left join fetch rollCall.absenceNotice as currentNotice") + .append(" where notice.key=:noticeKey and rollCall.identity.key=:identityKey") + .append(" and (") + .append(" (block.startDate>=:startDate and block.endDate<=:endDate)") + .append(" or ") + .append(" (block.startDate<=:startDate and block.startDate>=:endDate)") + .append(" or ") + .append(" (block.endDate<=:startDate and block.endDate>=:endDate)") + .append(" )"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), LectureBlockRollCall.class) .setParameter("noticeKey", notice.getKey()) + .setParameter("identityKey", notice.getIdentity().getKey()) + .setParameter("startDate", notice.getStartDate()) + .setParameter("endDate", notice.getEndDate()) .getResultList(); } @@ -104,11 +114,11 @@ public class AbsenceNoticeToRepositoryEntryDAO { * @param notice * @return */ - public List<LectureBlockRollCall> getRollCallsOfAllEntries(AbsenceNotice notice) { + public List<LectureBlockRollCall> searchRollCallsOfAllEntries(IdentityRef identity, Date start, Date end) { QueryBuilder sb = new QueryBuilder(); - sb.append("select rollCall from lectureblock block") - .append(" inner join lectureblockrollcall as rollCall on (rollCall.lectureBlock.key=block.key)") - .append(" inner join fetch rollCall.absenceNotice as currentNotice") + sb.append("select rollCall from lectureblockrollcall as rollCall") + .append(" inner join rollCall.lectureBlock as block") + .append(" left join fetch rollCall.absenceNotice as currentNotice") .append(" where rollCall.identity.key=:identityKey") .append(" and (") .append(" (block.startDate>=:startDate and block.endDate<=:endDate)") @@ -120,9 +130,9 @@ public class AbsenceNoticeToRepositoryEntryDAO { return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), LectureBlockRollCall.class) - .setParameter("identityKey", notice.getIdentity().getKey()) - .setParameter("startDate", notice.getStartDate(), TemporalType.TIMESTAMP) - .setParameter("endDate", notice.getEndDate(), TemporalType.TIMESTAMP) + .setParameter("identityKey", identity.getKey()) + .setParameter("startDate", start, TemporalType.TIMESTAMP) + .setParameter("endDate", end, TemporalType.TIMESTAMP) .getResultList(); } diff --git a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java index 3d58ecdce90b4015086dccc50a0334f3b2ff8cd8..21b61c73382a0ff7577002f78b9eb5e4da4e9fad 100644 --- a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java +++ b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java @@ -662,15 +662,22 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De } Set<LectureBlockRollCall> currentRollCallSet = new HashSet<>(currentRollCalls); + IdentityRef identity = notice.getIdentity(); + Date start = notice.getStartDate(); + Date end = notice.getEndDate(); + List<LectureBlockRollCall> rollCalls; switch(notice.getNoticeTarget()) { - case lectureblocks: rollCalls = absenceNoticeToLectureBlockDao.getRollCallsByLectureBlock(notice); break; - case entries: rollCalls = absenceNoticeToRepositoryEntryDao.getRollCallsByRepositoryEntry(notice); break; - case allentries: rollCalls = absenceNoticeToRepositoryEntryDao.getRollCallsOfAllEntries(notice); break; + case lectureblocks: rollCalls = absenceNoticeToLectureBlockDao.searchRollCallsByLectureBlock(notice); break; + case entries: rollCalls = absenceNoticeToRepositoryEntryDao.searchRollCallsByRepositoryEntry(notice); break; + case allentries: rollCalls = absenceNoticeToRepositoryEntryDao.searchRollCallsOfAllEntries(identity, start, end); break; default: rollCalls = Collections.emptyList(); } for(LectureBlockRollCall rollCall:rollCalls) { + if(!rollCall.getIdentity().equals(notice.getIdentity())) { + continue;// this is a paranoia check, the search methods already do this + } if(currentRollCallSet.contains(rollCall)) { currentRollCallSet.remove(rollCall); } else { diff --git a/src/main/java/org/olat/modules/lecture/model/EditAbsenceNoticeWrapper.java b/src/main/java/org/olat/modules/lecture/model/EditAbsenceNoticeWrapper.java index c5d60a7a1f981813e446abc409bd735c420b1102..51e08888df5e0e8bf6b73ce68a7ba670e57261f1 100644 --- a/src/main/java/org/olat/modules/lecture/model/EditAbsenceNoticeWrapper.java +++ b/src/main/java/org/olat/modules/lecture/model/EditAbsenceNoticeWrapper.java @@ -67,6 +67,10 @@ public class EditAbsenceNoticeWrapper { private List<VFSItem> attachmentsToDelete = new ArrayList<>(); private EditAbsenceNoticeWrapper(AbsenceNotice notice) { + wrap(notice); + } + + public void wrap(AbsenceNotice notice) { absenceNotice = notice; identity = notice.getIdentity(); startDate = notice.getStartDate(); diff --git a/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java b/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java index 991db9200704af9d84778c287ced0a38d0589c16..f01f9b808905715e754ce1c1faeb6137a1b8a52c 100644 --- a/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java +++ b/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java @@ -205,6 +205,10 @@ public class AppealListRepositoryController extends FormBasicController { loadModel(true); } + public void reloadModel() { + loadModel(false); + } + protected void loadModel(boolean reset) { searchParams.setEntry(entry); searchParams.setCalledIdentity(profiledIdentity); diff --git a/src/main/java/org/olat/modules/lecture/ui/ParticipantLecturesOverviewController.java b/src/main/java/org/olat/modules/lecture/ui/ParticipantLecturesOverviewController.java index 72aa51bf7f995bc8ee24351c0a249a960960b70d..a8ff169cadbdf881354882420523671784bc1c64 100644 --- a/src/main/java/org/olat/modules/lecture/ui/ParticipantLecturesOverviewController.java +++ b/src/main/java/org/olat/modules/lecture/ui/ParticipantLecturesOverviewController.java @@ -310,7 +310,7 @@ public class ParticipantLecturesOverviewController extends FormBasicController i return new ArrayList<>(results.values()); } - private void loadModel() { + public void loadModel() { List<LectureBlockStatistics> statistics = lectureService.getParticipantLecturesStatistics(assessedIdentity); if(filterByEntries != null && !filterByEntries.isEmpty()) { Set<Long> acceptedEntries = filterByEntries.stream() diff --git a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties index 00becb55136e6c185ef4479a18688d15415d0ff2..736806738d013f76c7a3a5387d3a30b3c666432f 100644 --- a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties @@ -324,6 +324,7 @@ planned.lectures=Geplante Lektionen previous.participant=Zur\u00FCck zum letzten Teilnehmer private.dates=$org.olat.repository\:cif.private.dates profile=Profil +prolongate.notice=Verlängern public.dates=$org.olat.repository\:cif.public.dates rate.error.title=Die Anwesenheitsquote liegt unter dem erforderlichen Limit. rate.warning.title=Die Anwesenheitsquote ist nah am erforderlichen Limit. diff --git a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties index 2ee51ee8aa577f83ea9f546be1adf8f51e71e239..87aef9093b459fb69635b185de90ef9105ed689e 100644 --- a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties @@ -323,6 +323,7 @@ planned.lectures=Planned lectures previous.participant=Back to previous participant private.dates=$org.olat.repository\:cif.private.dates profile=Profile +prolongate.notice=Prolong public.dates=$org.olat.repository\:cif.public.dates rate.error.title=The attendance rate is under the mandatory limit. rate.warning.title=The attendance rate is close to the mandatory limit. diff --git a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties index c3e515a28f53fd506acdd631b8de8f6dd42bb66f..d2a6f0e2141a1ef7e6838466bed772152c01a781 100644 --- a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties @@ -1,14 +1,22 @@ -#Wed Aug 21 10:55:15 CEST 2019 +#Wed Aug 21 18:47:34 CEST 2019 absence.category=Raison de l'absence absence.category.copied=La raison de l'absence a \u00E9t\u00E9 copi\u00E9 avec succ\u00E8s. absence.category.copy={0} (copie) absence.category.description=Description absence.category.in.use=Cette raison d'absence est encore utilis\u00E9 par certaines notices d'absences et ne peut pas \u00EAtre effac\u00E9e. absence.category.title=Raison de l'absence +absences.batch.authorize=Excuser des absences absences.title=Liste d'absences active=Actif +add.absence=Saisir une nouvelle absence +add.absence.category=Ajouter une justification d'absence +add.absence.title=Saisir une absence add.assessment.mode=Marqu\u00E9 comme \u00E9valuation +add.dispensation=Saisir une nouvelle dispense +add.dispensation.title=Saisir une dispense add.lecture=Cr\u00E9er un nouveau cours bloc +add.notice.absence=Saisir un nouvel avis d'absence +add.notice.absence.title=Saisir un avis d'absence add.reason=Cr\u00E9er une justification admin.menu.title=Cours blocs admin.menu.title.alt=Administration des cours blocs et absences @@ -42,6 +50,7 @@ appeals.title=Liste de recours appeals.update.title=Edit {0} recours archive.entry=Archivage assessment.tool=Outil d'\u00E9valuation +attachment.upload=Certificat m\u00E9dical, justification, etc attendance.list=Liste d'absences attendance.list.title=Liste d'absence\: {0} attendance.list.to.sign=Liste de pr\u00E9sence @@ -55,16 +64,25 @@ bulk.hint=Vous pouvez entrer une liste de noms d'utilisateurs ou d'adresses cour cancel.lecture.blocks=Annuler le cours bloc cancelled=Annul\u00E9 close.lecture.blocks=Termin\u00E9 le cours bloc +close.lecture.blocks.confirmation={0} cours seront clos le {1}. +close.lecture.blocks.day=Clore les absences de {0} closed=Termin\u00E9 coach.absence=Absences coach.appeals=Recours coach.cockpit=Cockpit +coach.dispensation=Avis d'absences / dispenses coach.lectures=Cours +coach.lectures.search=Recherche d'utilisateurs +coach.report=Rapport cockpit.absences=Absences cockpit.date=pour {0} +cockpit.day.overview=Aper\u00E7u journalier cockpit.lectures=Cours cockpit.lectures.day.list=Vous n'avez pas de cours ce jour-l\u00E0. cockpit.pending.day={0} {1} ({2} cours) +cockpit.pending.day.plural={0} {1} ({2} cours) +cockpit.pending.days=Un certain nombre de cours n'ont pas \u00E9t\u00E9 clos. Enregistrez s'il vous pla\u00EEt les absences pour la journ\u00E9e suivante\: +cockpit.pending.days.plural=Un certain nombre de cours n'ont pas \u00E9t\u00E9 clos. Enregistrez s'il vous pla\u00EEt les absences pour les jours suivants\: config.calculate.attendance.rate=Calculer le taux de pr\u00E9sence config.override=Permettre de modifier la configuration standard config.override.no=Non @@ -73,10 +91,13 @@ config.rollcall.enabled=Activer le contr\u00F4le du taux de pr\u00E9sence config.sync.course.calendar=Synchroniser le calendrier du cours config.sync.participant.calendar=Synchroniser le calendrier des participants config.sync.teacher.calendar=Synchroniser le calendrier des charg\u00E9s de cours +confirm.delete.absence.category=Voulez-vous vraiment effacer la justification d'absence "{0}"? confirm.delete.assessment.mode.text=Voulez-vous vraiment effacer l'examen de ce cours bloc "{0}"? confirm.delete.assessment.mode.title=Effacer l'examen confirm.delete.lectures=Voulez-vous vraiment effacer ce cours block "{0}"? confirm.delete.reason=Voulez-vous vraiment effacer cette justification "{0}"? +contact.teachers=Contacter les charg\u00E9s de cours +contact.teachers.list.name=Charg\u00E9s de cours copy=Copier current.lecture=Cours bloc actuel date.end=$org.olat.repository\:cif.date.end @@ -90,11 +111,18 @@ delete.lectures.title=Effacer un cours bloc delete.title=Effacer une justification detailled.list=Liste d\u00E9taill\u00E9e details=D\u00E9tails +dispensations.title=Avis d'absences / dispenses done=Termin\u00E9 +edit.absence.category=Editer la justification d'absence edit.assessment.mode=Editer la configuration d'\u00E9valuation edit.participant.rate=Editer la valeur seuil personalis\u00E9e edit.reason=Editer la justification +edit.type.absence=Editer l'absence +edit.type.dispensation=Editer la dispense +edit.type.notice.absence=Editer l'avis d'absence effective.lectures=Le\u00E7ons effectives +empty.absences.list=Il n'y a pas d'absences ou de dispenses qui correspondent \u00E0 vos crit\u00E8res de recherche. +empty.appeals.list=Il n'y a pas de recours qui correspondent \u00E0 vos crit\u00E8res de recherche. empty.lectures.list=La liste est vide empty.repository.entry.lectures=Vous n'avez pas encore de le\u00E7ons dans ce cours. empty.table.current.lectures.blocks=Vous n'avez aucun cours bloc actuellement. @@ -103,10 +131,13 @@ empty.table.lectures.blocks.admin=Aucun cours bloc n'a \u00E9t\u00E9 cr\u00E9\u0 empty.table.participant.list=Aucun contr\u00F4le de pr\u00E9sence n'a \u00E9t\u00E9 effectu\u00E9 pour ce cours jusqu'\u00E0 maintenant. entry.rate=Valeur seuil pour le cours error.atleastone.lecture=Choisissez au moins un cours bloc s'il vous pla\u00EEt. +error.collision=Il y a d\u00E9j\u00E0 une absence pour ces dates. error.integer.between=La valeur doit \u00EAtre un nombre entre {0} et {1} error.integer.positive=La valeur doit \u00EAtre un nombre positif. error.reason.mandatory=La justification est requis. error.search.form.notempty=Veuillez remplir au minimum un champ du formulaire avec deux caract\u00E8res s'il vous pla\u00EEt. +error.unauthorized.absence.msg=<strong>Une</strong> absence non-excus\u00E9e a \u00E9t\u00E9 trouv\u00E9e. +error.unauthorized.absences.msg=<strong>{0}</strong> absences non-excus\u00E9es ont \u00E9t\u00E9 trouv\u00E9es. evening=Soir\u00E9e export.footer.lectures.hint=x \= Absence export.header.entry=Cours\: {0} @@ -156,6 +187,9 @@ lecture.can.override.standard.configuration=Permettre de modifier la configurati lecture.compulsory=Pr\u00E9sence obligatoire lecture.count.authorized.absence.attendant=Compter les absences excus\u00E9es comme pr\u00E9sent lecture.course.admin.title=Configuration des cours blocs et absences du cours +lecture.daily.batch.absence=Saisie journali\u00E8re des absences +lecture.daily.batch.absence.day=Oui, autoriser la saisie des absences de tous les cours blocs d'une journ\u00E9e +lecture.daily.batch.absence.start=Non, au d\u00E9but des cours blocs seulement lecture.date=Date lecture.deleted=Cours bloc a \u00E9t\u00E9 effac\u00E9 avec succ\u00E8s. lecture.descr=Description @@ -164,7 +198,13 @@ lecture.from.to=Depuis lecture.from.to.format={0} jusqu'\u00E0 {1} lecture.groups=Cours / groupes / curriculum lecture.location=Lieu +lecture.mastercoach.can.authorize.absence=Les professeurs peuvent excuser les absences +lecture.mastercoach.can.authorize.appeal=Les professeurs peuvent octroyer un recours +lecture.mastercoach.can.record.notice=Les professeurs peuvent saisir les avis d'absences +lecture.mastercoach.can.see.absence=Les professeurs peuvent voir les absences +lecture.mastercoach.can.see.appeal=Les professeurs peuvent voir les recours lecture.owner.can.view.all.curriculum.elements=Les propri\u00E9taires peuvent voir tous les cours de l'\u00E9l\u00E9ment de curriculum +lecture.participant.can.notice=Les participants peuvent notifier une absence lecture.preparation=Pr\u00E9paration/suivi lecture.reminder.enabled=Activ\u00E9 la fonction de rappel lecture.reminder.period=D\u00E9lai de rappel @@ -178,12 +218,17 @@ lecture.status.partially.done.enabled=Activer la possibilit\u00E9 de le\u00E7ons lecture.taxonomy=Taxonomie lecture.teacher=Charg\u00E9s de cours lecture.teacher.can.authorize.absence=Les charg\u00E9s de cours peuvent excuser les absences +lecture.teacher.can.authorize.appeal=Les charg\u00E9s de cours peuvent octroyer un recours +lecture.teacher.can.see.appeal=Les charg\u00E9s de cours peuvent voir les recours lecture.teacher.reminder.body=<p>Ch\u00E8re charg\u00E9e de cours<br>Cher charg\u00E9 de cours</p><p>Nous aimerions vous rappeler que vous devez encore noter les absences pour un cours bloc. Avec le lien ci-dessous vous m\u00E8nera directement au cours bloc en question\:<br><a href\='{3}'>{3}</a></p><p>Merci beaucoup\!</p><p>Votre administrateur de cours</p> lecture.teacher.reminder.subject=Rappel cours bloc "{0}" lecture.time=Temps lecture.time.from=de lecture.time.until=jusqu'\u00E0 lecture.title=Titre +lectures.admin.absences.categories=Justifications d'absences +lectures.admin.permissions=Autorisations +lectures.admin.permissions.title=Autorisations pour charg\u00E9s de cours et professeurs lectures.admin.reasons=Justifications absences lectures.admin.report=Rapport cours blocs lectures.admin.settings=Configuration de l'administration des cours blocs et des absences @@ -202,6 +247,8 @@ log.change.admission.date=Date d'inscription modifi\u00E9e\: {0} log.change.rate=Valeur seuil modifi\u00E9e\: {0} log.remove.admission.date=Date d'inscription enlev\u00E9e log.remove.rate=Valeur seuil enlev\u00E9e +mail.body=Message +mail.subject=Sujet managed.flags.lecture.block.all=Gestion externe compl\u00E8te managed.flags.lecture.block.compulsory=Obligatoire managed.flags.lecture.block.dates=Date @@ -218,26 +265,50 @@ managed.flags.lecture.block.title=Titre menu.my.lectures=Absences menu.my.lectures.alt=Cours blocs et absences morning=Matin\u00E9e +multi.rollcall.callee.cmc.title=Saisie multiple des absences de cours <small>pour {0} ({1} cours blocs)</small> +multi.rollcall.callee.title=Aper\u00E7u journalier multi.rollcall.callee.title.for=pour {0} +multi.rollcall.title=Saisie multiple de cours +multi.rollcall.title.for=pour {0} ({1} cours blocks) new.appeal.title=Nouvelle notification de recours next.participant=Participant suivant no.teachers=Pas encore de charg\u00E9 de cours disponible +not.authorized.absence=Injustifi\u00E9 +notice.dates.lectures.title=Contexte +notice.end.date=Date de fin notice.reason.title=Raison de l'absence +notice.search=Recherche +notice.start.date=Date de d\u00E9but +noticed.absence.authorized=Excus\u00E9 noticed.absence.unauthorized=Absent +noticed.autorized.yes=Excus\u00E9 +noticed.category=Justification +noticed.dispensation=Dispense noticed.duration=Dur\u00E9e +noticed.duration.days=Choix de la date +noticed.duration.exact=Exact (avec l'heure) noticed.duration.today=Aujourd'hui noticed.entries=Cours noticed.identity=Personne absente noticed.lectures=Cours-blocs +noticed.notice.absence=Avis d'absence noticed.reason=Commentaire noticed.target.all=Tous les cours noticed.target.courses=Cours choisis noticed.target.lectureblocks=Cours-blocs choisis +noticed.targets=Absent pour noticed.teachers=Charg\u00E9s de cours noticed.till=jusqu'\u00E0 noticed.type=Type +noticed.type.absence=Sans avis pr\u00E9alable +noticed.type.dispensation=Dispens\u00E9 +noticed.type.notified=Avis d'absence +num.alert.absence.explain={0} absence(s) injustifi\u00E9e(s) +num.warnings.dispensation.explain={0} dispense(s) injustifi\u00E9e(s) +num.warnings.notice.absence.explain={0} avis d'absence(s) injustifi\u00E9e(s) open=Ouvert open.course=Cours ouvert +open.specific.course=Ouvrir "{0}" override.lecture=Passer outre la gestion ext\u00E9rieure du cours partiallydone=En partie termin\u00E9 participant.rate=Valeur seuil @@ -253,6 +324,7 @@ planned.lectures=Cours blocs pr\u00E9vus previous.participant=Participant pr\u00E9c\u00E9dent private.dates=$org.olat.repository\:cif.private.dates profile=Profil +prolongate.notice=Prolonger public.dates=$org.olat.repository\:cif.public.dates rate.error.title=Le taux de pr\u00E9sence se situe sous la limite obligatoire. rate.warning.title=Le taux de pr\u00E9sence est proche de la limite obligatoire. @@ -275,8 +347,10 @@ repo.settings=Configuration results=R\u00E9sultats rollcall=Contr\u00F4le de pr\u00E9sence rollcall.absence=Absence +rollcall.absence.notice=Avis d'absence rollcall.coach.hint=Note\: les absences sont enregistr\u00E9es pour chaque le\u00E7on. Si plus de la moiti\u00E9 d'une le\u00E7on est manqu\u00E9e, elle sera consid\u00E9r\u00E9e comme une absence. rollcall.comment=Remarque +rollcall.dispensation=Dispense rollcall.status=Status du cours bloc rollcall.tooltip.absence=Absence rollcall.tooltip.authorized.absence=Absence excus\u00E9e @@ -306,6 +380,7 @@ search.teachers=Recherche par charg\u00E9s de cours send=Envoyer several.entries=Plusieurs site.title=Gestion des cours blocs +site.title.alt=Gestion des cours blocs start.desktop=Bureau start.label=Noter les absences start.mobile=Mobile @@ -314,6 +389,8 @@ sync.course.calendar.enabled=Synchroniser le calendrier du cours sync.participants.calendar.enabled=Synchroniser le calendrier des participants sync.teachers.calendar.enabled=Synchroniser le calendrier des charg\u00E9s de cours table.header.absence=Pr\u00E9sences +table.header.absences.alert=Nombre d'absences injustifi\u00E9es +table.header.absences.warning=Nombre de dispenses et avis d'absences injustifi\u00E9es table.header.absent.lectures=Absences table.header.actions=<i class\='o_icon o_icon_actions o_icon-lg'> </i> table.header.appeal.status=Recours @@ -361,6 +438,7 @@ table.header.log.effective.lectures=Le\u00E7ons effectives table.header.log.planned.lectures=Le\u00E7ons pr\u00E9vues table.header.log.user=Utilisateur table.header.notice.type=Type +table.header.num.absences=Nombre de participants absents table.header.num.lecture.block=Cours table.header.num.participants=Nombre de participants table.header.num.presences=Nombre de participants pr\u00E9sents @@ -391,9 +469,13 @@ tool.teacher=Comme charg\u00E9 de cours tools=Action tools.import.table=Importer des cours blocs depuis Excel total=Total +unauthorized.filter=Injustifi\u00E9 +unauthorized.filter.label=Afficher unoverride.lecture=Retour en gestion ext\u00E9rieur upto=jusqu'\u00E0 {0} user.overview.appeals=Recours +user.overview.dispensation=Dispenses +user.overview.lectures=Cours et absences user.profil=Profil de l'utilisateur warning.choose.at.least.one.appeal=S'il-vous-pla\u00EEt, s\u00E9lectionnez au moins un recours. warning.edit.lecture=Le contr\u00F4le d'absence est d\u00E9sactiv\u00E9. diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java b/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java index 9744dbd22f3750abc5cbefd1717bdb09aa3d754f..aa7d782f283dac64c3d508f9376818b865f92536 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java @@ -60,6 +60,7 @@ public class AbsencesController extends BasicController { private Link addAbsenceButton; private VelocityContainer mainVC; + private final Roles roles; private final LecturesSecurityCallback secCallback; private final AbsenceNoticeSearchParameters searchParams = new AbsenceNoticeSearchParameters(); @@ -74,6 +75,7 @@ public class AbsencesController extends BasicController { super(ureq, wControl, Util.createPackageTranslator(LectureRepositoryAdminController.class, ureq.getLocale())); this.secCallback = secCallback; + this.roles = ureq.getUserSession().getRoles(); searchParams.addTypes(AbsenceNoticeType.absence); searchParams.setViewAs(getIdentity(), ureq.getUserSession().getRoles(), secCallback.viewAs()); searchParams.setLinkedToRollCall(true); @@ -95,14 +97,13 @@ public class AbsencesController extends BasicController { putInitialPanel(mainVC); noticesListCtlr.loadModel(searchParams); - loadUnauthorizedAbsences(ureq); + loadUnauthorizedAbsences(); } - private void loadUnauthorizedAbsences(UserRequest ureq) { + private void loadUnauthorizedAbsences() { AbsenceNoticeSearchParameters unauthorizedSearchParams = new AbsenceNoticeSearchParameters(); unauthorizedSearchParams.addTypes(AbsenceNoticeType.absence); unauthorizedSearchParams.setLinkedToRollCall(true); - Roles roles = ureq.getUserSession().getRoles(); unauthorizedSearchParams.setViewAs(getIdentity(), roles, secCallback.viewAs()); List<AbsenceNoticeInfos> unauthorizedAbsences = lectureService.searchAbsenceNotices(unauthorizedSearchParams); @@ -152,6 +153,11 @@ public class AbsencesController extends BasicController { } } + protected void reloadModels() { + noticesListCtlr.reloadModel(); + loadUnauthorizedAbsences(); + } + private void doSearch(SearchAbsenceNoticeEvent event) { searchParams.setStartDate(event.getStartDate()); searchParams.setEndDate(event.getEndDate()); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/AppealsController.java b/src/main/java/org/olat/modules/lecture/ui/coach/AppealsController.java index 720394980d725bbecb3cdbde5598916c6e157f06..787ebc3c3254f3fde28c6657be07982f3a497cd5 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/AppealsController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/AppealsController.java @@ -96,6 +96,10 @@ public class AppealsController extends BasicController { } } + protected void reloadModels() { + loadPendentAppeals(); + } + private void loadPendentAppeals() { LectureBlockRollCallSearchParameters searchParams = new LectureBlockRollCallSearchParameters(); searchParams.setAppealStatus(Collections.singletonList(LectureBlockAppealStatus.pending)); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/DailyLectureBlockOverviewController.java b/src/main/java/org/olat/modules/lecture/ui/coach/DailyLectureBlockOverviewController.java index 0f7361b6e5094c89d528ce1cbd712e69ec7f19ae..bf99deff1e38ddfb49137324eaa93d9bcd9d02bc 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/DailyLectureBlockOverviewController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/DailyLectureBlockOverviewController.java @@ -214,7 +214,7 @@ public class DailyLectureBlockOverviewController extends FormBasicController { closeButton.setVisible(canClose); } - private void loadModel() { + protected void loadModel() { LecturesBlockSearchParameters searchParams = new LecturesBlockSearchParameters(); searchParams.setStartDate(CalendarUtils.startOfDay(currentDate)); searchParams.setEndDate(CalendarUtils.endOfDay(currentDate)); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/EditDatesLecturesEntriesController.java b/src/main/java/org/olat/modules/lecture/ui/coach/EditDatesLecturesEntriesController.java index 0d8dd434d1ac5802fc3b0a07c53b13592fb10db4..e0eba14b8a5288611421ad4837ef534bf9871e75 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/EditDatesLecturesEntriesController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/EditDatesLecturesEntriesController.java @@ -32,11 +32,13 @@ import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.DateChooser; +import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; import org.olat.core.gui.components.form.flexible.impl.Form; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.util.KeyValues; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; @@ -74,6 +76,7 @@ public class EditDatesLecturesEntriesController extends FormBasicController { private DateChooser datesEl; private SingleSelection targetsEl; private SingleSelection durationEl; + private FormLink prolongateButton; private MultipleSelectionElement entriesEl; private MultipleSelectionElement lectureBlocksEl; @@ -177,6 +180,11 @@ public class EditDatesLecturesEntriesController extends FormBasicController { datesEl.setSeparator("noticed.till"); datesEl.setMandatory(true); + if(noticeWrapper.getAbsenceNotice() == null) { + prolongateButton = uifactory.addFormLink("prolongate.notice", formLayout, Link.BUTTON); + prolongateButton.setVisible(false); + } + // targets: all, courses, lectureblocks String[] targetValues = new String[] { translate("noticed.target.all"), translate("noticed.target.courses"), translate("noticed.target.lectureblocks") @@ -408,6 +416,9 @@ public class EditDatesLecturesEntriesController extends FormBasicController { allOk &= validate(entriesEl); datesEl.clearError(); + if(prolongateButton != null) { + prolongateButton.setVisible(false); + } if(datesEl.getDate() == null || datesEl.getSecondDate() == null) { datesEl.setErrorKey("form.legende.mandatory", null); allOk &= false; @@ -418,6 +429,10 @@ public class EditDatesLecturesEntriesController extends FormBasicController { if(!notices.isEmpty()) { datesEl.setErrorKey("error.collision", null); allOk &= false; + if(prolongateButton != null) { + prolongateButton.setVisible(true); + prolongateButton.setUserObject(notices); + } } } @@ -440,6 +455,8 @@ public class EditDatesLecturesEntriesController extends FormBasicController { updateDuration(); } else if(targetsEl == source || datesEl == source) { updateTargets(); + } else if(prolongateButton == source) { + doProlongate(); } super.formInnerEvent(ureq, source, event); } @@ -468,6 +485,44 @@ public class EditDatesLecturesEntriesController extends FormBasicController { } } + private void doProlongate() { + Dates dates = getDates(); + List<AbsenceNotice> notices = lectureService.detectCollision(noticedIdentity, + noticeWrapper.getAbsenceNotice(), dates.getStartDate(), dates.getEndDate()); + if(!notices.isEmpty()) { + AbsenceNotice notice = notices.get(0); + noticeWrapper.wrap(notice); + if(notice.getStartDate().before(dates.getStartDate())) { + datesEl.setDate(notice.getStartDate()); + } else { + noticeWrapper.setStartDate(dates.getStartDate()); + } + if(notice.getEndDate().after(dates.getEndDate())) { + datesEl.setSecondDate(notice.getEndDate()); + } else { + noticeWrapper.setEndDate(dates.getEndDate()); + } + updateTargets(); + + Date startDate = noticeWrapper.getStartDate(); + Date endDate = noticeWrapper.getEndDate(); + boolean sameDay = CalendarUtils.isSameDay(startDate, endDate); + boolean startDay = AbsenceNoticeHelper.isStartOfWholeDay(startDate); + boolean endDay = AbsenceNoticeHelper.isEndOfWholeDay(endDate); + + String selectedDurationKey; + if(sameDay && startDay && endDay) { + selectedDurationKey = "today"; + } else if(startDay && endDay) { + selectedDurationKey = "days"; + } else { + selectedDurationKey = "exact"; + } + durationEl.select(selectedDurationKey, true); + updateDuration(); + } + } + public Dates getDates() { Date start = null; Date end = null; diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java index b1c3c330a60f5b4abc2cce9e5ae230605f642231..96f5e01c15aa863d7f11164e73dad2c21d6de4f0 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java @@ -203,6 +203,8 @@ public class LecturesCoachingController extends BasicController implements Activ WindowControl swControl = addToHistory(ureq, OresHelper.createOLATResourceableType("Cockpit"), null); cockpitController = new LecturesCockpitController(ureq, swControl, secCallback); listenTo(cockpitController); + } else { + cockpitController.reloadModels(); } addToHistory(ureq, cockpitController); mainVC.put("segmentCmp", cockpitController.getInitialComponent()); @@ -230,6 +232,8 @@ public class LecturesCoachingController extends BasicController implements Activ WindowControl swControl = addToHistory(ureq, OresHelper.createOLATResourceableType("Absences"), null); absencesController = new AbsencesController(ureq, swControl, getCurrentDate(), secCallback); listenTo(absencesController); + } else { + absencesController.reloadModels(); } addToHistory(ureq, absencesController); mainVC.put("segmentCmp", absencesController.getInitialComponent()); @@ -240,6 +244,8 @@ public class LecturesCoachingController extends BasicController implements Activ WindowControl swControl = addToHistory(ureq, OresHelper.createOLATResourceableType("Dispenses"), null); dispensationsController = new DispensationsController(ureq, swControl, getCurrentDate(), secCallback, true, true); listenTo(dispensationsController); + } else { + dispensationsController.reloadModel(); } addToHistory(ureq, dispensationsController); mainVC.put("segmentCmp", dispensationsController.getInitialComponent()); @@ -250,6 +256,8 @@ public class LecturesCoachingController extends BasicController implements Activ WindowControl swControl = addToHistory(ureq, OresHelper.createOLATResourceableType("Appeals"), null); appealsController = new AppealsController(ureq, swControl, secCallback); listenTo(appealsController); + } else { + appealsController.reloadModels(); } addToHistory(ureq, appealsController); mainVC.put("segmentCmp", appealsController.getInitialComponent()); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCockpitController.java b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCockpitController.java index 01b55cee75b5d0c182e6fedb54f63b99d52bbe2b..83f846b520867ef0699321c47108b1b1846c93cf 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCockpitController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCockpitController.java @@ -188,6 +188,14 @@ public class LecturesCockpitController extends BasicController implements Activa cleanUp(); } + protected void reloadModels() { + if(lectureBlocksCtrl != null) { + lectureBlocksCtrl.loadModel(); + } + absencesListCtrl.reloadModel(); + loadPendingLectureBlocks(); + } + private void doRollCall(UserRequest ureq, List<LectureBlock> lectureBlocks) { if(lectureBlocks.isEmpty()) { // msg diff --git a/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java b/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java index 70c231a1519d23000a76c8fe0247896eabac3397..015194697b6902798764cd5eebeade96c283d95e 100644 --- a/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java +++ b/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java @@ -29,6 +29,7 @@ import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.stack.BreadcrumbedStackedPanel; import org.olat.core.gui.components.tabbedpane.TabbedPane; +import org.olat.core.gui.components.tabbedpane.TabbedPaneChangedEvent; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; @@ -66,6 +67,11 @@ public class IdentityProfileController extends BasicController implements Activa private Link addDispensation; private Link addNoticeOfAbsence; + private final int dailyTab; + private final int lecturesTab; + private final int appealsTab; + private final int dispensationsTab; + private TabbedPane tabPane; private final VelocityContainer mainVC; @@ -118,10 +124,10 @@ public class IdentityProfileController extends BasicController implements Activa // day overview dailyOverviewCtrl = new DailyOverviewProfilController(ureq, getWindowControl(), profiledIdentity, secCallback); listenTo(dailyOverviewCtrl); - tabPane.addTab(translate("cockpit.day.overview"), dailyOverviewCtrl); + dailyTab = tabPane.addTab(translate("cockpit.day.overview"), dailyOverviewCtrl); // list of lectures - tabPane.addTab(translate("user.overview.lectures"), uureq -> { + lecturesTab = tabPane.addTab(translate("user.overview.lectures"), uureq -> { lecturesCtrl = new ParticipantLecturesOverviewController(uureq, getWindowControl(), profiledIdentity, null, true, true, true, true, true, false); listenTo(lecturesCtrl); @@ -133,14 +139,14 @@ public class IdentityProfileController extends BasicController implements Activa }); // dispensation - tabPane.addTab(translate("user.overview.dispensation"), uureq -> { + dispensationsTab = tabPane.addTab(translate("user.overview.dispensation"), uureq -> { dispensationsCtrl = new DispensationsController(uureq, getWindowControl(), null, secCallback, false, false); listenTo(dispensationsCtrl); return dispensationsCtrl.getInitialComponent(); }); // appeals - tabPane.addTab(translate("user.overview.appeals"), uureq -> { + appealsTab = tabPane.addTab(translate("user.overview.appeals"), uureq -> { appealsCtrl = new AppealListRepositoryController(uureq, getWindowControl(), profiledIdentity, secCallback); listenTo(appealsCtrl); return appealsCtrl.getInitialComponent(); @@ -169,6 +175,10 @@ public class IdentityProfileController extends BasicController implements Activa doAddNotice(ureq, AbsenceNoticeType.notified); } else if(addDispensation == source) { doAddNotice(ureq, AbsenceNoticeType.dispensation); + } else if(source == tabPane) { + if(event instanceof TabbedPaneChangedEvent) { + reload(); + } } } @@ -191,6 +201,19 @@ public class IdentityProfileController extends BasicController implements Activa addNoticeCtrl = null; } + private void reload() { + int selectedPane = tabPane.getSelectedPane(); + if(dispensationsCtrl != null && dispensationsTab == selectedPane) { + dispensationsCtrl.reloadModel(); + } else if(dailyOverviewCtrl != null && dailyTab == selectedPane) { + dailyOverviewCtrl.reloadModel(); + } else if(lecturesCtrl != null && lecturesTab == selectedPane) { + lecturesCtrl.loadModel(); + } else if(appealsCtrl != null && appealsTab == selectedPane) { + appealsCtrl.reloadModel(); + } + } + private void updateModels() { if(dispensationsCtrl != null) { dispensationsCtrl.reloadModel(); diff --git a/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java b/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java index 2f7e3574ebe10d26663e59489d0ba7906edf1bd6..ca518b79c841011b4b1fbf36b88e0eefc0ab4cb4 100644 --- a/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java +++ b/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java @@ -19,7 +19,10 @@ */ package org.olat.modules.lecture.manager; +import java.util.Calendar; +import java.util.Collections; import java.util.Date; +import java.util.List; import org.junit.Assert; import org.junit.Test; @@ -31,7 +34,11 @@ import org.olat.modules.lecture.AbsenceNoticeTarget; import org.olat.modules.lecture.AbsenceNoticeToLectureBlock; import org.olat.modules.lecture.AbsenceNoticeType; import org.olat.modules.lecture.LectureBlock; +import org.olat.modules.lecture.LectureBlockRollCall; +import org.olat.modules.lecture.LectureService; +import org.olat.modules.vitero.model.GroupRole; import org.olat.repository.RepositoryEntry; +import org.olat.repository.manager.RepositoryEntryRelationDAO; import org.olat.test.JunitTestHelper; import org.olat.test.OlatTestCase; import org.springframework.beans.factory.annotation.Autowired; @@ -47,10 +54,14 @@ public class AbsenceNoticeToLectureBlockDAOTest extends OlatTestCase { @Autowired private DB dbInstance; @Autowired + private LectureService lectureService; + @Autowired private LectureBlockDAO lectureBlockDao; @Autowired private AbsenceNoticeDAO absenceNoticeDao; @Autowired + private RepositoryEntryRelationDAO repositoryEntryRelationDAO; + @Autowired private AbsenceNoticeToLectureBlockDAO absenceNoticeToLectureBlockDao; @Test @@ -88,4 +99,108 @@ public class AbsenceNoticeToLectureBlockDAOTest extends OlatTestCase { } + @Test + public void searchRollCallsByLectureBlock() { + // make a course, with a participant, with a lecture block, an absence + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + LectureBlock lectureBlock = createMinimalLectureBlock(entry, new Date(), new Date()); + Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-1"); + repositoryEntryRelationDAO.addRole(participant, entry, GroupRole.participant.name()); + AbsenceNotice notice = lectureService.createAbsenceNotice(participant, AbsenceNoticeType.absence, AbsenceNoticeTarget.entries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, null, Collections.singletonList(lectureBlock), null); + dbInstance.commit(); + + LectureBlockRollCall rollCall = lectureService.getOrCreateRollCall(participant, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall); + + dbInstance.commitAndCloseSession(); + + List<LectureBlockRollCall> rollCallList = absenceNoticeToLectureBlockDao.searchRollCallsByLectureBlock(notice); + Assert.assertNotNull(rollCallList); + Assert.assertEquals(1, rollCallList.size()); + Assert.assertTrue(rollCallList.contains(rollCall)); + } + + /** + * The test make sure that the search method returns the roll call + * of the right user, at the right dates, with the right course. It + * adds noise in the form of a second course, second lecture block + * with different dates and a second participant (with a lecture at + * the moment of the search). + * + */ + @Test + public void searchRollCallsByLectureBlock_severalParticipants() { + // make a course, with a participant, with a lecture block, an absence + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 12); + Date startLecture = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY, 14); + Date endLecture = cal.getTime(); + + LectureBlock lectureBlock = createMinimalLectureBlock(entry, startLecture, endLecture); + + cal.add(Calendar.DATE, -7); + cal.set(Calendar.HOUR_OF_DAY, 12); + Date oldStartLecture = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY, 14); + Date oldEndLecture = cal.getTime(); + LectureBlock oldLectureBlock = createMinimalLectureBlock(entry, oldStartLecture, oldEndLecture); + + // second repository entry for some noise + RepositoryEntry entry2 = JunitTestHelper.createAndPersistRepositoryEntry(); + LectureBlock lectureBlockEntry2 = createMinimalLectureBlock(entry2, startLecture, endLecture); + + // memberships + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-2"); + repositoryEntryRelationDAO.addRole(participant1, entry, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant2, entry, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant1, entry2, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant2, entry2, GroupRole.participant.name()); + + AbsenceNotice notice1 = lectureService.createAbsenceNotice(participant1, AbsenceNoticeType.absence, AbsenceNoticeTarget.entries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, null, Collections.singletonList(lectureBlock), null); + AbsenceNotice notice2 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.absence, AbsenceNoticeTarget.entries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, null, Collections.singletonList(lectureBlock), null); + dbInstance.commit(); + Assert.assertNotNull(notice1); + Assert.assertNotNull(notice2); + + // roll call first entry + LectureBlockRollCall rollCall1 = lectureService.getOrCreateRollCall(participant1, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall1); + LectureBlockRollCall rollCall2 = lectureService.getOrCreateRollCall(participant2, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall2); + LectureBlockRollCall oldRollCall1 = lectureService.getOrCreateRollCall(participant1, oldLectureBlock, null, null, null); + Assert.assertNotNull(oldRollCall1); + // roll call second entry + LectureBlockRollCall rollCall1Entry2 = lectureService.getOrCreateRollCall(participant1, lectureBlockEntry2 , null, null, null); + Assert.assertNotNull(rollCall1Entry2); + LectureBlockRollCall rollCall2Entry2 = lectureService.getOrCreateRollCall(participant2, lectureBlockEntry2, null, null, null); + Assert.assertNotNull(rollCall2Entry2); + + dbInstance.commitAndCloseSession(); + + List<LectureBlockRollCall> rollCallList = absenceNoticeToLectureBlockDao.searchRollCallsByLectureBlock(notice1); + Assert.assertNotNull(rollCallList); + Assert.assertEquals(1, rollCallList.size()); + Assert.assertTrue(rollCallList.contains(rollCall1)); + Assert.assertFalse(rollCallList.contains(rollCall2)); + Assert.assertFalse(rollCallList.contains(oldRollCall1)); + } + + private LectureBlock createMinimalLectureBlock(RepositoryEntry entry, Date start, Date end) { + LectureBlock lectureBlock = lectureBlockDao.createLectureBlock(entry); + lectureBlock.setStartDate(start); + lectureBlock.setEndDate(end); + lectureBlock.setTitle("Absence"); + lectureBlock.setPlannedLecturesNumber(4); + lectureBlock.setEffectiveLecturesNumber(4); + return lectureBlockDao.update(lectureBlock); + } } diff --git a/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAOTest.java b/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAOTest.java index 5928d2c8ae4c5296479b6b631cd4b94b60e9a912..2d03775fb79f71fd0f5aa87aad1befbc6bb59923 100644 --- a/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAOTest.java +++ b/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAOTest.java @@ -86,7 +86,7 @@ public class AbsenceNoticeToRepositoryEntryDAOTest extends OlatTestCase { } @Test - public void getRollCalls() { + public void searchRollCallsByRepositoryEntry() { // make a course, with a participant, with a lecture block, an absence RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); LectureBlock lectureBlock = createMinimalLectureBlock(entry, new Date(), new Date()); @@ -102,14 +102,87 @@ public class AbsenceNoticeToRepositoryEntryDAOTest extends OlatTestCase { dbInstance.commitAndCloseSession(); - List<LectureBlockRollCall> rollCallList = absenceNoticeToRepositoryEntryDao.getRollCallsByRepositoryEntry(notice); + List<LectureBlockRollCall> rollCallList = absenceNoticeToRepositoryEntryDao.searchRollCallsByRepositoryEntry(notice); Assert.assertNotNull(rollCallList); Assert.assertEquals(1, rollCallList.size()); Assert.assertTrue(rollCallList.contains(rollCall)); } + /** + * The test make sure that the search method returns the roll call + * of the right user, at the right dates, with the right course. It + * adds noise in the form of a second course, second lecture block + * with different dates and a second participant (with a lecture at + * the moment of the search). + * + */ @Test - public void getRollCallsOfAllEntries() { + public void searchRollCallsByRepositoryEntry_severalParticipants() { + // make a course, with a participant, with a lecture block, an absence + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 12); + Date startLecture = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY, 14); + Date endLecture = cal.getTime(); + + LectureBlock lectureBlock = createMinimalLectureBlock(entry, startLecture, endLecture); + + cal.add(Calendar.DATE, -7); + cal.set(Calendar.HOUR_OF_DAY, 12); + Date oldStartLecture = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY, 14); + Date oldEndLecture = cal.getTime(); + LectureBlock oldLectureBlock = createMinimalLectureBlock(entry, oldStartLecture, oldEndLecture); + + // second repository entry for some noise + RepositoryEntry entry2 = JunitTestHelper.createAndPersistRepositoryEntry(); + LectureBlock lectureBlockEntry2 = createMinimalLectureBlock(entry2, startLecture, endLecture); + + // memberships + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-2"); + repositoryEntryRelationDAO.addRole(participant1, entry, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant2, entry, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant1, entry2, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant2, entry2, GroupRole.participant.name()); + + + AbsenceNotice notice1 = lectureService.createAbsenceNotice(participant1, AbsenceNoticeType.absence, AbsenceNoticeTarget.entries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, Collections.singletonList(entry), null, null); + AbsenceNotice notice2 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.absence, AbsenceNoticeTarget.entries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, Collections.singletonList(entry), null, null); + dbInstance.commit(); + Assert.assertNotNull(notice1); + Assert.assertNotNull(notice2); + + // roll call first entry + LectureBlockRollCall rollCall1 = lectureService.getOrCreateRollCall(participant1, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall1); + LectureBlockRollCall rollCall2 = lectureService.getOrCreateRollCall(participant2, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall2); + LectureBlockRollCall oldRollCall1 = lectureService.getOrCreateRollCall(participant1, oldLectureBlock, null, null, null); + Assert.assertNotNull(oldRollCall1); + // roll call second entry + LectureBlockRollCall rollCall1Entry2 = lectureService.getOrCreateRollCall(participant1, lectureBlockEntry2 , null, null, null); + Assert.assertNotNull(rollCall1Entry2); + LectureBlockRollCall rollCall2Entry2 = lectureService.getOrCreateRollCall(participant2, lectureBlockEntry2, null, null, null); + Assert.assertNotNull(rollCall2Entry2); + + dbInstance.commitAndCloseSession(); + + List<LectureBlockRollCall> rollCallList = absenceNoticeToRepositoryEntryDao.searchRollCallsByRepositoryEntry(notice1); + Assert.assertNotNull(rollCallList); + Assert.assertEquals(1, rollCallList.size()); + Assert.assertTrue(rollCallList.contains(rollCall1)); + Assert.assertFalse(rollCallList.contains(rollCall2)); + Assert.assertFalse(rollCallList.contains(oldRollCall1)); + } + + @Test + public void searchRollCallsOfAllEntries() { // make a course, with a participant, with a lecture block, an absence RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); Calendar cal = Calendar.getInstance(); @@ -123,7 +196,7 @@ public class AbsenceNoticeToRepositoryEntryDAOTest extends OlatTestCase { repositoryEntryRelationDAO.addRole(participant, entry, GroupRole.participant.name()); AbsenceNotice notice = lectureService.createAbsenceNotice(participant, AbsenceNoticeType.absence, AbsenceNoticeTarget.allentries, CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), - null, null, Boolean.TRUE, Collections.singletonList(entry), null, null); + null, null, Boolean.TRUE, null, null, null); dbInstance.commit(); LectureBlockRollCall rollCall = lectureService.getOrCreateRollCall(participant, lectureBlock, null, null, null); @@ -131,12 +204,72 @@ public class AbsenceNoticeToRepositoryEntryDAOTest extends OlatTestCase { dbInstance.commitAndCloseSession(); - List<LectureBlockRollCall> rollCallList = absenceNoticeToRepositoryEntryDao.getRollCallsOfAllEntries(notice); + List<LectureBlockRollCall> rollCallList = absenceNoticeToRepositoryEntryDao + .searchRollCallsOfAllEntries(notice.getIdentity(), notice.getStartDate(), notice.getEndDate()); Assert.assertNotNull(rollCallList); Assert.assertEquals(1, rollCallList.size()); Assert.assertTrue(rollCallList.contains(rollCall)); } + /** + * The test make sure that the search method returns the roll call + * of the right user, at the right dates, with the right course. It + * adds noise in the form of a second course, second lecture block + * with different dates and a second participant (with a lecture at + * the moment of the search). + * + */ + @Test + public void searchRollCallsOfAllEntries_severalParticipants() { + // make a course, with a participant, with a lecture block, an absence + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 12); + Date startLecture = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY, 14); + Date endLecture = cal.getTime(); + + LectureBlock lectureBlock = createMinimalLectureBlock(entry, startLecture, endLecture); + + cal.add(Calendar.DATE, -7); + cal.set(Calendar.HOUR_OF_DAY, 12); + Date oldStartLecture = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY, 14); + Date oldEndLecture = cal.getTime(); + LectureBlock oldLectureBlock = createMinimalLectureBlock(entry, oldStartLecture, oldEndLecture); + + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-2"); + repositoryEntryRelationDAO.addRole(participant1, entry, GroupRole.participant.name()); + repositoryEntryRelationDAO.addRole(participant2, entry, GroupRole.participant.name()); + AbsenceNotice notice1 = lectureService.createAbsenceNotice(participant1, AbsenceNoticeType.absence, AbsenceNoticeTarget.allentries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, null, null, null); + AbsenceNotice notice2 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.absence, AbsenceNoticeTarget.allentries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, null, null, null); + dbInstance.commit(); + Assert.assertNotNull(notice1); + Assert.assertNotNull(notice2); + + LectureBlockRollCall rollCall1 = lectureService.getOrCreateRollCall(participant1, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall1); + LectureBlockRollCall rollCall2 = lectureService.getOrCreateRollCall(participant2, lectureBlock, null, null, null); + Assert.assertNotNull(rollCall2); + LectureBlockRollCall oldRollCall1 = lectureService.getOrCreateRollCall(participant1, oldLectureBlock, null, null, null); + Assert.assertNotNull(oldRollCall1); + + dbInstance.commitAndCloseSession(); + + List<LectureBlockRollCall> rollCallList = absenceNoticeToRepositoryEntryDao + .searchRollCallsOfAllEntries(notice1.getIdentity(), notice1.getStartDate(), notice1.getEndDate()); + Assert.assertNotNull(rollCallList); + Assert.assertEquals(1, rollCallList.size()); + Assert.assertTrue(rollCallList.contains(rollCall1)); + Assert.assertFalse(rollCallList.contains(rollCall2)); + Assert.assertFalse(rollCallList.contains(oldRollCall1)); + } + private LectureBlock createMinimalLectureBlock(RepositoryEntry entry, Date start, Date end) { LectureBlock lectureBlock = lectureBlockDao.createLectureBlock(entry); lectureBlock.setStartDate(start);