    001package net.sf.cpsolver.coursett.model;
    002
    003import java.util.Collection;
    004import java.util.HashSet;
    005import java.util.HashMap;
    006import java.util.Map;
    007import java.util.Set;
    008
    009import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
    010import net.sf.cpsolver.coursett.constraint.JenrlConstraint;
    011
    012/**
    013 * Student.
    014 *
    015 * @version CourseTT 1.2 (University Course Timetabling)<br>
    016 *          Copyright (C) 2006 - 2010 Tomáš Müller<br>
    017 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    018 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    019 * <br>
    020 *          This library is free software; you can redistribute it and/or modify
    021 *          it under the terms of the GNU Lesser General Public License as
    022 *          published by the Free Software Foundation; either version 3 of the
    023 *          License, or (at your option) any later version. <br>
    024 * <br>
    025 *          This library is distributed in the hope that it will be useful, but
    026 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    027 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    028 *          Lesser General Public License for more details. <br>
    029 * <br>
    030 *          You should have received a copy of the GNU Lesser General Public
    031 *          License along with this library; if not see
    032 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    033 */
    034public class Student implements Comparable<Student> {
    035    private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Student.class);
    036    public static boolean USE_DISTANCE_CACHE = false;
    037    Long iStudentId = null;
    038    HashMap<Long, Double> iOfferings = new HashMap<Long, Double>();
    039    Set<Lecture> iLectures = new HashSet<Lecture>();
    040    Set<Configuration> iConfigurations = new HashSet<Configuration>();
    041    HashMap<Long, Set<Lecture>> iCanNotEnrollSections = null;
    042    HashMap<Student, Double> iDistanceCache = null;
    043    HashSet<Placement> iCommitedPlacements = null;
    044    private String iAcademicArea = null, iAcademicClassification = null, iMajor = null, iCurriculum = null;
    045    HashMap<Long, Double> iOfferingPriority = new HashMap<Long, Double>();
    046    private InstructorConstraint iInstructor = null;
    047
    048    public Student(Long studentId) {
    049        iStudentId = studentId;
    050    }
    051
    052    public void addOffering(Long offeringId, double weight, Double priority) {
    053        iOfferings.put(offeringId, weight);
    054        if (priority != null) iOfferingPriority.put(offeringId, priority);
    055    }
    056
    057    public void addOffering(Long offeringId, double weight) {
    058        addOffering(offeringId, weight, null);
    059    }
    060
    061    public Map<Long, Double> getOfferingsMap() {
    062        return iOfferings;
    063    }
    064
    065    public Set<Long> getOfferings() {
    066        return iOfferings.keySet();
    067    }
    068
    069    public boolean hasOffering(Long offeringId) {
    070        return iOfferings.containsKey(offeringId);
    071    }
    072
    073    public InstructorConstraint getInstructor() { return iInstructor; }
    074
    075    public void setInstructor(InstructorConstraint instructor) { iInstructor = instructor; }
    076
    077    /**
    078     * Priority of an offering (for the student). Null if not used, or between
    079     * zero (no priority) and one (highest priority)
    080     */
    081    public Double getPriority(Long offeringId) {
    082        return offeringId == null ? null : iOfferingPriority.get(offeringId);
    083    }
    084
    085    public Double getPriority(Configuration configuration) {
    086        return configuration == null ? null : getPriority(configuration.getOfferingId());
    087    }
    088
    089    public Double getPriority(Lecture lecture) {
    090        return lecture == null ? null : getPriority(lecture.getConfiguration());
    091    }
    092
    093    public Double getConflictingPriorty(Lecture l1, Lecture l2) {
    094        // Conflicting priority is the lower of the two priorities
    095        Double p1 = getPriority(l1);
    096        Double p2 = getPriority(l2);
    097        return p1 == null ? null : p2 == null ? null : Math.min(p1, p2);
    098    }
    099
    100    public double getOfferingWeight(Configuration configuration) {
    101        if (configuration == null)
    102            return 1.0;
    103        return getOfferingWeight(configuration.getOfferingId());
    104    }
    105
    106    public double getOfferingWeight(Long offeringId) {
    107        Double weight = iOfferings.get(offeringId);
    108        return (weight == null ? 0.0 : weight.doubleValue());
    109    }
    110
    111    public boolean canUnenroll(Lecture lecture) {
    112        if (getInstructor() != null)
    113            return !getInstructor().variables().contains(lecture);
    114        return true;
    115    }
    116
    117    public boolean canEnroll(Lecture lecture) {
    118        if (iCanNotEnrollSections != null) {
    119            Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId());
    120            return canEnroll(canNotEnrollLectures, lecture, true);
    121        }
    122        return true;
    123    }
    124
    125    private boolean canEnroll(Set<Lecture> canNotEnrollLectures, Lecture lecture, boolean checkParents) {
    126        if (canNotEnrollLectures == null)
    127            return true;
    128        if (canNotEnrollLectures.contains(lecture))
    129            return false;
    130        if (checkParents) {
    131            Lecture parent = lecture.getParent();
    132            while (parent != null) {
    133                if (canNotEnrollLectures.contains(parent))
    134                    return false;
    135                parent = parent.getParent();
    136            }
    137        }
    138        if (lecture.hasAnyChildren()) {
    139            for (Long subpartId: lecture.getChildrenSubpartIds()) {
    140                boolean canEnrollChild = false;
    141                for (Lecture childLecture : lecture.getChildren(subpartId)) {
    142                    if (canEnroll(canNotEnrollLectures, childLecture, false)) {
    143                        canEnrollChild = true;
    144                        break;
    145                    }
    146                }
    147                if (!canEnrollChild)
    148                    return false;
    149            }
    150        }
    151        return true;
    152    }
    153
    154    public void addCanNotEnroll(Lecture lecture) {
    155        if (iCanNotEnrollSections == null)
    156            iCanNotEnrollSections = new HashMap<Long, Set<Lecture>>();
    157        if (lecture.getConfiguration() == null) {
    158            sLogger.warn("Student.addCanNotEnroll(" + lecture
    159                    + ") -- given lecture has no configuration associated with.");
    160            return;
    161        }
    162        Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId());
    163        if (canNotEnrollLectures == null) {
    164            canNotEnrollLectures = new HashSet<Lecture>();
    165            iCanNotEnrollSections.put(lecture.getConfiguration().getOfferingId(), canNotEnrollLectures);
    166        }
    167        canNotEnrollLectures.add(lecture);
    168    }
    169
    170    public void addCanNotEnroll(Long offeringId, Collection<Lecture> lectures) {
    171        if (lectures == null || lectures.isEmpty())
    172            return;
    173        if (iCanNotEnrollSections == null)
    174            iCanNotEnrollSections = new HashMap<Long, Set<Lecture>>();
    175        Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(offeringId);
    176        if (canNotEnrollLectures == null) {
    177            canNotEnrollLectures = new HashSet<Lecture>();
    178            iCanNotEnrollSections.put(offeringId, canNotEnrollLectures);
    179        }
    180        canNotEnrollLectures.addAll(lectures);
    181    }
    182
    183    public Map<Long, Set<Lecture>> canNotEnrollSections() {
    184        return iCanNotEnrollSections;
    185    }
    186
    187    public void addLecture(Lecture lecture) {
    188        iLectures.add(lecture);
    189    }
    190
    191    public void removeLecture(Lecture lecture) {
    192        iLectures.remove(lecture);
    193    }
    194
    195    public Set<Lecture> getLectures() {
    196        return iLectures;
    197    }
    198
    199    public void addConfiguration(Configuration config) {
    200        iConfigurations.add(config);
    201    }
    202
    203    public void removeConfiguration(Configuration config) {
    204        iConfigurations.remove(config);
    205    }
    206
    207    public Set<Configuration> getConfigurations() {
    208        return iConfigurations;
    209    }
    210
    211    public Long getId() {
    212        return iStudentId;
    213    }
    214
    215    public double getDistance(Student student) {
    216        Double dist = (USE_DISTANCE_CACHE && iDistanceCache != null ? iDistanceCache.get(student) : null);
    217        if (dist == null) {
    218            int same = 0;
    219            for (Long o : getOfferings()) {
    220                if (student.getOfferings().contains(o))
    221                    same++;
    222            }
    223            double all = student.getOfferings().size() + getOfferings().size();
    224            double dif = all - 2.0 * same;
    225            dist = new Double(dif / all);
    226            if (USE_DISTANCE_CACHE) {
    227                if (iDistanceCache == null)
    228                    iDistanceCache = new HashMap<Student, Double>();
    229                iDistanceCache.put(student, dist);
    230            }
    231        }
    232        return dist.doubleValue();
    233    }
    234
    235    public void clearDistanceCache() {
    236        if (USE_DISTANCE_CACHE && iDistanceCache != null)
    237            iDistanceCache.clear();
    238    }
    239
    240    @Override
    241    public String toString() {
    242        return String.valueOf(getId());
    243    }
    244
    245    @Override
    246    public int hashCode() {
    247        return getId().hashCode();
    248    }
    249
    250    @Override
    251    public int compareTo(Student s) {
    252        return getId().compareTo(s.getId());
    253    }
    254
    255    @Override
    256    public boolean equals(Object o) {
    257        if (o == null || !(o instanceof Student))
    258            return false;
    259        return getId().equals(((Student) o).getId());
    260    }
    261
    262    public void addCommitedPlacement(Placement placement) {
    263        if (iCommitedPlacements == null)
    264            iCommitedPlacements = new HashSet<Placement>();
    265        iCommitedPlacements.add(placement);
    266    }
    267
    268    public Set<Placement> getCommitedPlacements() {
    269        return iCommitedPlacements;
    270    }
    271
    272    public Set<Placement> conflictPlacements(Placement placement) {
    273        if (iCommitedPlacements == null)
    274            return null;
    275        Set<Placement> ret = new HashSet<Placement>();
    276        Lecture lecture = placement.variable();
    277        for (Placement commitedPlacement : iCommitedPlacements) {
    278            Lecture commitedLecture = commitedPlacement.variable();
    279            if (lecture.getSchedulingSubpartId() != null
    280                    && lecture.getSchedulingSubpartId().equals(commitedLecture.getSchedulingSubpartId()))
    281                continue;
    282            if (lecture.isToIgnoreStudentConflictsWith(commitedLecture)) continue;
    283            if (JenrlConstraint.isInConflict(commitedPlacement, placement, ((TimetableModel)placement.variable().getModel()).getDistanceMetric()))
    284                ret.add(commitedPlacement);
    285        }
    286        return ret;
    287    }
    288
    289    public int countConflictPlacements(Placement placement) {
    290        Set<Placement> conflicts = conflictPlacements(placement);
    291        double w = getOfferingWeight((placement.variable()).getConfiguration());
    292        return (int) Math.round(conflicts == null ? 0 : avg(w, 1.0) * conflicts.size());
    293    }
    294
    295    public double getJenrlWeight(Lecture l1, Lecture l2) {
    296        if (getInstructor() != null && (getInstructor().variables().contains(l1) || getInstructor().variables().contains(l2)))
    297            return 1.0;
    298        return avg(getOfferingWeight(l1.getConfiguration()), getOfferingWeight(l2.getConfiguration()));
    299    }
    300
    301    public double avg(double w1, double w2) {
    302        return Math.sqrt(w1 * w2);
    303    }
    304
    305    public String getAcademicArea() {
    306        return iAcademicArea;
    307    }
    308
    309    public void setAcademicArea(String acadArea) {
    310        iAcademicArea = acadArea;
    311    }
    312
    313    public String getAcademicClassification() {
    314        return iAcademicClassification;
    315    }
    316
    317    public void setAcademicClassification(String acadClasf) {
    318        iAcademicClassification = acadClasf;
    319    }
    320
    321    public String getMajor() {
    322        return iMajor;
    323    }
    324
    325    public void setMajor(String major) {
    326        iMajor = major;
    327    }
    328
    329    public String getCurriculum() {
    330        return iCurriculum;
    331    }
    332
    333    public void setCurriculum(String curriculum) {
    334        iCurriculum = curriculum;
    335    }
    336}