org.unitime.timetable.test.UpdateExamConflicts.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.timetable.test.UpdateExamConflicts.java

Source

/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 *
 * The Apereo Foundation licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
*/
package org.unitime.timetable.test;

import java.text.DecimalFormat;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
import org.hibernate.Transaction;
import org.unitime.commons.hibernate.util.HibernateUtil;
import org.unitime.timetable.ApplicationProperties;
import org.unitime.timetable.dataexchange.DataExchangeHelper;
import org.unitime.timetable.model.DepartmentalInstructor;
import org.unitime.timetable.model.Exam;
import org.unitime.timetable.model.ExamConflict;
import org.unitime.timetable.model.ExamOwner;
import org.unitime.timetable.model.ExamType;
import org.unitime.timetable.model.Meeting;
import org.unitime.timetable.model.Session;
import org.unitime.timetable.model.Student;
import org.unitime.timetable.model.dao.DepartmentalInstructorDAO;
import org.unitime.timetable.model.dao.ExamDAO;
import org.unitime.timetable.model.dao.StudentDAO;
import org.unitime.timetable.model.dao._RootDAO;
import org.unitime.timetable.solver.exam.ui.ExamAssignment;
import org.unitime.timetable.solver.exam.ui.ExamAssignmentInfo;
import org.unitime.timetable.solver.exam.ui.ExamAssignmentInfo.BackToBackConflict;
import org.unitime.timetable.solver.exam.ui.ExamAssignmentInfo.DirectConflict;
import org.unitime.timetable.solver.exam.ui.ExamAssignmentInfo.MoreThanTwoADayConflict;
import org.unitime.timetable.solver.exam.ui.ExamAssignmentInfo.Parameters;

/**
 * @author Tomas Muller
 */
public class UpdateExamConflicts {
    private static Log sLog = LogFactory.getLog(UpdateExamConflicts.class);
    private static DecimalFormat sDF = new DecimalFormat("0.0");
    private static boolean sDebug = false;

    private static int sCreate = 0;
    private static int sUpdate = 1;
    private static int sDelete = 2;
    private static int sStudents = 0;
    private static int sInstructors = 1;
    private int[][][] iCnt;
    private int[][] iTotal;

    private DataExchangeHelper iHelper = null;

    public UpdateExamConflicts() {
    }

    public UpdateExamConflicts(DataExchangeHelper helper) {
        iHelper = helper;
    }

    private void debug(String message) {
        if (!sDebug)
            return;
        if (iHelper == null)
            sLog.debug(message);
        else
            iHelper.debug(message);
    }

    private void info(String message) {
        if (iHelper == null)
            sLog.info(message);
        else
            iHelper.info(message);
    }

    public void updateConflicts(ExamAssignmentInfo assignment, org.hibernate.Session hibSession) throws Exception {
        Transaction tx = null;
        try {
            if (hibSession.getTransaction() == null || !hibSession.getTransaction().isActive())
                tx = hibSession.beginTransaction();

            Exam exam = assignment.getExam(hibSession);

            HashSet<Exam> otherExams = new HashSet();

            HashSet<ExamConflict> conflicts = new HashSet(exam.getConflicts());

            for (DirectConflict dc : assignment.getDirectConflicts()) {
                if (dc.getOtherExam() == null)
                    continue;
                ExamConflict conf = null;
                for (ExamConflict c : conflicts) {
                    if (c.getConflictType() != ExamConflict.sConflictTypeDirect)
                        continue;
                    if (c.getNrStudents() == 0)
                        continue;
                    Exam other = null;
                    for (Iterator i = c.getExams().iterator(); i.hasNext();) {
                        Exam x = (Exam) i.next();
                        if (x.getUniqueId().equals(dc.getOtherExam().getExamId())) {
                            other = x;
                            break;
                        }
                    }
                    if (other == null)
                        continue;
                    conf = c;
                    break;
                }
                HashSet<Student> students = getStudents(hibSession, dc.getStudents());
                if (exam.getUniqueId().compareTo(dc.getOtherExam().getExamId()) < 0)
                    iTotal[sStudents][ExamConflict.sConflictTypeDirect] += students.size();
                if (conf == null) {
                    debug("    new direct " + assignment.getExamName() + " " + dc.getOtherExam().getExamName()
                            + " (" + students.size() + " students)");
                    conf = new ExamConflict();
                    conf.setConflictType(ExamConflict.sConflictTypeDirect);
                    conf.setStudents(students);
                    conf.setNrStudents(students.size());
                    exam.getConflicts().add(conf);
                    Exam other = dc.getOtherExam().getExam(hibSession);
                    other.getConflicts().add(conf);
                    otherExams.add(other);
                    conf.setExams(new HashSet());
                    conf.getExams().add(exam);
                    conf.getExams().add(other);
                    hibSession.save(conf);
                    iCnt[sStudents][conf.getConflictType()][sCreate] += conf.getNrStudents();
                } else {
                    conflicts.remove(conf);
                    boolean change = (students.size() != conf.getStudents().size()
                            || !students.containsAll(conf.getStudents()));
                    if (change) {
                        debug("    update direct " + assignment.getExamName() + " "
                                + dc.getOtherExam().getExamName() + " (" + conf.getNrStudents() + "->"
                                + students.size() + " students)");
                        conf.setStudents(students);
                        conf.setNrStudents(students.size());
                        hibSession.update(conf);
                        iCnt[sStudents][conf.getConflictType()][sUpdate] += conf.getNrStudents();
                    }
                }
            }
            for (DirectConflict dc : assignment.getInstructorDirectConflicts()) {
                if (dc.getOtherExam() == null)
                    continue;
                ExamConflict conf = null;
                for (ExamConflict c : conflicts) {
                    if (c.getConflictType() != ExamConflict.sConflictTypeDirect)
                        continue;
                    if (c.getNrInstructors() == 0)
                        continue;
                    Exam other = null;
                    for (Iterator i = c.getExams().iterator(); i.hasNext();) {
                        Exam x = (Exam) i.next();
                        if (x.getUniqueId().equals(dc.getOtherExam().getExamId())) {
                            other = x;
                            break;
                        }
                    }
                    if (other == null)
                        continue;
                    conf = c;
                    break;
                }
                HashSet<DepartmentalInstructor> instructors = getInstructors(hibSession, dc.getStudents());
                if (exam.getUniqueId().compareTo(dc.getOtherExam().getExamId()) < 0)
                    iTotal[sInstructors][ExamConflict.sConflictTypeDirect] += instructors.size();
                if (conf == null) {
                    debug("    new direct " + assignment.getExamName() + " " + dc.getOtherExam().getExamName()
                            + " (" + instructors.size() + " instructors)");
                    conf = new ExamConflict();
                    conf.setConflictType(ExamConflict.sConflictTypeDirect);
                    conf.setInstructors(instructors);
                    conf.setNrInstructors(instructors.size());
                    exam.getConflicts().add(conf);
                    Exam other = dc.getOtherExam().getExam(hibSession);
                    other.getConflicts().add(conf);
                    otherExams.add(other);
                    conf.setExams(new HashSet());
                    conf.getExams().add(exam);
                    conf.getExams().add(other);
                    hibSession.save(conf);
                    iCnt[sInstructors][conf.getConflictType()][sCreate] += conf.getNrInstructors();
                } else {
                    conflicts.remove(conf);
                    boolean change = (instructors.size() != conf.getInstructors().size()
                            || !instructors.containsAll(conf.getInstructors()));
                    if (change) {
                        debug("    update direct " + assignment.getExamName() + " "
                                + dc.getOtherExam().getExamName() + " (" + conf.getNrInstructors() + "->"
                                + instructors.size() + " instructors)");
                        conf.setInstructors(instructors);
                        conf.setNrInstructors(instructors.size());
                        hibSession.update(conf);
                        iCnt[sInstructors][conf.getConflictType()][sUpdate] += conf.getNrInstructors();
                    }
                }
            }

            for (BackToBackConflict btb : assignment.getBackToBackConflicts()) {
                int type = (btb.isDistance() ? ExamConflict.sConflictTypeBackToBackDist
                        : ExamConflict.sConflictTypeBackToBack);
                if (btb.getOtherExam() == null)
                    continue;
                ExamConflict conf = null;
                for (ExamConflict c : conflicts) {
                    if (c.getConflictType() != type)
                        continue;
                    if (c.getNrStudents() == 0)
                        continue;
                    Exam other = null;
                    for (Iterator i = c.getExams().iterator(); i.hasNext();) {
                        Exam x = (Exam) i.next();
                        if (x.getUniqueId().equals(btb.getOtherExam().getExamId())) {
                            other = x;
                            break;
                        }
                    }
                    if (other == null)
                        continue;
                    conf = c;
                    break;
                }
                HashSet<Student> students = getStudents(hibSession, btb.getStudents());
                if (exam.getUniqueId().compareTo(btb.getOtherExam().getExamId()) < 0)
                    iTotal[sStudents][type] += students.size();
                if (conf == null) {
                    debug("    new btb " + assignment.getExamName() + " " + btb.getOtherExam().getExamName() + " ("
                            + students.size() + " students)");
                    conf = new ExamConflict();
                    conf.setConflictType(type);
                    conf.setStudents(students);
                    conf.setNrStudents(students.size());
                    conf.setDistance(btb.getDistance());
                    exam.getConflicts().add(conf);
                    Exam other = btb.getOtherExam().getExam(hibSession);
                    other.getConflicts().add(conf);
                    otherExams.add(other);
                    conf.setExams(new HashSet());
                    conf.getExams().add(exam);
                    conf.getExams().add(other);
                    hibSession.save(conf);
                    iCnt[sStudents][conf.getConflictType()][sCreate] += conf.getNrStudents();
                } else {
                    conflicts.remove(conf);
                    boolean change = (students.size() != conf.getStudents().size()
                            || !students.containsAll(conf.getStudents()));
                    if (change) {
                        debug("    update btb " + assignment.getExamName() + " " + btb.getOtherExam().getExamName()
                                + " (" + conf.getNrStudents() + "->" + students.size() + " students)");
                        conf.setStudents(students);
                        conf.setNrStudents(students.size());
                        conf.setDistance(btb.getDistance());
                        hibSession.update(conf);
                        iCnt[sStudents][conf.getConflictType()][sUpdate] += conf.getNrStudents();
                    } else if (conf.getDistance() == null
                            || Math.abs(conf.getDistance() - btb.getDistance()) > 1.0) {
                        debug("    update btb " + assignment.getExamName() + " " + btb.getOtherExam().getExamName()
                                + " (distance " + conf.getDistance() + " -> " + btb.getDistance() + ")");
                        conf.setDistance(btb.getDistance());
                        hibSession.update(conf);
                        iCnt[sStudents][conf.getConflictType()][sUpdate] += conf.getNrStudents();
                    }
                }
            }
            for (BackToBackConflict btb : assignment.getInstructorBackToBackConflicts()) {
                int type = (btb.isDistance() ? ExamConflict.sConflictTypeBackToBackDist
                        : ExamConflict.sConflictTypeBackToBack);
                if (btb.getOtherExam() == null)
                    continue;
                ExamConflict conf = null;
                for (ExamConflict c : conflicts) {
                    if (c.getConflictType() != type)
                        continue;
                    if (c.getNrInstructors() == 0)
                        continue;
                    Exam other = null;
                    for (Iterator i = c.getExams().iterator(); i.hasNext();) {
                        Exam x = (Exam) i.next();
                        if (x.getUniqueId().equals(btb.getOtherExam().getExamId())) {
                            other = x;
                            break;
                        }
                    }
                    if (other == null)
                        continue;
                    conf = c;
                    break;
                }
                HashSet<DepartmentalInstructor> instructors = getInstructors(hibSession, btb.getStudents());
                if (exam.getUniqueId().compareTo(btb.getOtherExam().getExamId()) < 0)
                    iTotal[sInstructors][type] += instructors.size();
                if (conf == null) {
                    debug("    new btb " + assignment.getExamName() + " " + btb.getOtherExam().getExamName() + " ("
                            + instructors.size() + " instructors)");
                    conf = new ExamConflict();
                    conf.setConflictType(type);
                    conf.setInstructors(instructors);
                    conf.setNrInstructors(instructors.size());
                    if (btb.isDistance())
                        conf.setDistance(btb.getDistance());
                    exam.getConflicts().add(conf);
                    Exam other = btb.getOtherExam().getExam(hibSession);
                    other.getConflicts().add(conf);
                    otherExams.add(other);
                    conf.setExams(new HashSet());
                    conf.getExams().add(exam);
                    conf.getExams().add(other);
                    hibSession.save(conf);
                    iCnt[sInstructors][conf.getConflictType()][sCreate] += conf.getNrInstructors();
                } else {
                    conflicts.remove(conf);
                    boolean change = (instructors.size() != conf.getStudents().size()
                            || !instructors.containsAll(conf.getInstructors()));
                    if (change) {
                        debug("    update btb " + assignment.getExamName() + " " + btb.getOtherExam().getExamName()
                                + " (" + conf.getNrInstructors() + "->" + instructors.size() + " instructors)");
                        conf.setInstructors(instructors);
                        conf.setNrInstructors(instructors.size());
                        conf.setDistance(btb.getDistance());
                        hibSession.update(conf);
                        iCnt[sInstructors][conf.getConflictType()][sUpdate] += conf.getNrInstructors();
                    } else if (conf.getDistance() == null
                            || Math.abs(conf.getDistance() - btb.getDistance()) > 1.0) {
                        debug("    update btb " + assignment.getExamName() + " " + btb.getOtherExam().getExamName()
                                + " (distance " + conf.getDistance() + " -> " + btb.getDistance() + ")");
                        conf.setDistance(btb.getDistance());
                        hibSession.update(conf);
                        iCnt[sInstructors][conf.getConflictType()][sUpdate] += conf.getNrInstructors();
                    }
                }
            }

            for (MoreThanTwoADayConflict m2d : assignment.getMoreThanTwoADaysConflicts()) {
                if (m2d.getOtherExams() == null || m2d.getOtherExams().isEmpty())
                    continue;
                ExamConflict conf = null;
                conf: for (ExamConflict c : conflicts) {
                    if (c.getConflictType() != ExamConflict.sConflictTypeMoreThanTwoADay)
                        continue;
                    if (c.getNrStudents() == 0)
                        continue;
                    if (c.getExams().size() != 1 + m2d.getOtherExams().size())
                        continue;
                    for (Iterator i = c.getExams().iterator(); i.hasNext();) {
                        Exam other = (Exam) i.next();
                        if (other.getUniqueId().equals(exam.getUniqueId()))
                            continue;
                        boolean contain = false;
                        for (ExamAssignment x : m2d.getOtherExams()) {
                            if (x.getExamId().equals(other.getUniqueId())) {
                                contain = true;
                                break;
                            }
                        }
                        if (!contain)
                            continue conf;
                    }
                    conf = c;
                    break;
                }
                HashSet<Student> students = getStudents(hibSession, m2d.getStudents());
                boolean smallest = true;
                for (ExamAssignment x : m2d.getOtherExams())
                    if (exam.getUniqueId().compareTo(x.getExamId()) > 0) {
                        smallest = false;
                        break;
                    }
                if (smallest)
                    iTotal[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay] += students.size();
                String name = assignment.getExamName();
                for (ExamAssignment x : m2d.getOtherExams()) {
                    name += " " + x.getExamName();
                }
                if (conf == null) {
                    debug("    new m2d " + name + " (" + students.size() + " students)");
                    conf = new ExamConflict();
                    conf.setConflictType(ExamConflict.sConflictTypeMoreThanTwoADay);
                    conf.setStudents(students);
                    conf.setNrStudents(students.size());
                    exam.getConflicts().add(conf);
                    conf.setExams(new HashSet());
                    conf.getExams().add(exam);
                    for (ExamAssignment x : m2d.getOtherExams()) {
                        Exam other = x.getExam(hibSession);
                        other.getConflicts().add(conf);
                        otherExams.add(other);
                        conf.getExams().add(other);
                    }
                    hibSession.save(conf);
                    iCnt[sStudents][conf.getConflictType()][sCreate] += conf.getNrStudents();
                } else {
                    conflicts.remove(conf);
                    boolean change = (students.size() != conf.getStudents().size()
                            || !students.containsAll(conf.getStudents()));
                    if (change) {
                        debug("    update m2d " + name + " (" + conf.getNrStudents() + "->" + students.size()
                                + " students)");
                        conf.setStudents(students);
                        conf.setNrStudents(students.size());
                        hibSession.update(conf);
                        iCnt[sStudents][conf.getConflictType()][sUpdate] += conf.getNrStudents();
                    }
                }
            }
            for (MoreThanTwoADayConflict m2d : assignment.getInstructorMoreThanTwoADaysConflicts()) {
                if (m2d.getOtherExams() == null || m2d.getOtherExams().isEmpty())
                    continue;
                ExamConflict conf = null;
                conf: for (ExamConflict c : conflicts) {
                    if (c.getConflictType() != ExamConflict.sConflictTypeMoreThanTwoADay)
                        continue;
                    if (c.getNrInstructors() == 0)
                        continue;
                    if (c.getExams().size() != 1 + m2d.getOtherExams().size())
                        continue;
                    for (Iterator i = c.getExams().iterator(); i.hasNext();) {
                        Exam other = (Exam) i.next();
                        if (other.getUniqueId().equals(exam.getUniqueId()))
                            continue;
                        boolean contain = false;
                        for (ExamAssignment x : m2d.getOtherExams()) {
                            if (x.getExamId().equals(other.getUniqueId())) {
                                contain = true;
                                break;
                            }
                        }
                        if (!contain)
                            continue conf;
                    }
                    conf = c;
                    break;
                }
                HashSet<DepartmentalInstructor> instructors = getInstructors(hibSession, m2d.getStudents());
                boolean smallest = true;
                for (ExamAssignment x : m2d.getOtherExams())
                    if (exam.getUniqueId().compareTo(x.getExamId()) > 0) {
                        smallest = false;
                        break;
                    }
                if (smallest)
                    iTotal[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay] += instructors.size();
                String name = assignment.getExamName();
                for (ExamAssignment x : m2d.getOtherExams()) {
                    name += " " + x.getExamName();
                }
                if (conf == null) {
                    debug("    new btb " + name + " (" + instructors.size() + " instructors)");
                    conf = new ExamConflict();
                    conf.setConflictType(ExamConflict.sConflictTypeMoreThanTwoADay);
                    conf.setInstructors(instructors);
                    conf.setNrInstructors(instructors.size());
                    exam.getConflicts().add(conf);
                    conf.setExams(new HashSet());
                    conf.getExams().add(exam);
                    for (ExamAssignment x : m2d.getOtherExams()) {
                        Exam other = x.getExam(hibSession);
                        other.getConflicts().add(conf);
                        otherExams.add(other);
                        conf.getExams().add(other);
                    }
                    hibSession.save(conf);
                    iCnt[sInstructors][conf.getConflictType()][sCreate] += conf.getNrInstructors();
                } else {
                    conflicts.remove(conf);
                    boolean change = (instructors.size() != conf.getStudents().size()
                            || !instructors.containsAll(conf.getInstructors()));
                    if (change) {
                        debug("    update btb " + name + " (" + conf.getNrInstructors() + "->" + instructors.size()
                                + " instructors)");
                        conf.setInstructors(instructors);
                        conf.setNrInstructors(instructors.size());
                        hibSession.update(conf);
                        iCnt[sInstructors][conf.getConflictType()][sUpdate] += conf.getNrInstructors();
                    }
                }
            }

            for (ExamConflict conf : conflicts) {
                String name = "";
                if (conf.getConflictType() == ExamConflict.sConflictTypeDirect)
                    name = "direct";
                else if (conf.getConflictType() == ExamConflict.sConflictTypeMoreThanTwoADay)
                    name = "m2d";
                else if (conf.getConflictType() == ExamConflict.sConflictTypeBackToBack)
                    name = "btb";
                else if (conf.getConflictType() == ExamConflict.sConflictTypeBackToBackDist)
                    name = "btb";
                if (conf.getNrInstructors() != null)
                    iCnt[sInstructors][conf.getConflictType()][sDelete] += conf.getNrInstructors();
                if (conf.getNrStudents() != null)
                    iCnt[sStudents][conf.getConflictType()][sDelete] += conf.getNrStudents();
                for (Iterator i = conf.getExams().iterator(); i.hasNext();) {
                    Exam other = (Exam) i.next();
                    name += " " + other.getLabel();
                    other.getConflicts().remove(conf);
                    if (!other.equals(exam))
                        otherExams.add(other);
                }
                debug("  delete " + name + " ("
                        + (conf.getNrStudents() != null && conf.getNrStudents() > 0
                                ? conf.getNrStudents() + " students"
                                : "")
                        + (conf.getNrInstructors() != null && conf.getNrInstructors() > 0
                                ? conf.getNrInstructors() + " instructors"
                                : "")
                        + ")");
                hibSession.delete(conf);
            }

            hibSession.update(exam);
            for (Exam other : otherExams)
                hibSession.update(other);

            //hibSession.flush();
            if (tx != null)
                tx.commit();
        } catch (Exception e) {
            if (tx != null)
                tx.rollback();
            throw e;
        }
    }

    protected static HashSet<Student> getStudents(org.hibernate.Session hibSession, Collection studentIds) {
        HashSet<Student> students = new HashSet();
        if (studentIds == null || studentIds.isEmpty())
            return students;
        for (Iterator i = studentIds.iterator(); i.hasNext();) {
            Long studentId = (Long) i.next();
            Student student = new StudentDAO().get(studentId, hibSession);
            if (student != null)
                students.add(student);
            else
                sLog.warn("Student " + studentId + " not found.");
        }
        return students;
    }

    protected static HashSet<DepartmentalInstructor> getInstructors(org.hibernate.Session hibSession,
            Collection instructorIds) {
        HashSet<DepartmentalInstructor> instructors = new HashSet();
        if (instructorIds == null || instructorIds.isEmpty())
            return instructors;
        for (Iterator i = instructorIds.iterator(); i.hasNext();) {
            Long instructorId = (Long) i.next();
            DepartmentalInstructor instructor = new DepartmentalInstructorDAO().get(instructorId, hibSession);
            if (instructor != null)
                instructors.add(instructor);
            else
                sLog.warn("Instructor " + instructorId + " not found.");
        }
        return instructors;
    }

    public void simpleCheck(ExamAssignmentInfo e1, ExamAssignmentInfo e2) {
        if (e1.getNrDirectConflicts() != e2.getNrDirectConflicts()) {
            sLog.warn("Wrong number of direct student conflicts for " + e1.getExamName() + " ("
                    + e1.getNrDirectConflicts() + "!=" + e2.getNrDirectConflicts() + ")");
        }
        if (e1.getNrBackToBackConflicts() != e2.getNrBackToBackConflicts()) {
            sLog.warn("Wrong number of back-to-back student conflicts for " + e1.getExamName() + " ("
                    + e1.getNrBackToBackConflicts() + "!=" + e2.getNrBackToBackConflicts() + ")");
        }
        if (e1.getNrMoreThanTwoConflicts() != e2.getNrMoreThanTwoConflicts()) {
            sLog.warn("Wrong number of >2 exams a day student conflicts for " + e1.getExamName() + " ("
                    + e1.getNrMoreThanTwoConflicts() + "!=" + e2.getNrMoreThanTwoConflicts() + ")");
        }
        if (e1.getNrInstructorDirectConflicts() != e2.getNrInstructorDirectConflicts()) {
            sLog.warn("Wrong number of direct instructor conflicts for " + e1.getExamName() + " ("
                    + e1.getNrInstructorDirectConflicts() + "!=" + e2.getNrInstructorDirectConflicts() + ")");
        }
        if (e1.getNrInstructorBackToBackConflicts() != e2.getNrInstructorBackToBackConflicts()) {
            sLog.warn("Wrong number of back-to-back instructor conflicts for " + e1.getExamName() + " ("
                    + e1.getNrInstructorBackToBackConflicts() + "!=" + e2.getNrInstructorBackToBackConflicts()
                    + ")");
        }
        if (e1.getNrInstructorMoreThanTwoConflicts() != e2.getNrInstructorMoreThanTwoConflicts()) {
            sLog.warn("Wrong number of >2 exams a day instructor conflicts for " + e1.getExamName() + " ("
                    + e1.getNrInstructorMoreThanTwoConflicts() + "!=" + e2.getNrInstructorMoreThanTwoConflicts()
                    + ")");
        }
    }

    public TreeSet<ExamAssignmentInfo> loadExams(Long sessionId, Long examTypeId) throws Exception {
        info("Loading exams...");
        long t0 = System.currentTimeMillis();
        Hashtable<Long, Exam> exams = new Hashtable();
        for (Iterator i = new ExamDAO().getSession().createQuery(
                "select x from Exam x where x.session.uniqueId=:sessionId and x.examType.uniqueId=:examTypeId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId).setCacheable(true).list()
                .iterator(); i.hasNext();) {
            Exam exam = (Exam) i.next();
            exams.put(exam.getUniqueId(), exam);
        }
        info("  Fetching related objects (class)...");
        new ExamDAO().getSession().createQuery(
                "select c from Class_ c, ExamOwner o where o.exam.session.uniqueId=:sessionId and o.exam.examType.uniqueId=:examTypeId and o.ownerType=:classType and c.uniqueId=o.ownerId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId)
                .setInteger("classType", ExamOwner.sOwnerTypeClass).setCacheable(true).list();
        info("  Fetching related objects (config)...");
        new ExamDAO().getSession().createQuery(
                "select c from InstrOfferingConfig c, ExamOwner o where o.exam.session.uniqueId=:sessionId and o.exam.examType.uniqueId=:examTypeId and o.ownerType=:configType and c.uniqueId=o.ownerId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId)
                .setInteger("configType", ExamOwner.sOwnerTypeConfig).setCacheable(true).list();
        info("  Fetching related objects (course)...");
        new ExamDAO().getSession().createQuery(
                "select c from CourseOffering c, ExamOwner o where o.exam.session.uniqueId=:sessionId and o.exam.examType.uniqueId=:examTypeId and o.ownerType=:courseType and c.uniqueId=o.ownerId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId)
                .setInteger("courseType", ExamOwner.sOwnerTypeCourse).setCacheable(true).list();
        info("  Fetching related objects (offering)...");
        new ExamDAO().getSession().createQuery(
                "select c from InstructionalOffering c, ExamOwner o where o.exam.session.uniqueId=:sessionId and o.exam.examType.uniqueId=:examTypeId and o.ownerType=:offeringType and c.uniqueId=o.ownerId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId)
                .setInteger("offeringType", ExamOwner.sOwnerTypeOffering).setCacheable(true).list();
        Hashtable<Long, Set<Long>> owner2students = new Hashtable();
        Hashtable<Long, Set<Exam>> student2exams = new Hashtable();
        Hashtable<Long, Hashtable<Long, Set<Long>>> owner2course2students = new Hashtable();
        info("  Loading students (class)...");
        for (Iterator i = new ExamDAO().getSession()
                .createQuery("select x.uniqueId, o.uniqueId, e.student.uniqueId, e.courseOffering.uniqueId from "
                        + "Exam x inner join x.owners o, " + "StudentClassEnrollment e inner join e.clazz c "
                        + "where x.session.uniqueId=:sessionId and x.examType.uniqueId=:examTypeId and "
                        + "o.ownerType=" + org.unitime.timetable.model.ExamOwner.sOwnerTypeClass + " and "
                        + "o.ownerId=c.uniqueId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId).setCacheable(true).list()
                .iterator(); i.hasNext();) {
            Object[] o = (Object[]) i.next();
            Long examId = (Long) o[0];
            Long ownerId = (Long) o[1];
            Long studentId = (Long) o[2];
            Set<Long> studentsOfOwner = owner2students.get(ownerId);
            if (studentsOfOwner == null) {
                studentsOfOwner = new HashSet<Long>();
                owner2students.put(ownerId, studentsOfOwner);
            }
            studentsOfOwner.add(studentId);
            Set<Exam> examsOfStudent = student2exams.get(studentId);
            if (examsOfStudent == null) {
                examsOfStudent = new HashSet<Exam>();
                student2exams.put(studentId, examsOfStudent);
            }
            examsOfStudent.add(exams.get(examId));
            Long courseId = (Long) o[3];
            Hashtable<Long, Set<Long>> course2students = owner2course2students.get(ownerId);
            if (course2students == null) {
                course2students = new Hashtable<Long, Set<Long>>();
                owner2course2students.put(ownerId, course2students);
            }
            Set<Long> studentsOfCourse = course2students.get(courseId);
            if (studentsOfCourse == null) {
                studentsOfCourse = new HashSet<Long>();
                course2students.put(courseId, studentsOfCourse);
            }
            studentsOfCourse.add(studentId);
        }
        info("  Loading students (config)...");
        for (Iterator i = new ExamDAO().getSession()
                .createQuery("select x.uniqueId, o.uniqueId, e.student.uniqueId, e.courseOffering.uniqueId from "
                        + "Exam x inner join x.owners o, " + "StudentClassEnrollment e inner join e.clazz c "
                        + "inner join c.schedulingSubpart.instrOfferingConfig ioc "
                        + "where x.session.uniqueId=:sessionId and x.examType.uniqueId=:examTypeId and "
                        + "o.ownerType=" + org.unitime.timetable.model.ExamOwner.sOwnerTypeConfig + " and "
                        + "o.ownerId=ioc.uniqueId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId).setCacheable(true).list()
                .iterator(); i.hasNext();) {
            Object[] o = (Object[]) i.next();
            Long examId = (Long) o[0];
            Long ownerId = (Long) o[1];
            Long studentId = (Long) o[2];
            Set<Long> studentsOfOwner = owner2students.get(ownerId);
            if (studentsOfOwner == null) {
                studentsOfOwner = new HashSet<Long>();
                owner2students.put(ownerId, studentsOfOwner);
            }
            studentsOfOwner.add(studentId);
            Set<Exam> examsOfStudent = student2exams.get(studentId);
            if (examsOfStudent == null) {
                examsOfStudent = new HashSet<Exam>();
                student2exams.put(studentId, examsOfStudent);
            }
            examsOfStudent.add(exams.get(examId));
            Long courseId = (Long) o[3];
            Hashtable<Long, Set<Long>> course2students = owner2course2students.get(ownerId);
            if (course2students == null) {
                course2students = new Hashtable<Long, Set<Long>>();
                owner2course2students.put(ownerId, course2students);
            }
            Set<Long> studentsOfCourse = course2students.get(courseId);
            if (studentsOfCourse == null) {
                studentsOfCourse = new HashSet<Long>();
                course2students.put(courseId, studentsOfCourse);
            }
            studentsOfCourse.add(studentId);
        }
        info("  Loading students (course)...");
        for (Iterator i = new ExamDAO().getSession()
                .createQuery("select x.uniqueId, o.uniqueId, e.student.uniqueId, e.courseOffering.uniqueId from "
                        + "Exam x inner join x.owners o, "
                        + "StudentClassEnrollment e inner join e.courseOffering co "
                        + "where x.session.uniqueId=:sessionId and x.examType.uniqueId=:examTypeId and "
                        + "o.ownerType=" + org.unitime.timetable.model.ExamOwner.sOwnerTypeCourse + " and "
                        + "o.ownerId=co.uniqueId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId).setCacheable(true).list()
                .iterator(); i.hasNext();) {
            Object[] o = (Object[]) i.next();
            Long examId = (Long) o[0];
            Long ownerId = (Long) o[1];
            Long studentId = (Long) o[2];
            Set<Long> studentsOfOwner = owner2students.get(ownerId);
            if (studentsOfOwner == null) {
                studentsOfOwner = new HashSet<Long>();
                owner2students.put(ownerId, studentsOfOwner);
            }
            studentsOfOwner.add(studentId);
            Set<Exam> examsOfStudent = student2exams.get(studentId);
            if (examsOfStudent == null) {
                examsOfStudent = new HashSet<Exam>();
                student2exams.put(studentId, examsOfStudent);
            }
            examsOfStudent.add(exams.get(examId));
            Long courseId = (Long) o[3];
            Hashtable<Long, Set<Long>> course2students = owner2course2students.get(ownerId);
            if (course2students == null) {
                course2students = new Hashtable<Long, Set<Long>>();
                owner2course2students.put(ownerId, course2students);
            }
            Set<Long> studentsOfCourse = course2students.get(courseId);
            if (studentsOfCourse == null) {
                studentsOfCourse = new HashSet<Long>();
                course2students.put(courseId, studentsOfCourse);
            }
            studentsOfCourse.add(studentId);
        }
        info("  Loading students (offering)...");
        for (Iterator i = new ExamDAO().getSession()
                .createQuery("select x.uniqueId, o.uniqueId, e.student.uniqueId, e.courseOffering.uniqueId from "
                        + "Exam x inner join x.owners o, "
                        + "StudentClassEnrollment e inner join e.courseOffering.instructionalOffering io "
                        + "where x.session.uniqueId=:sessionId and x.examType.uniqueId=:examTypeId and "
                        + "o.ownerType=" + org.unitime.timetable.model.ExamOwner.sOwnerTypeOffering + " and "
                        + "o.ownerId=io.uniqueId")
                .setLong("sessionId", sessionId).setLong("examTypeId", examTypeId).setCacheable(true).list()
                .iterator(); i.hasNext();) {
            Object[] o = (Object[]) i.next();
            Long examId = (Long) o[0];
            Long ownerId = (Long) o[1];
            Long studentId = (Long) o[2];
            Set<Long> studentsOfOwner = owner2students.get(ownerId);
            if (studentsOfOwner == null) {
                studentsOfOwner = new HashSet<Long>();
                owner2students.put(ownerId, studentsOfOwner);
            }
            studentsOfOwner.add(studentId);
            Set<Exam> examsOfStudent = student2exams.get(studentId);
            if (examsOfStudent == null) {
                examsOfStudent = new HashSet<Exam>();
                student2exams.put(studentId, examsOfStudent);
            }
            examsOfStudent.add(exams.get(examId));
            Long courseId = (Long) o[3];
            Hashtable<Long, Set<Long>> course2students = owner2course2students.get(ownerId);
            if (course2students == null) {
                course2students = new Hashtable<Long, Set<Long>>();
                owner2course2students.put(ownerId, course2students);
            }
            Set<Long> studentsOfCourse = course2students.get(courseId);
            if (studentsOfCourse == null) {
                studentsOfCourse = new HashSet<Long>();
                course2students.put(courseId, studentsOfCourse);
            }
            studentsOfCourse.add(studentId);
        }
        Hashtable<Long, Set<Meeting>> period2meetings = new Hashtable();
        Parameters p = new Parameters(sessionId, examTypeId);
        info("  Creating exam assignments...");
        TreeSet<ExamAssignmentInfo> ret = new TreeSet();
        for (Enumeration<Exam> e = exams.elements(); e.hasMoreElements();) {
            Exam exam = (Exam) e.nextElement();
            ExamAssignmentInfo info = new ExamAssignmentInfo(exam, owner2students, owner2course2students,
                    student2exams, period2meetings, p);
            ret.add(info);
        }
        long t1 = System.currentTimeMillis();
        info("Exams loaded in " + sDF.format((t1 - t0) / 1000.0) + "s.");
        return ret;
    }

    public void update(Long sessionId, Long examTypeId, org.hibernate.Session hibSession) throws Exception {
        iCnt = new int[][][] { { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } },
                { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } } };
        iTotal = new int[][] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
        TreeSet<ExamAssignmentInfo> exams = loadExams(sessionId, examTypeId);
        for (ExamAssignmentInfo exam : exams) {
            debug("Checking " + exam.getExamName() + " ...");
            updateConflicts(exam, hibSession);
            //simpleCheck(exam, new ExamAssignmentInfo(exam.getExam(),true));
        }

        if (iTotal[sStudents][ExamConflict.sConflictTypeDirect] > 0) {
            info("Direct student conflicts: " + iTotal[sStudents][ExamConflict.sConflictTypeDirect]);
            info("    created: " + iCnt[sStudents][ExamConflict.sConflictTypeDirect][sCreate]);
            info("    updated: " + iCnt[sStudents][ExamConflict.sConflictTypeDirect][sUpdate]);
            info("    deleted: " + iCnt[sStudents][ExamConflict.sConflictTypeDirect][sDelete]);
            info("  unchanged: " + (iTotal[sStudents][ExamConflict.sConflictTypeDirect]
                    - iCnt[sStudents][ExamConflict.sConflictTypeDirect][sCreate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeDirect][sUpdate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeDirect][sDelete]));
        }
        if (iTotal[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay] > 0) {
            info(">2 exams a day student conflicts: "
                    + iTotal[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay]);
            info("    created: " + iCnt[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay][sCreate]);
            info("    updated: " + iCnt[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay][sUpdate]);
            info("    deleted: " + iCnt[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay][sDelete]);
            info("  unchanged: " + (iTotal[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay]
                    - iCnt[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay][sCreate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay][sUpdate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeMoreThanTwoADay][sDelete]));
        }
        if (iTotal[sStudents][ExamConflict.sConflictTypeBackToBack] > 0) {
            info("Back-to-back student conflicts: " + iTotal[sStudents][ExamConflict.sConflictTypeBackToBack]);
            info("    created: " + iCnt[sStudents][ExamConflict.sConflictTypeBackToBack][sCreate]);
            info("    updated: " + iCnt[sStudents][ExamConflict.sConflictTypeBackToBack][sUpdate]);
            info("    deleted: " + iCnt[sStudents][ExamConflict.sConflictTypeBackToBack][sDelete]);
            info("  unchanged: " + (iTotal[sStudents][ExamConflict.sConflictTypeBackToBack]
                    - iCnt[sStudents][ExamConflict.sConflictTypeBackToBack][sCreate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeBackToBack][sUpdate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeBackToBack][sDelete]));
        }
        if (iTotal[sStudents][ExamConflict.sConflictTypeBackToBackDist] > 0) {
            info("Distance back-to-back student conflicts: "
                    + iTotal[sStudents][ExamConflict.sConflictTypeBackToBackDist]);
            info("    created: " + iCnt[sStudents][ExamConflict.sConflictTypeBackToBackDist][sCreate]);
            info("    updated: " + iCnt[sStudents][ExamConflict.sConflictTypeBackToBackDist][sUpdate]);
            info("    deleted: " + iCnt[sStudents][ExamConflict.sConflictTypeBackToBackDist][sDelete]);
            info("  unchanged: " + (iTotal[sStudents][ExamConflict.sConflictTypeBackToBackDist]
                    - iCnt[sStudents][ExamConflict.sConflictTypeBackToBackDist][sCreate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeBackToBackDist][sUpdate]
                    - iCnt[sStudents][ExamConflict.sConflictTypeBackToBackDist][sDelete]));
        }

        if (iTotal[sInstructors][ExamConflict.sConflictTypeDirect] > 0) {
            info("Direct instructor conflicts: " + iTotal[sInstructors][ExamConflict.sConflictTypeDirect]);
            info("    created: " + iCnt[sInstructors][ExamConflict.sConflictTypeDirect][sCreate]);
            info("    updated: " + iCnt[sInstructors][ExamConflict.sConflictTypeDirect][sUpdate]);
            info("    deleted: " + iCnt[sInstructors][ExamConflict.sConflictTypeDirect][sDelete]);
            info("  unchanged: " + (iTotal[sInstructors][ExamConflict.sConflictTypeDirect]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeDirect][sCreate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeDirect][sUpdate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeDirect][sDelete]));
        }
        if (iTotal[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay] > 0) {
            info(">2 exams a day instructor conflicts: "
                    + iTotal[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay]);
            info("    created: " + iCnt[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay][sCreate]);
            info("    updated: " + iCnt[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay][sUpdate]);
            info("    deleted: " + iCnt[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay][sDelete]);
            info("  unchanged: " + (iTotal[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay][sCreate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay][sUpdate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeMoreThanTwoADay][sDelete]));
        }
        if (iTotal[sInstructors][ExamConflict.sConflictTypeBackToBack] > 0) {
            info("Back-to-back instructor conflicts: "
                    + iTotal[sInstructors][ExamConflict.sConflictTypeBackToBack]);
            info("    created: " + iCnt[sInstructors][ExamConflict.sConflictTypeBackToBack][sCreate]);
            info("    updated: " + iCnt[sInstructors][ExamConflict.sConflictTypeBackToBack][sUpdate]);
            info("    deleted: " + iCnt[sInstructors][ExamConflict.sConflictTypeBackToBack][sDelete]);
            info("  unchanged: " + (iTotal[sInstructors][ExamConflict.sConflictTypeBackToBack]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeBackToBack][sCreate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeBackToBack][sUpdate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeBackToBack][sDelete]));
        }
        if (iTotal[sInstructors][ExamConflict.sConflictTypeBackToBackDist] > 0) {
            info("Distance back-to-back instructor conflicts: "
                    + iTotal[sInstructors][ExamConflict.sConflictTypeBackToBackDist]);
            info("    created: " + iCnt[sInstructors][ExamConflict.sConflictTypeBackToBackDist][sCreate]);
            info("    updated: " + iCnt[sInstructors][ExamConflict.sConflictTypeBackToBackDist][sUpdate]);
            info("    deleted: " + iCnt[sInstructors][ExamConflict.sConflictTypeBackToBackDist][sDelete]);
            info("  unchanged: " + (iTotal[sInstructors][ExamConflict.sConflictTypeBackToBackDist]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeBackToBackDist][sCreate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeBackToBackDist][sUpdate]
                    - iCnt[sInstructors][ExamConflict.sConflictTypeBackToBackDist][sDelete]));
        }
    }

    public static void main(String args[]) {
        try {
            Properties props = new Properties();
            props.setProperty("log4j.rootLogger", "DEBUG, A1");
            props.setProperty("log4j.appender.A1", "org.apache.log4j.ConsoleAppender");
            props.setProperty("log4j.appender.A1.layout", "org.apache.log4j.PatternLayout");
            props.setProperty("log4j.appender.A1.layout.ConversionPattern", "%-5p %c{2}: %m%n");
            props.setProperty("log4j.logger.org.hibernate", "INFO");
            props.setProperty("log4j.logger.org.hibernate.cfg", "WARN");
            props.setProperty("log4j.logger.org.hibernate.cache.EhCacheProvider", "ERROR");
            props.setProperty("log4j.logger.org.unitime.commons.hibernate", "INFO");
            props.setProperty("log4j.logger.net", "INFO");
            PropertyConfigurator.configure(props);

            HibernateUtil.configureHibernate(ApplicationProperties.getProperties());

            Session session = Session.getSessionUsingInitiativeYearTerm(
                    ApplicationProperties.getProperty("initiative", "PWL"),
                    ApplicationProperties.getProperty("year", "2008"),
                    ApplicationProperties.getProperty("term", "Fal"));
            if (session == null) {
                sLog.error(
                        "Academic session not found, use properties initiative, year, and term to set academic session.");
                System.exit(0);
            } else {
                sLog.info("Session: " + session);
            }

            ExamType examType = ExamType.findByReference(ApplicationProperties.getProperty("type", "final"));

            org.hibernate.Session hibSession = new _RootDAO().getSession();

            Transaction tx = null;
            try {
                tx = hibSession.beginTransaction();

                new UpdateExamConflicts().update(session.getUniqueId(), examType.getUniqueId(), hibSession);

                tx.commit();
            } catch (Exception e) {
                sLog.error(e);
                if (tx != null)
                    tx.rollback();
            }

        } catch (Exception e) {
            sLog.error(e);
        }
    }

}