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]
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
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
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
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 _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))