Exemplo n.º 1
0
    def __init__(self,
                 courses,
                 session="2014W",
                 terms=(1, 2),
                 refresh=False,
                 duplicates=True,
                 ssc_conn=None):
        """Schedule

        :type  courses: list|tuple
        :param courses: ["CPSC 304", ...]
        :type  session: str|unicode
        :param session: Session you want to schedule for
        :type  terms: tuple|list
        :param terms: List of terms you want to schedule courses in;
            i.e., [1] for only first term, [1, 2] for whole session etc.
        :param refresh: Invalidate all cached data for relevant courses
        :type ssc_conn: SSCConnection
        """
        self.ssc_conn = SSCConnection() if ssc_conn is None else ssc_conn
        self.courses = {
            c: self.ssc_conn.get_course(c,
                                        session,
                                        refresh=refresh,
                                        duplicates=duplicates)
            for c in courses
        }
        self.terms = terms
        self.session = session
        self._constraints = []
Exemplo n.º 2
0
    def __init__(self, courses, session="2014W", terms=(1, 2), refresh=False, duplicates=True, ssc_conn=None):
        """Schedule

        :type  courses: list|tuple
        :param courses: ["CPSC 304", ...]
        :type  session: str|unicode
        :param session: Session you want to schedule for
        :type  terms: tuple|list
        :param terms: List of terms you want to schedule courses in;
            i.e., [1] for only first term, [1, 2] for whole session etc.
        :param refresh: Invalidate all cached data for relevant courses
        :type ssc_conn: SSCConnection
        """
        self.ssc_conn = SSCConnection() if ssc_conn is None else ssc_conn
        self.courses = {
            c: self.ssc_conn.get_course(c, session, refresh=refresh, duplicates=duplicates) for c in courses
        }
        self.terms = terms
        self.session = session
        self._constraints = []
Exemplo n.º 3
0
class Scheduler(object):
    def __init__(self,
                 courses,
                 session="2014W",
                 terms=(1, 2),
                 refresh=False,
                 duplicates=True,
                 ssc_conn=None):
        """Schedule

        :type  courses: list|tuple
        :param courses: ["CPSC 304", ...]
        :type  session: str|unicode
        :param session: Session you want to schedule for
        :type  terms: tuple|list
        :param terms: List of terms you want to schedule courses in;
            i.e., [1] for only first term, [1, 2] for whole session etc.
        :param refresh: Invalidate all cached data for relevant courses
        :type ssc_conn: SSCConnection
        """
        self.ssc_conn = SSCConnection() if ssc_conn is None else ssc_conn
        self.courses = {
            c: self.ssc_conn.get_course(c,
                                        session,
                                        refresh=refresh,
                                        duplicates=duplicates)
            for c in courses
        }
        self.terms = terms
        self.session = session
        self._constraints = []

    ##################
    # Public Methods #
    ##################

    def generate_schedules(self, bad_statuses=("Full", "Blocked")):
        """Generate valid schedules"""
        schedules_by_course = {}
        for name, course in self.courses.items():
            logging.info("Generating schedules for {} ...".format(name))
            # Courses should have at least one activity
            if not course.activities:
                raise NoActivitiesError(name)
            acts = course.activities
            r = sum(c[1] for c in course.num_section_constraints)
            combs = combinations(acts, r)
            # Makes sure:
            # * num_section_constraints from Course are met
            # * all activities are in terms that we want (according to self.terms)
            # * all activities themselves are in the same term (UNLESS they're multiterm)
            # * no activities are included that are Full/Blocked
            # * all constraints from the course are satisfied
            filter_func = lambda combo: all([
                all(
                    sum(int(isinstance(act, constraint[0]))
                        for act in combo) == constraint[1]
                    for constraint in course.num_section_constraints),
                all(act.term in self.terms for act in combo),
                (check_equal([act.term
                              for act in combo]) or any(act.is_multi_term
                                                        for act in combo)),
                all(a.status not in bad_statuses for a in combo),
                all(c(combo) for c in course.constraints)
            ])
            filtered_combs = filter(filter_func, combs)
            schedules_by_course[name] = filtered_combs
            logging.info("Schedules for {} generated.".format(name))

        # Get all combinations (so all possible schedules); but we still need to check for conflicts
        all_scheds = self._generate_combinations(schedules_by_course)
        # Makes sure:
        # * Schedules don't have recurring courses
        # * Don't have conflicts
        filter_func = lambda s: all([
            # all_unique(a.section for t in s for a in t),  # This seems to be unnecessary
            not self._check_schedule_conflicts(s)
        ])
        # Now we filter away all schedules that have conflicts
        filtered_all_scheds = ifilter(filter_func, all_scheds)
        logging.info("Generating all valid schedules ...")
        schedules = [Schedule(sched) for sched in filtered_all_scheds]
        # Now we filter away all the schedules that don't obey constraints
        filter_func = lambda s: all(c(s) for c in self._constraints)
        schedules = filter(filter_func, schedules)
        logging.info("Found {} valid schedules.".format(len(schedules)))

        return schedules

    def add_constraint(self, constraint):
        """Add constraint ``constraint`` to list of constraints

        :type  constraint: callable
        :param constraint: A callable that takes a Schedule
            and returns True or False depending on whether
            a constraint is met
        """
        self._constraints.append(constraint)

    ###################
    # Private Methods #
    ###################

    def _generate_combinations(self, scheds_by_course):
        """Generate all possible schedules given ``scheds_by_course``

        :type  scheds_by_course: dict
        :param scheds_by_course: Dictionary of possible schedules by course
        :rtype: list
        :return: Combination of all schedules
        """
        if not scheds_by_course:
            return []

        schedules = list(product(*scheds_by_course.itervalues()))
        return schedules

    @classmethod
    def _check_conflict(cls, act1, act2):
        """Checks for a scheduling conflict between two Activity instances"""
        return all([
            # Check time conflict
            act1.start_time < act2.end_time,
            act1.end_time > act2.start_time,
            # Check if they are on the same day(s)
            act1.days & act2.days,  # set intersection
            # Check that they are in the same term
            act1.term == act2.term
        ])

    @classmethod
    def _check_conflicts(cls, current_act, other_acts):
        """Check for scheduling conflicts between ``current_act`` and ``other_acts``

        :type  current_act: Activity
        :type  other_acts: [Activity, ...]
        """
        return any(
            cls._check_conflict(current_act, other_act)
            for other_act in other_acts)

    @classmethod
    def _check_schedule_conflicts(cls, schedule):
        """Check for conflicts in ``schedule``"""
        acts = [a for t in schedule for a in t]
        for current_act in acts:
            other_acts = (a for a in acts if a != current_act)
            if cls._check_conflicts(current_act, other_acts):
                return True
        else:
            return False
Exemplo n.º 4
0
class Scheduler(object):
    def __init__(self, courses, session="2014W", terms=(1, 2), refresh=False, duplicates=True, ssc_conn=None):
        """Schedule

        :type  courses: list|tuple
        :param courses: ["CPSC 304", ...]
        :type  session: str|unicode
        :param session: Session you want to schedule for
        :type  terms: tuple|list
        :param terms: List of terms you want to schedule courses in;
            i.e., [1] for only first term, [1, 2] for whole session etc.
        :param refresh: Invalidate all cached data for relevant courses
        :type ssc_conn: SSCConnection
        """
        self.ssc_conn = SSCConnection() if ssc_conn is None else ssc_conn
        self.courses = {
            c: self.ssc_conn.get_course(c, session, refresh=refresh, duplicates=duplicates) for c in courses
        }
        self.terms = terms
        self.session = session
        self._constraints = []

    ##################
    # Public Methods #
    ##################

    def generate_schedules(self, bad_statuses=("Full", "Blocked")):
        """Generate valid schedules"""
        schedules_by_course = {}
        for name, course in self.courses.items():
            logging.info("Generating schedules for {} ...".format(name))
            # Courses should have at least one activity
            if not course.activities:
                raise NoActivitiesError(name)
            acts = course.activities
            r = sum(c[1] for c in course.num_section_constraints)
            combs = combinations(acts, r)
            # Makes sure:
            # * num_section_constraints from Course are met
            # * all activities are in terms that we want (according to self.terms)
            # * all activities themselves are in the same term (UNLESS they're multiterm)
            # * no activities are included that are Full/Blocked
            # * all constraints from the course are satisfied
            filter_func = lambda combo: all(
                [
                    all(
                        sum(int(isinstance(act, constraint[0])) for act in combo) == constraint[1]
                        for constraint in course.num_section_constraints
                    ),
                    all(act.term in self.terms for act in combo),
                    (check_equal([act.term for act in combo]) or any(act.is_multi_term for act in combo)),
                    all(a.status not in bad_statuses for a in combo),
                    all(c(combo) for c in course.constraints),
                ]
            )
            filtered_combs = filter(filter_func, combs)
            schedules_by_course[name] = filtered_combs
            logging.info("Schedules for {} generated.".format(name))

        # Get all combinations (so all possible schedules); but we still need to check for conflicts
        all_scheds = self._generate_combinations(schedules_by_course)
        # Makes sure:
        # * Schedules don't have recurring courses
        # * Don't have conflicts
        filter_func = lambda s: all(
            [
                # all_unique(a.section for t in s for a in t),  # This seems to be unnecessary
                not self._check_schedule_conflicts(s)
            ]
        )
        # Now we filter away all schedules that have conflicts
        filtered_all_scheds = ifilter(filter_func, all_scheds)
        logging.info("Generating all valid schedules ...")
        schedules = [Schedule(sched) for sched in filtered_all_scheds]
        # Now we filter away all the schedules that don't obey constraints
        filter_func = lambda s: all(c(s) for c in self._constraints)
        schedules = filter(filter_func, schedules)
        logging.info("Found {} valid schedules.".format(len(schedules)))

        return schedules

    def add_constraint(self, constraint):
        """Add constraint ``constraint`` to list of constraints

        :type  constraint: callable
        :param constraint: A callable that takes a Schedule
            and returns True or False depending on whether
            a constraint is met
        """
        self._constraints.append(constraint)

    ###################
    # Private Methods #
    ###################

    def _generate_combinations(self, scheds_by_course):
        """Generate all possible schedules given ``scheds_by_course``

        :type  scheds_by_course: dict
        :param scheds_by_course: Dictionary of possible schedules by course
        :rtype: list
        :return: Combination of all schedules
        """
        if not scheds_by_course:
            return []

        schedules = list(product(*scheds_by_course.itervalues()))
        return schedules

    @classmethod
    def _check_conflict(cls, act1, act2):
        """Checks for a scheduling conflict between two Activity instances"""
        return all(
            [
                # Check time conflict
                act1.start_time < act2.end_time,
                act1.end_time > act2.start_time,
                # Check if they are on the same day(s)
                act1.days & act2.days,  # set intersection
                # Check that they are in the same term
                act1.term == act2.term,
            ]
        )

    @classmethod
    def _check_conflicts(cls, current_act, other_acts):
        """Check for scheduling conflicts between ``current_act`` and ``other_acts``

        :type  current_act: Activity
        :type  other_acts: [Activity, ...]
        """
        return any(cls._check_conflict(current_act, other_act) for other_act in other_acts)

    @classmethod
    def _check_schedule_conflicts(cls, schedule):
        """Check for conflicts in ``schedule``"""
        acts = [a for t in schedule for a in t]
        for current_act in acts:
            other_acts = (a for a in acts if a != current_act)
            if cls._check_conflicts(current_act, other_acts):
                return True
        else:
            return False