org.unitime.timetable.solver.studentsct.StudentSectioningDatabaseSaver.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.timetable.solver.studentsct.StudentSectioningDatabaseSaver.java

Source

/*
 * UniTime 3.2 - 3.5 (University Timetabling Application)
 * Copyright (C) 2008 - 2013, UniTime LLC, and individual contributors
 * as indicated by the @authors tag.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
*/
package org.unitime.timetable.solver.studentsct;

import java.util.Date;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cpsolver.ifs.solver.Solver;
import org.cpsolver.ifs.util.Progress;
import org.cpsolver.studentsct.StudentSectioningSaver;
import org.cpsolver.studentsct.model.Config;
import org.cpsolver.studentsct.model.CourseRequest;
import org.cpsolver.studentsct.model.Enrollment;
import org.cpsolver.studentsct.model.Offering;
import org.cpsolver.studentsct.model.Request;
import org.cpsolver.studentsct.model.Section;
import org.cpsolver.studentsct.model.Student;
import org.cpsolver.studentsct.model.Subpart;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.Transaction;
import org.unitime.timetable.model.Class_;
import org.unitime.timetable.model.CourseDemand;
import org.unitime.timetable.model.CourseOffering;
import org.unitime.timetable.model.SectioningInfo;
import org.unitime.timetable.model.Session;
import org.unitime.timetable.model.StudentClassEnrollment;
import org.unitime.timetable.model.StudentSectioningQueue;
import org.unitime.timetable.model.StudentSectioningStatus;
import org.unitime.timetable.model.WaitList;
import org.unitime.timetable.model.dao.SessionDAO;

/**
 * @author Tomas Muller
 */
public class StudentSectioningDatabaseSaver extends StudentSectioningSaver {
    private static Log sLog = LogFactory.getLog(StudentSectioningDatabaseSaver.class);
    private boolean iIncludeCourseDemands = true;
    private String iInitiative = null;
    private String iTerm = null;
    private String iYear = null;
    private Hashtable<Long, org.unitime.timetable.model.Student> iStudents = null;
    private Hashtable<Long, CourseOffering> iCourses = null;
    private Hashtable<Long, Class_> iClasses = null;
    private Hashtable<String, org.unitime.timetable.model.CourseRequest> iRequests = null;
    private Date iTimeStamp = null;
    private StudentSectioningStatus iStatusToSet = null;
    private boolean iResetStatus = false;

    private int iInsert = 0;

    private Progress iProgress = null;
    private boolean iProjections = false;

    public StudentSectioningDatabaseSaver(Solver solver) {
        super(solver);
        iIncludeCourseDemands = solver.getProperties().getPropertyBoolean("Load.IncludeCourseDemands",
                iIncludeCourseDemands);
        iInitiative = solver.getProperties().getProperty("Data.Initiative");
        iYear = solver.getProperties().getProperty("Data.Year");
        iTerm = solver.getProperties().getProperty("Data.Term");
        iProgress = Progress.getInstance(getModel());
        iProjections = "Projection".equals(solver.getProperties().getProperty("StudentSctBasic.Mode", "Initial"));
    }

    public void save() {
        iProgress.setStatus("Saving solution ...");
        iTimeStamp = new Date();
        org.hibernate.Session hibSession = null;
        Transaction tx = null;
        try {
            hibSession = SessionDAO.getInstance().getSession();
            hibSession.setCacheMode(CacheMode.IGNORE);
            hibSession.setFlushMode(FlushMode.MANUAL);

            tx = hibSession.beginTransaction();

            Session session = Session.getSessionUsingInitiativeYearTerm(iInitiative, iYear, iTerm);

            if (session == null)
                throw new Exception("Session " + iInitiative + " " + iTerm + iYear + " not found!");

            save(session, hibSession);

            StudentSectioningQueue.sessionStatusChanged(hibSession, null, session.getUniqueId(), true);

            hibSession.flush();

            tx.commit();
            tx = null;

        } catch (Exception e) {
            iProgress.fatal("Unable to save student schedule, reason: " + e.getMessage(), e);
            sLog.error(e.getMessage(), e);
            if (tx != null)
                tx.rollback();
        } finally {
            // here we need to close the session since this code may run in a separate thread
            if (hibSession != null && hibSession.isOpen())
                hibSession.close();
        }
    }

    public void flushIfNeeded(org.hibernate.Session hibSession) {
        iInsert++;
        if ((iInsert % 1000) == 0) {
            hibSession.flush();
            hibSession.clear();
        }
    }

    public void flush(org.hibernate.Session hibSession) {
        hibSession.flush();
        hibSession.clear();
        iInsert = 0;
    }

    public void saveStudent(org.hibernate.Session hibSession, Student student) {
        org.unitime.timetable.model.Student s = iStudents.get(student.getId());
        if (s == null) {
            iProgress.warn("Student " + student.getId() + " not found.");
            return;
        }

        if (iStatusToSet != null)
            s.setSectioningStatus(iStatusToSet);
        else if (iResetStatus)
            s.setSectioningStatus(null);

        for (Iterator<StudentClassEnrollment> i = s.getClassEnrollments().iterator(); i.hasNext();) {
            StudentClassEnrollment sce = i.next();
            sce.getClazz().getStudentEnrollments().remove(sce);
            hibSession.delete(sce);
            i.remove();
        }
        for (Iterator<WaitList> i = s.getWaitlists().iterator(); i.hasNext();) {
            WaitList wl = i.next();
            hibSession.delete(wl);
            i.remove();
        }

        for (Iterator e = student.getRequests().iterator(); e.hasNext();) {
            Request request = (Request) e.next();
            Enrollment enrollment = (Enrollment) getAssignment().getValue(request);
            if (request instanceof CourseRequest) {
                CourseRequest courseRequest = (CourseRequest) request;
                if (enrollment == null) {
                    if (courseRequest.isWaitlist() && student.canAssign(getAssignment(), courseRequest)) {
                        CourseOffering co = iCourses.get(courseRequest.getCourses().get(0).getId());
                        if (co == null) {
                            iProgress.warn(
                                    "Course offering " + courseRequest.getCourses().get(0).getId() + " not found.");
                            continue;
                        }
                        WaitList wl = new WaitList();
                        wl.setStudent(s);
                        wl.setCourseOffering(co);
                        wl.setTimestamp(iTimeStamp);
                        wl.setType(new Integer(0));
                        s.getWaitlists().add(wl);
                        hibSession.save(wl);
                    }
                } else {
                    org.unitime.timetable.model.CourseRequest cr = iRequests
                            .get(request.getId() + ":" + enrollment.getOffering().getId());
                    for (Iterator j = enrollment.getAssignments().iterator(); j.hasNext();) {
                        Section section = (Section) j.next();
                        Class_ clazz = iClasses.get(section.getId());
                        if (clazz == null) {
                            iProgress.warn("Class " + section.getId() + " not found.");
                            continue;
                        }
                        StudentClassEnrollment sce = new StudentClassEnrollment();
                        sce.setChangedBy(StudentClassEnrollment.SystemChange.BATCH.toString());
                        sce.setStudent(s);
                        sce.setClazz(clazz);
                        if (cr == null) {
                            CourseOffering co = iCourses.get(enrollment.getCourse().getId());
                            if (co == null)
                                co = clazz.getSchedulingSubpart().getControllingCourseOffering();
                            sce.setCourseOffering(co);
                        } else {
                            sce.setCourseRequest(cr);
                            sce.setCourseOffering(cr.getCourseOffering());
                        }
                        sce.setTimestamp(iTimeStamp);
                        s.getClassEnrollments().add(sce);
                        hibSession.save(sce);
                    }
                    if (cr != null)
                        hibSession.saveOrUpdate(cr);
                }
            }
        }
        hibSession.saveOrUpdate(s);
    }

    public void save(Session session, org.hibernate.Session hibSession) {
        iClasses = new Hashtable<Long, Class_>();
        iProgress.setPhase("Loading classes...", 1);
        for (Class_ clazz : (List<Class_>) hibSession.createQuery("select distinct c from Class_ c where "
                + "c.schedulingSubpart.instrOfferingConfig.instructionalOffering.session.uniqueId = :sessionId")
                .setLong("sessionId", session.getUniqueId()).list()) {
            iClasses.put(clazz.getUniqueId(), clazz);
        }
        iProgress.incProgress();

        if (iIncludeCourseDemands && !iProjections) {
            iCourses = new Hashtable<Long, CourseOffering>();
            iProgress.setPhase("Loading courses...", 1);
            for (CourseOffering course : (List<CourseOffering>) hibSession.createQuery(
                    "select distinct c from CourseOffering c where c.subjectArea.session.uniqueId = :sessionId")
                    .setLong("sessionId", session.getUniqueId()).list()) {
                iCourses.put(course.getUniqueId(), course);
            }
            iProgress.incProgress();

            iStudents = new Hashtable<Long, org.unitime.timetable.model.Student>();
            iProgress.setPhase("Loading students...", 1);
            for (org.unitime.timetable.model.Student student : (List<org.unitime.timetable.model.Student>) hibSession
                    .createQuery("select distinct s from Student s " + "left join fetch s.courseDemands as cd "
                            + "left join fetch cd.courseRequests as cr "
                            + "left join fetch s.classEnrollments as e " + "left join fetch s.waitlists as w "
                            + "where s.session.uniqueId = :sessionId")
                    .setLong("sessionId", session.getUniqueId()).list()) {
                iStudents.put(student.getUniqueId(), student);
            }
            iProgress.incProgress();

            iRequests = new Hashtable<String, org.unitime.timetable.model.CourseRequest>();
            iProgress.setPhase("Loading course demands...", 1);
            for (CourseDemand demand : (List<CourseDemand>) hibSession
                    .createQuery("select distinct c from CourseDemand c " + "left join fetch c.courseRequests r "
                            + "left join fetch r.courseOffering as co "
                            + "left join fetch co.instructionalOffering as io "
                            + "where c.student.session.uniqueId=:sessionId")
                    .setLong("sessionId", session.getUniqueId()).list()) {
                for (org.unitime.timetable.model.CourseRequest request : demand.getCourseRequests()) {
                    iRequests.put(
                            demand.getUniqueId() + ":"
                                    + request.getCourseOffering().getInstructionalOffering().getUniqueId(),
                            request);
                }
            }
            iProgress.incProgress();

            iProgress.setPhase("Saving student enrollments...", getModel().getStudents().size());
            String statusToSet = getSolver().getProperties().getProperty("Save.StudentSectioningStatusToSet");
            if ("Default".equalsIgnoreCase(statusToSet)) {
                iStatusToSet = null;
                iResetStatus = true;
                iProgress.info("Setting student sectioning status to "
                        + (session.getDefaultSectioningStatus() == null ? "System Default (All Enabled)"
                                : "Session Default (" + session.getDefaultSectioningStatus().getLabel() + ")")
                        + ".");
            } else if (statusToSet != null && !statusToSet.isEmpty() && !statusToSet.equals("N/A")) {
                iStatusToSet = StudentSectioningStatus.getStatus(statusToSet, null, hibSession);
                if (iStatusToSet == null)
                    iProgress.warn("Student sectioning status " + statusToSet + " does not exist.");
                else
                    iProgress.info("Setting student sectioning status to " + iStatusToSet.getLabel());
            }
            if (iStatusToSet == null && !iResetStatus)
                iProgress.info("Keeping student sectioning status unchanged.");
            for (Iterator e = getModel().getStudents().iterator(); e.hasNext();) {
                Student student = (Student) e.next();
                iProgress.incProgress();
                if (student.isDummy())
                    continue;
                saveStudent(hibSession, student);
            }
            flush(hibSession);
        }

        if (getModel().getNrLastLikeRequests(false) > 0 || iProjections) {
            iProgress.setPhase("Computing expected/held space for online sectioning...", 0);
            getModel().computeOnlineSectioningInfos(getAssignment());
            iProgress.incProgress();

            Hashtable<Long, SectioningInfo> infoTable = new Hashtable<Long, SectioningInfo>();
            List<SectioningInfo> infos = hibSession.createQuery(
                    "select i from SectioningInfo i where i.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.session.uniqueId = :sessionId")
                    .setLong("sessionId", session.getUniqueId()).list();
            for (SectioningInfo info : infos)
                infoTable.put(info.getClazz().getUniqueId(), info);

            iProgress.setPhase("Saving expected/held space for online sectioning...",
                    getModel().getOfferings().size());
            for (Iterator e = getModel().getOfferings().iterator(); e.hasNext();) {
                Offering offering = (Offering) e.next();
                iProgress.incProgress();
                for (Iterator f = offering.getConfigs().iterator(); f.hasNext();) {
                    Config config = (Config) f.next();
                    for (Iterator g = config.getSubparts().iterator(); g.hasNext();) {
                        Subpart subpart = (Subpart) g.next();
                        for (Iterator h = subpart.getSections().iterator(); h.hasNext();) {
                            Section section = (Section) h.next();
                            Class_ clazz = iClasses.get(section.getId());
                            if (clazz == null)
                                continue;
                            SectioningInfo info = infoTable.get(section.getId());
                            if (info == null) {
                                info = new SectioningInfo();
                                info.setClazz(clazz);
                            }
                            info.setNbrExpectedStudents(section.getSpaceExpected());
                            info.setNbrHoldingStudents(section.getSpaceHeld());
                            hibSession.saveOrUpdate(info);
                            flushIfNeeded(hibSession);
                        }
                    }
                }
            }
        }

        // Update class enrollments
        /*
        if (!iProjections) {
        iProgress.setPhase("Updating enrollment counts...", getModel().getOfferings().size());
        for (Offering offering: getModel().getOfferings()) {
            iProgress.incProgress();
            for (Config config: offering.getConfigs()) {
                for (Subpart subpart: config.getSubparts()) {
                    for (Section section: subpart.getSections()) {
                        Class_ clazz = iClasses.get(section.getId());
                        if (clazz==null) continue;
                        int enrl = 0;
                        for (Enrollment en: section.getEnrollments())
                           if (!en.getStudent().isDummy()) enrl++;
                        clazz.setEnrollment(enrl);
                        hibSession.saveOrUpdate(clazz);
                        flushIfNeeded(hibSession);
                    }
                }
            }
            for (Course course: offering.getCourses()) {
               CourseOffering co = iCourses.get(course.getId());
               if (co == null) continue;
                int enrl = 0;
                for (Enrollment en: course.getEnrollments())
                   if (!en.getStudent().isDummy()) enrl++;
                co.setEnrollment(enrl);
                hibSession.saveOrUpdate(co);
                flushIfNeeded(hibSession);
            }
        }
        }
        */

        flush(hibSession);

        iProgress.setPhase("Done", 1);
        iProgress.incProgress();
    }

}