package de.dhbw.humbuch.viewmodel;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.hibernate.criterion.LogicalExpression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import com.google.inject.Inject;
import de.davherrmann.mvvm.ActionHandler;
import de.davherrmann.mvvm.BasicState;
import de.davherrmann.mvvm.State;
import de.davherrmann.mvvm.annotations.AfterVMBinding;
import de.davherrmann.mvvm.annotations.HandlesAction;
import de.davherrmann.mvvm.annotations.ProvidesState;
import de.dhbw.humbuch.model.DAO;
import de.dhbw.humbuch.model.entity.BorrowedMaterial;
import de.dhbw.humbuch.model.entity.Grade;
import de.dhbw.humbuch.model.entity.SchoolYear;
import de.dhbw.humbuch.model.entity.SchoolYear.Term;
import de.dhbw.humbuch.model.entity.Student;
import de.dhbw.humbuch.model.entity.Subject;
import de.dhbw.humbuch.model.entity.TeachingMaterial;
/**
* Provides {@link State}s and methods for automatic and manual lending of {@link TeachingMaterial}s.
*
* @author David Vitt
*
*/
public class LendingViewModel {
public interface GenerateMaterialListGrades extends ActionHandler {};
public interface SetBorrowedMaterialsReceived extends ActionHandler {};
public interface DoManualLending extends ActionHandler {};
public interface StudentsWithUnreceivedBorrowedMaterials extends State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> {};
public interface MaterialListGrades extends State<Map<Grade, Map<TeachingMaterial, Integer>>> {};
public interface TeachingMaterials extends State<Collection<TeachingMaterial>> {};
@ProvidesState(StudentsWithUnreceivedBorrowedMaterials.class)
public State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> studentsWithUnreceivedBorrowedMaterials = new BasicState<>(Map.class);
@ProvidesState(MaterialListGrades.class)
public State<Map<Grade, Map<TeachingMaterial, Integer>>> materialListGrades = new BasicState<>(Map.class);
@ProvidesState(TeachingMaterials.class)
public State<Collection<TeachingMaterial>> teachingMaterials = new BasicState<>(Collection.class);
private DAO<Grade> daoGrade;
private DAO<Student> daoStudent;
private DAO<TeachingMaterial> daoTeachingMaterial;
private DAO<BorrowedMaterial> daoBorrowedMaterial;
private DAO<SchoolYear> daoSchoolYear;
private SchoolYear recentlyActiveSchoolYear;
/**
* Constructor
*
* @param daoStudent
* @param daoTeachingMaterial
* @param daoGrade
* @param daoBorrowedMaterial
* @param daoSchoolYear
*/
@Inject
public LendingViewModel(DAO<Student> daoStudent, DAO<TeachingMaterial> daoTeachingMaterial, DAO<Grade> daoGrade,
DAO<BorrowedMaterial> daoBorrowedMaterial, DAO<SchoolYear> daoSchoolYear) {
this.daoStudent = daoStudent;
this.daoTeachingMaterial = daoTeachingMaterial;
this.daoGrade = daoGrade;
this.daoBorrowedMaterial = daoBorrowedMaterial;
this.daoSchoolYear = daoSchoolYear;
}
@AfterVMBinding
public void initialiseStates() {
studentsWithUnreceivedBorrowedMaterials.set(new HashMap<Grade, Map<Student, List<BorrowedMaterial>>>());
materialListGrades.set(new HashMap<Grade, Map<TeachingMaterial, Integer>>());
teachingMaterials.set(new ArrayList<TeachingMaterial>());
}
public void refresh() {
updateSchoolYear();
updateTeachingMaterials();
updateAllStudentsBorrowedMaterials();
}
/**
* Generates "list" of all {@link TeachingMaterial}s required by the given {@link Grade}s.<br>
* The "list" is returned as {@code Map<Grade, Map<TeachingMaterial, Integer>>} in the state {@link MaterialListGrades}
*
* @param selectedGrades {@link Set} of {@link Grade}s
*/
@HandlesAction(GenerateMaterialListGrades.class)
public void generateMaterialListGrades(Set<Grade> selectedGrades) {
Map<Grade, Map<TeachingMaterial, Integer>> materialList = new TreeMap<Grade, Map<TeachingMaterial, Integer>>();
for (Grade grade : selectedGrades) {
Map<TeachingMaterial, Integer> gradeMap = new TreeMap<TeachingMaterial, Integer>();
for(Student student : daoStudent.findAllWithCriteria(Restrictions.eq("leavingSchool", false), Restrictions.eq("grade", grade))) {
for(BorrowedMaterial borrowedMaterial : student.getUnreceivedBorrowedList()) {
TeachingMaterial teachingMaterial = borrowedMaterial.getTeachingMaterial();
if(gradeMap.containsKey(teachingMaterial)) {
gradeMap.put(teachingMaterial, gradeMap.get(teachingMaterial) + 1);
} else {
gradeMap.put(teachingMaterial, 1);
}
}
}
materialList.put(grade, gradeMap);
}
materialListGrades.set(materialList);
}
/**
* Marks the given {@link BorrowedMaterial}s as {@code received}.
*
* @param borrowedMaterials {@link Collection} of {@link BorrowedMaterial}s that should be marked {@code received}
*/
@HandlesAction(SetBorrowedMaterialsReceived.class)
public void setBorrowedMaterialsReceived(Collection<BorrowedMaterial> borrowedMaterials) {
for (BorrowedMaterial borrowedMaterial : borrowedMaterials) {
borrowedMaterial.setReceived(true);
}
daoBorrowedMaterial.update(borrowedMaterials);
updateAllStudentsBorrowedMaterials();
}
/**
* Lends the given {@link TeachingMaterial} for the given {@link Student} until the given {@link Date}.
*
* @param student {@link Student} to lend this...
* @param toLend {@link TeachingMaterial} ...
* @param borrowUntil until this {@link Date}
*/
@HandlesAction(DoManualLending.class)
public void doManualLending(Student student, TeachingMaterial toLend, Date borrowUntil) {
persistBorrowedMaterial(student, toLend, borrowUntil);
updateAllStudentsBorrowedMaterials();
}
/**
* Updates the list of {@link BorrowedMaterial}s for all {@link Student}s
*/
private void updateAllStudentsBorrowedMaterials() {
persistBorrowedMaterials(getNewTeachingMaterials(daoGrade.findAll()));
updateUnreceivedBorrowedMaterialsState();
}
/**
* Updates the {@link State} {@link TeachingMaterials}
*/
private void updateTeachingMaterials() {
teachingMaterials.set(daoTeachingMaterial.findAll());
}
/**
* Updates the recently actice {@link SchoolYear}
*/
private void updateSchoolYear() {
recentlyActiveSchoolYear = daoSchoolYear.findSingleWithCriteria(
Order.desc("toDate"),
Restrictions.le("fromDate", new Date()));
if(recentlyActiveSchoolYear == null) {
recentlyActiveSchoolYear = new SchoolYear.Builder(
"now", getDate(Calendar.AUGUST, 1), getDate(Calendar.JUNE, 31))
.endFirstTerm(getDate(Calendar.JANUARY, 31))
.beginSecondTerm(getDate(Calendar.FEBRUARY, 1))
.build();
}
}
/**
* Returns {@link Date} object with the current year and the given month and day.
*
* @param month
* @param day
* @return {@link Date} object with given information
*/
private Date getDate(int month, int day) {
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR), month, day);
return calendar.getTime();
}
/**
* Updates the {@link State} of all {@link Student}s unreceived {@link BorrowedMaterial}s.<br>
* Returned as {@code Map<Grade, Map<Student, List<BorrowedMaterial>>>} in {@link StudentsWithUnreceivedBorrowedMaterials}
*
*/
private void updateUnreceivedBorrowedMaterialsState() {
Map<Grade, Map<Student, List<BorrowedMaterial>>> unreceivedMap = new TreeMap<Grade, Map<Student, List<BorrowedMaterial>>>();
for (Grade grade : daoGrade.findAll()) {
Map<Student, List<BorrowedMaterial>> studentsWithUnreceivedBorrowedMaterials = new TreeMap<Student, List<BorrowedMaterial>>();
for (Student student : grade.getStudents()) {
if (student.hasUnreceivedBorrowedMaterials()) {
List<BorrowedMaterial> unreceivedBorrowedList = student.getUnreceivedBorrowedList();
Collections.sort(unreceivedBorrowedList);
studentsWithUnreceivedBorrowedMaterials.put(student, unreceivedBorrowedList);
}
}
if(!studentsWithUnreceivedBorrowedMaterials.isEmpty()) {
unreceivedMap.put(grade, studentsWithUnreceivedBorrowedMaterials);
}
}
studentsWithUnreceivedBorrowedMaterials.set(unreceivedMap);
}
/**
* Returns map of {@link TeachingMaterial}s that have to be lended by the {@link Student}s of the given {@link Grade}s.
*
* @param grades
* @return {@link Map} of {@link Student}s and their {@link TeachingMaterial}s that have to be lended
*/
private Map<Student, List<TeachingMaterial>> getNewTeachingMaterials(List<Grade> grades) {
Map<Student, List<TeachingMaterial>> studentsNewTeachingMaterialMap = new HashMap<Student, List<TeachingMaterial>>();
Date today = new Date();
//No TeachingMaterials to lend when not in an active SchoolYear
if(recentlyActiveSchoolYear.getToDate().before(today)) {
return studentsNewTeachingMaterialMap;
}
Term recentlyActiveTerm = recentlyActiveSchoolYear.getRecentlyActiveTerm();
SimpleExpression restrictionValidFrom = Restrictions.le("validFrom", today);
SimpleExpression restrictionFromTerm = Restrictions.le("fromTerm", recentlyActiveTerm);
SimpleExpression restrictionToTerm = Restrictions.ge("toTerm", recentlyActiveTerm);
LogicalExpression restrictionValidUntil = Restrictions.or(
Restrictions.ge("validUntil", today),
Restrictions.isNull("validUntil"));
for (Grade gradeEntity : grades) {
int grade = gradeEntity.getGrade();
Collection<TeachingMaterial> teachingMaterials = daoTeachingMaterial.findAllWithCriteria(
Restrictions.and(
Restrictions.le("fromGrade", grade)
, Restrictions.ge("toGrade", grade)
, restrictionValidFrom
, restrictionFromTerm
, restrictionToTerm
, restrictionValidUntil
));
for (Student student : gradeEntity.getStudents()) {
if (!student.isLeavingSchool()) {
List<TeachingMaterial> teachingMaterialsOfStudent = new ArrayList<>(student.getTeachingMaterials());
List<TeachingMaterial> toLend = new ArrayList<TeachingMaterial>();
Set<Subject> studentsProfile = student.getProfile();
for (TeachingMaterial teachingMaterial : teachingMaterials) {
if (studentsProfile.containsAll(teachingMaterial.getProfile())
&& !teachingMaterialsOfStudent.contains(teachingMaterial)) {
toLend.add(teachingMaterial);
}
}
studentsNewTeachingMaterialMap.put(student, toLend);
}
}
}
return studentsNewTeachingMaterialMap;
}
/**
* Persists the {@link TeachingMaterial}s for the {@link Student}s as {@link BorrowedMaterial}s in the database.
*
* @param newTeachingMaterials {@link Map} of {@link Student}s and their new {@link TeachingMaterial}s
*/
private void persistBorrowedMaterials(Map<Student, List<TeachingMaterial>> newTeachingMaterials) {
Collection<BorrowedMaterial> borrowedMaterials = new ArrayList<>();
for(Student student : newTeachingMaterials.keySet()) {
borrowedMaterials.addAll(buildBorrowedMaterials(student, newTeachingMaterials.get(student)));
}
daoBorrowedMaterial.insert(borrowedMaterials);
}
/**
* Builds a {@link Collection} of {@link BorrowedMaterial}s
*
* @param student
* @param teachingMaterials
* @return {@link Collection} of {@link BorrowedMaterial}s
*/
private Collection<BorrowedMaterial> buildBorrowedMaterials(Student student, List<TeachingMaterial> teachingMaterials) {
Date borrowFrom = new Date();
Collection<BorrowedMaterial> borrowedMaterials = new ArrayList<>();
for (TeachingMaterial teachingMaterial : teachingMaterials) {
borrowedMaterials.add(new BorrowedMaterial.Builder(student, teachingMaterial, borrowFrom).build());
}
return borrowedMaterials;
}
/**
* Persists a single {@link TeachingMaterial} as {@link BorrowedMaterial}.<br>
* Is needed for manual lending.
*
* @param student
* @param teachingMaterial
* @param borrowUntil
*/
private void persistBorrowedMaterial(Student student, TeachingMaterial teachingMaterial, Date borrowUntil) {
daoBorrowedMaterial.insert(new BorrowedMaterial.Builder(student, teachingMaterial, new Date()).borrowUntil(borrowUntil).build());
}
}