def _save(self, objects, datatype, should_update=False):
        self.push_datatype(datatype)

        def _should_report_progress(i, num):
            return i == num or num < 5 or i % (num / 5) == 0

        def _report_progress(i, num):
            logging.debug('...{}%\t({}/{})'.format(i * 100 / num, i, num))

        logging.debug("Saving some <{}> <{}> to local db".format(
            self._institution, datatype))
        for i, obj in enumerate(objects, start=1):
            identifiers = tuple(obj.get(pkey) for pkey in self.cur_primary_keys())
            should_add = self.doesnt_know_about(datatype=self.cur_datatype(),
                                                identifiers=identifiers)
            if should_add:
                self._local_db.add(obj, datatype=self.cur_datatype())
            elif should_update:
                self._local_db.update(obj, datatype=self.cur_datatype(),
                                           identifiers=primary_key_values)
            if _should_report_progress(i, len(objects)):
                _report_progress(i, len(objects))
        try:
            self._local_db.commit()
        except Exception as e:
            logging.error(str(e))
            logging.error("Failed to save <{}> <{}> to local_db".format(
                self._institution, self.cur_datatype()))
        else:
            verb = "Updated" if should_update else "Saved"
            logging.debug("{} some <{}> <{}> to local_db".format(
                verb, self._institution, self.cur_datatype()))

        self.pop_datatype()
def find_schedules(schedule_params, num_requested):
    """
    :param dict schedule_params: parameters to build the schedule with.
        Check :ref:`api/generate-schedules <api-generate-schedules>`
        for available parameters.
    """
    logging.info('Received schedule request')

    if 'term' not in schedule_params:
        logging.error("Schedule generation call did not specify <term>")
    term = schedule_params.get('term', '')
    institution = schedule_params.get('institution', 'ualberta')
    cal = classtime.brain.get_calendar(institution)

    if 'courses' not in schedule_params:
        logging.error("Schedule generation call did not specify <courses>")
    course_ids = schedule_params.get('courses', list())
    busy_times = schedule_params.get('busy-times', list())
    preferences = schedule_params.get('preferences', dict())
    electives_groups = schedule_params.get('electives', list())
    for electives_group in electives_groups:
        if 'courses' not in electives_group:
            logging.warning('"courses" not found for electives. q={}'.format(
                schedule_params))

    schedules = _generate_schedules_sat(cal, term, course_ids, busy_times, electives_groups, preferences)
    schedules = _condense_schedules(cal, schedules)
    schedules = sorted(schedules,
                       reverse=True,
                       key=lambda s: s.overall_score())
    if not schedules:
        logging.error('No schedules found for q={}'.format(
            schedule_params))
    else:
        logging.info('Returning {}/{} schedules from request q={}'.format(
            min(num_requested, len(schedules)),
            len(schedules),
            schedule_params))
        debug_msg = 'Request q={q}\n' + \
                    'Response: Returning {ret} schedules\n' + \
                    '          including {ret_like} more like them\n' + \
                    '          out of {tot} total generated\n' + \
                    'Returning:\n{ret_schedules}'
        logging.debug(debug_msg.format(
            q=schedule_params,
            ret=min(num_requested,
                len(schedules)),
            ret_like=sum([len(s.more_like_this)
                          for s in schedules[:num_requested]]),
            tot=len(schedules) + sum([len(s.more_like_this)
                                     for s in schedules]),
            ret_schedules=schedules[:num_requested]))
    return schedules[:num_requested]
def find_schedules(schedule_params, num_requested):
    """
    :param dict schedule_params: parameters to build the schedule with.
        Check :ref:`api/generate-schedules <api-generate-schedules>`
        for available parameters.
    """
    logging.info('Received schedule request')

    if 'term' not in schedule_params:
        logging.error("Schedule generation call did not specify <term>")
    term = schedule_params.get('term', '')
    institution = schedule_params.get('institution', 'ualberta')
    cal = classtime.brain.get_calendar(institution)

    if 'courses' not in schedule_params:
        logging.error("Schedule generation call did not specify <courses>")
    course_ids = schedule_params.get('courses', list())
    busy_times = schedule_params.get('busy-times', list())
    preferences = schedule_params.get('preferences', dict())
    electives_groups = schedule_params.get('electives', list())
    for electives_group in electives_groups:
        if 'courses' not in electives_group:
            logging.warning('"courses" not found for electives. q={}'.format(
                schedule_params))

    schedules = _generate_schedules_sat(cal, term, course_ids, busy_times,
                                        electives_groups, preferences)
    schedules = _condense_schedules(cal, schedules)
    schedules = sorted(schedules,
                       reverse=True,
                       key=lambda s: s.overall_score())
    if not schedules:
        logging.error('No schedules found for q={}'.format(schedule_params))
    else:
        logging.info('Returning {}/{} schedules from request q={}'.format(
            min(num_requested, len(schedules)), len(schedules),
            schedule_params))
        debug_msg = 'Request q={q}\n' + \
                    'Response: Returning {ret} schedules\n' + \
                    '          including {ret_like} more like them\n' + \
                    '          out of {tot} total generated\n' + \
                    'Returning:\n{ret_schedules}'
        logging.debug(
            debug_msg.format(q=schedule_params,
                             ret=min(num_requested, len(schedules)),
                             ret_like=sum([
                                 len(s.more_like_this)
                                 for s in schedules[:num_requested]
                             ]),
                             tot=len(schedules) +
                             sum([len(s.more_like_this) for s in schedules]),
                             ret_schedules=schedules[:num_requested]))
    return schedules[:num_requested]
Beispiel #4
0
def assert_valid_sections(sections, query):
    assert len(sections) > 0
    logging.debug('sections: {}'.format([s.get('asString') for s in sections]))
    for section in sections:
        assert section.get('institution') == query['q']['institution']
        assert section.get('term') == query['q']['term']
        assert section.get('asString') is not None
        elective_courses = [course
            for elective in query['q'].get('electives', dict())
            for course in elective.get('courses')]
        assert section.get('course') in query['q']['courses'] \
            or section.get('course') in elective_courses
        assert section.get('component') is not None
    def _fetch(self, datatype, **kwargs):
        if datatype not in self._remote_db.known_searches():
            logging.error('<{}> has no datatype <{}>'.format(
                self._institution, datatype))
            results = list()
        else:
            logging.debug("Fetching <{}> <{}> ({}) from remote db".format(
                self._institution, datatype, kwargs))
            results = self._remote_db.search(datatype, **kwargs)

            if 'section' in datatype.lower():
                results = self._attach_classtimes(results)
        return results
Beispiel #6
0
    def _fetch(self, datatype, **kwargs):
        if datatype not in self._remote_db.known_searches():
            logging.error('<{}> has no datatype <{}>'.format(
                self._institution, datatype))
            results = list()
        else:
            logging.debug("Fetching <{}> <{}> ({}) from remote db".format(
                self._institution, datatype, kwargs))
            results = self._remote_db.search(datatype, **kwargs)

            if 'section' in datatype.lower():
                results = self._attach_classtimes(results)
        return results
Beispiel #7
0
    def _get_components_single(self, course):
        def _attach_course_info(section_dict, course_dict):
            clone = dict(section_dict)
            section_dict.update(course_dict)
            section_dict.update(clone)
            section_dict['class_'] = clone.get('class')
            section_dict['asString'] = ' '.join([
                course_dict.get('asString'),
                section_dict.get('component'),
                section_dict.get('section')
            ])
            return section_dict

        identifiers = (self._term, course)
        course_info = self._local_db.get(datatype='course', identifiers=identifiers) \
                                    .to_dict()
        section_query = self._local_db.query(datatype='sections') \
                                      .filter_by(term=self._term, course=course)
        components = list()
        section_code_to_section = dict()
        for component in ['LEC', 'LAB', 'SEM', 'LBL']:
            section_models = section_query \
                .filter_by(component=component) \
                .order_by(self._local_db.Section.day.desc()) \
                .order_by(self._local_db.Section.startTime.desc()) \
                .order_by(self._local_db.Section.endTime.desc()) \
                .all()
            if len(section_models) == 0:
                continue
            logging.debug('{}:{} - {} found'.format(course, component,
                                                    len(section_models)))
            sections = [
                section_model.to_dict() for section_model in section_models
            ]
            sections = [
                _attach_course_info(section, course_info)
                for section in sections
            ]
            section_code_to_section.update(
                {section['section']: section
                 for section in sections})
            components.append(sections)

        for component in components:
            for section in component:
                if 'autoEnroll' in section and section[
                        'autoEnroll'] is not None:
                    section['autoEnrollComponent'] = section_code_to_section[
                        section['autoEnroll']]['component']

        return components
Beispiel #8
0
def assert_valid_sections(sections, query):
    assert len(sections) > 0
    logging.debug('sections: {}'.format([s.get('asString') for s in sections]))
    for section in sections:
        assert section.get('institution') == query['q']['institution']
        assert section.get('term') == query['q']['term']
        assert section.get('asString') is not None
        elective_courses = [
            course for elective in query['q'].get('electives', dict())
            for course in elective.get('courses')
        ]
        assert section.get('course') in query['q']['courses'] \
            or section.get('course') in elective_courses
        assert section.get('component') is not None
    def _fetch_multiple(self, datatype, identifiers):
        if datatype not in self._remote_db.known_searches():
            logging.error('<{}> has no datatype <{}>'.format(
                self._institution, datatype))
            results = list()
        else:
            logging.debug("Fetching <{}> <{}> <{}> from remote db".format(
                len(identifiers), self._institution, datatype))

            multiple_results = self._remote_db.search_multiple(
                [datatype] * len(identifiers),
                identifiers)

            if 'section' in datatype.lower():
                multiple_results = [self._attach_classtimes(results)
                                    for results in multiple_results]
        return multiple_results
    def _get_components_single(self, course):
        def _attach_course_info(section_dict, course_dict):
            clone = dict(section_dict)
            section_dict.update(course_dict)
            section_dict.update(clone)
            section_dict['class_'] = clone.get('class')
            section_dict['asString'] = ' '.join([course_dict.get('asString'),
                                                 section_dict.get('component'),
                                                 section_dict.get('section')])
            return section_dict

        identifiers = (self._term, course)
        course_info = self._local_db.get(datatype='course', identifiers=identifiers) \
                                    .to_dict()
        section_query = self._local_db.query(datatype='sections') \
                                      .filter_by(term=self._term, course=course)
        components = list()
        section_code_to_section = dict()
        for component in ['LEC', 'LAB', 'SEM', 'LBL']:
            section_models = section_query \
                .filter_by(component=component) \
                .order_by(self._local_db.Section.day.desc()) \
                .order_by(self._local_db.Section.startTime.desc()) \
                .order_by(self._local_db.Section.endTime.desc()) \
                .all()
            if len(section_models) == 0:
                continue
            logging.debug('{}:{} - {} found'.format(
                course, component, len(section_models)))
            sections = [section_model.to_dict()
                        for section_model in section_models]
            sections = [_attach_course_info(section, course_info)
                        for section in sections]
            section_code_to_section.update({
                section['section']: section
                for section in sections
            })
            components.append(sections)

        for component in components:
            for section in component:
                if 'autoEnroll' in section and section['autoEnroll'] is not None:
                    section['autoEnrollComponent'] = section_code_to_section[section['autoEnroll']]['component']

        return components
Beispiel #11
0
    def _fetch_multiple(self, datatype, identifiers):
        if datatype not in self._remote_db.known_searches():
            logging.error('<{}> has no datatype <{}>'.format(
                self._institution, datatype))
            results = list()
        else:
            logging.debug("Fetching <{}> <{}> <{}> from remote db".format(
                len(identifiers), self._institution, datatype))

            multiple_results = self._remote_db.search_multiple(
                [datatype] * len(identifiers), identifiers)

            if 'section' in datatype.lower():
                multiple_results = [
                    self._attach_classtimes(results)
                    for results in multiple_results
                ]
        return multiple_results
Beispiel #12
0
    def _save(self, objects, datatype, should_update=False):
        self.push_datatype(datatype)

        def _should_report_progress(i, num):
            return i == num or num < 5 or i % (num / 5) == 0

        def _report_progress(i, num):
            logging.debug('...{}%\t({}/{})'.format(i * 100 / num, i, num))

        logging.debug("Saving some <{}> <{}> to local db".format(
            self._institution, datatype))
        for i, obj in enumerate(objects, start=1):
            identifiers = tuple(
                obj.get(pkey) for pkey in self.cur_primary_keys())
            should_add = self.doesnt_know_about(datatype=self.cur_datatype(),
                                                identifiers=identifiers)
            if should_add:
                self._local_db.add(obj, datatype=self.cur_datatype())
            elif should_update:
                self._local_db.update(obj,
                                      datatype=self.cur_datatype(),
                                      identifiers=primary_key_values)
            if _should_report_progress(i, len(objects)):
                _report_progress(i, len(objects))
        try:
            self._local_db.commit()
        except Exception as e:
            logging.error(str(e))
            logging.error("Failed to save <{}> <{}> to local_db".format(
                self._institution, self.cur_datatype()))
        else:
            verb = "Updated" if should_update else "Saved"
            logging.debug("{} some <{}> <{}> to local_db".format(
                verb, self._institution, self.cur_datatype()))

        self.pop_datatype()
 def _report_progress(i, num):
     logging.debug('...{}%\t({}/{})'.format(i * 100 / num, i, num))
Beispiel #14
0
 def _report_progress(i, num):
     logging.debug('...{}%\t({}/{})'.format(i * 100 / num, i, num))