def test_mapping_in_groups(self): items = [self.SampleObject(i % 3, i + 1, i + 2) for i in range(10)] ordered = utils.dict_by_attr(items, 'a') self.assertEqual([0, 1, 2], ordered.keys()) self.assertEqual([(0, 1, 2), (0, 4, 5), (0, 7, 8), (0, 10, 11)], ordered[0]) self.assertEqual([(1, 2, 3), (1, 5, 6), (1, 8, 9), ], ordered[1]) self.assertEqual([(2, 3, 4), (2, 6, 7), (2, 9, 10), ], ordered[2])
def cache_conflicts(semester_year=None, semester_month=None, semester=None, sql=True, stdout=False): assert (semester_year and semester_month) or semester, "Semester year & month must be provided or the semester object." import sys # trash existing conflict data... if not semester: semester = courses.Semester.objects.get(year=semester_year, month=semester_month) #SectionConflict.objects.filter(semester=semester).delete() Syncer = Synchronizer(SectionConflict, SectionConflict.objects.values_list('id', flat=True)) sections = courses.Section.objects .select_related('course', 'semester') \ .by_semester(semester).prefetch_periods() section_courses = dict_by_attr(sections, 'course') mapping = {} for id, sid1, sid2 in SectionConflict.objects.filter(semester=semester).values_list('id', 'section1', 'section2'): mapping[(sid1, sid2)] = id conflicts = [] def log(msg): sys.stdout.write(msg) sys.stdout.flush() def perform_insert(conflicts): SectionConflict.objects.bulk_create(conflicts) count = 0 for course1, course2 in itertools.combinations(section_courses.keys(), 2): for section1, section2 in itertools.product(section_courses[course1], section_courses[course2]): if section1.conflicts_with(section2): if section1.id > section2.id: section1, section2 = section2, section1 count += 1 if sql: if count % 10000 == 0: perform_insert(conflicts) conflicts = [] log('.') if mapping.get((section1.id, section2.id), None) is None: conflicts.append( SectionConflict(section1=section1, section2=section2, semester=semester) ) else: Syncer.exclude_id(mapping[(section1.id, section2.id)]) else: log('C') Syncer.get_or_create( section1=section1, section2=section2, semester=semester, ) if sql and conflicts: perform_insert(conflicts) log('.') log('\n') Syncer.trim(semester=semester) log('\n')
def schedules(request, id=None, version=None): selection = None if id: selection = Selection.objects.get(id=id) section_ids = selection.section_ids else: section_ids = int_list(request.REQUEST.getlist('section_id')) created = False if not selection: selection, created = Selection.objects.get_or_create( section_ids=section_ids) sections = SectionProxy.objects.filter(id__in=section_ids) \ .select_related('course').prefetch_periods() selected_courses = dict_by_attr(sections, 'course') conflict_cache = ConflictCache( SectionConflict.objects.as_dictionary([s.id for s in sections])) # if check flag given, return only if we have a schedule or not. if request.REQUEST.get('check'): return {'context': has_schedule(selected_courses, conflict_cache)} # check the cache if not created and selection.api_cache: return {'context': json.loads(selection.api_cache)} schedules = compute_schedules(selected_courses, conflict_cache) periods = set(p for s in sections for p in s.get_periods()) timerange, dow_used = period_stats(periods) # note: if you change this, caches will have to be updated somehow context = { 'time_range': timerange, 'schedules': schedules, 'course_ids': list(set(c.id for c in selected_courses.keys())), 'section_ids': list( set(s.id for sections in selected_courses.values() for s in sections)), 'days_of_the_week': list(DAYS), 'id': selection.id, } selection.api_cache = json.dumps(context) selection.save() return {'context': context}
def full_select(self, year=None, month=None, amount=None): """Returns all courses in the given queryset, plus Sections, Periods, and SectionPeriod data. Optionally can have all related data to be filtered by semester year and month. Since this operation performs multiple selects and merges the resulting queries, the queryset is actively evaluated. """ from courses.models import SectionPeriod sps = SectionPeriod.objects.by_courses(self, year, month).select_related( 'period', 'section', 'section__course__id') # TODO: optimize into one loop sid2sps = dict_by_attr(sps, 'section_id') sid2periods = dict_by_attr(sps, 'section_id', 'period') cid2sections = dict_by_attr([sp.section for sp in sps], 'course.id') cid2sps = dict_by_attr(sps, 'section.course.id') for sp in sps: set_prefetch_cache(sp.section, 'section_times', sid2sps.get(sp.section.id, [])) set_prefetch_cache(sp.section, 'periods', sid2periods.get(sp.section.id, [])) def section_key(section): return section.number result = [] courses = self if amount is not None: courses[:amount] for course in courses: sections = sorted(set(cid2sections.get(course.id, [])), key=section_key) for section in sections: section.course = course set_prefetch_cache(course, 'sections', sections) result.append(course) return result
def schedules(request, slug=None, version=None): selection = None if slug: selection = Selection.objects.get(slug=slug) section_ids = selection.section_ids else: section_ids = int_list(request.GET.getlist('section_id')) created = False if not selection: selection, created = Selection.objects.get_or_create( section_ids=section_ids) sections = SectionProxy.objects.filter(id__in=section_ids) \ .select_related('course').prefetch_periods() selected_courses = dict_by_attr(sections, 'course') conflict_cache = ConflictCache( SectionConflict.objects.as_dictionary([s.id for s in sections])) # if check flag given, return only if we have a schedule or not. if request.GET.get('check'): return {'context': has_schedule(selected_courses, conflict_cache)} # check the cache if not created and selection.api_cache: print selection.api_cache return {'context': json.loads(selection.api_cache)} schedules = compute_schedules(selected_courses, conflict_cache) print schedules periods = set(p for s in sections for p in s.get_periods()) timerange, dow_used = period_stats(periods) # note: if you change this, caches will have to be updated somehow context = { 'time_range': timerange, 'schedules': schedules, 'course_ids': list(set( c.id for c in selected_courses.keys())), 'section_ids': list(set( s.id for sections in selected_courses.values() for s in sections )), 'days_of_the_week': list(DAYS), 'id': selection.slug } selection.api_cache = json.dumps(context) selection.save() return {'context': context}
def full_select(self, year=None, month=None, amount=None): """Returns all courses in the given queryset, plus Sections, Periods, and SectionPeriod data. Optionally can have all related data to be filtered by semester year and month. Since this operation performs multiple selects and merges the resulting queries, the queryset is actively evaluated. """ from courses.models import SectionPeriod sps = SectionPeriod.objects.by_courses(self, year, month).select_related( "period", "section", "section__course__id" ) # TODO: optimize into one loop sid2sps = dict_by_attr(sps, "section_id") sid2periods = dict_by_attr(sps, "section_id", "period") cid2sections = dict_by_attr([sp.section for sp in sps], "course.id") cid2sps = dict_by_attr(sps, "section.course.id") for sp in sps: set_prefetch_cache(sp.section, "section_times", sid2sps.get(sp.section.id, [])) set_prefetch_cache(sp.section, "periods", sid2periods.get(sp.section.id, [])) def section_key(section): return section.number result = [] courses = self if amount is not None: courses[:amount] for course in courses: sections = sorted(set(cid2sections.get(course.id, [])), key=section_key) for section in sections: section.course = course set_prefetch_cache(course, "sections", sections) result.append(course) return result
def test_mapping(self): items = [self.SampleObject(i, i + 1, i + 2) for i in range(10)] ordered = utils.dict_by_attr(items, 'a') self.assertEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ordered.keys()) self.assertEqual([(0, 1, 2)], ordered[0]) self.assertEqual([(1, 2, 3)], ordered[1]) self.assertEqual([(2, 3, 4)], ordered[2]) self.assertEqual([(3, 4, 5)], ordered[3]) self.assertEqual([(4, 5, 6)], ordered[4]) self.assertEqual([(5, 6, 7)], ordered[5]) self.assertEqual([(6, 7, 8)], ordered[6]) self.assertEqual([(7, 8, 9)], ordered[7]) self.assertEqual([(8, 9, 10)], ordered[8]) self.assertEqual([(9, 10, 11)], ordered[9])
def test_mapping_with_value(self): items = [self.SampleObject(i, i + 1, i + 2) for i in range(10)] ordered = utils.dict_by_attr(items, 'a', 'b') self.assertEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ordered.keys()) self.assertEqual([1], ordered[0]) self.assertEqual([2], ordered[1]) self.assertEqual([3], ordered[2]) self.assertEqual([4], ordered[3]) self.assertEqual([5], ordered[4]) self.assertEqual([6], ordered[5]) self.assertEqual([7], ordered[6]) self.assertEqual([8], ordered[7]) self.assertEqual([9], ordered[8]) self.assertEqual([10], ordered[9])
def test_mapping_in_groups(self): items = [self.SampleObject(i % 3, i + 1, i + 2) for i in range(10)] ordered = utils.dict_by_attr(items, 'a') self.assertEqual([0, 1, 2], ordered.keys()) self.assertEqual([(0, 1, 2), (0, 4, 5), (0, 7, 8), (0, 10, 11)], ordered[0]) self.assertEqual([ (1, 2, 3), (1, 5, 6), (1, 8, 9), ], ordered[1]) self.assertEqual([ (2, 3, 4), (2, 6, 7), (2, 9, 10), ], ordered[2])
def schedules(request, id=None, version=None): selection = None if id: selection = Selection.objects.get(id=id) section_ids = selection.section_ids else: section_ids = int_list(request.GET.getlist("section_id")) created = False if not selection: selection, created = Selection.objects.get_or_create(section_ids=section_ids) sections = SectionProxy.objects.filter(id__in=section_ids).select_related("course").prefetch_periods() selected_courses = dict_by_attr(sections, "course") conflict_cache = ConflictCache(SectionConflict.objects.as_dictionary([s.id for s in sections])) # if check flag given, return only if we have a schedule or not. if request.GET.get("check"): return {"context": has_schedule(selected_courses, conflict_cache)} # check the cache if not created and selection.api_cache: return {"context": json.loads(selection.api_cache)} schedules = compute_schedules(selected_courses, conflict_cache) periods = set(p for s in sections for p in s.get_periods()) timerange, dow_used = period_stats(periods) # note: if you change this, caches will have to be updated somehow context = { "time_range": timerange, "schedules": schedules, "course_ids": list(set(c.id for c in selected_courses.keys())), "section_ids": list(set(s.id for sections in selected_courses.values() for s in sections)), "days_of_the_week": list(DAYS), "id": selection.id, } selection.api_cache = json.dumps(context) selection.save() return {"context": context}
def reformat_to_selected_courses(self, sections): "Returns a dictionary of a course mapped to its sections objects." return dict_by_attr(sections, 'course')
def cache_conflicts(semester_year=None, semester_month=None, semester=None, sql=True, stdout=False): assert ( semester_year and semester_month ) or semester, "Semester year & month must be provided or the semester object." import sys # trash existing conflict data... if not semester: semester = courses.Semester.objects.get(year=semester_year, month=semester_month) with transaction.atomic(): # we don't want to increment IDs too quickly (ev 25 minutes) Syncer = Synchronizer(SectionConflict, SectionConflict.objects.values_list("id", flat=True)) sections = courses.Section.objects.select_related("course", "semester").by_semester(semester).prefetch_periods() section_courses = dict_by_attr(sections, "course") mapping = {} for id, sid1, sid2 in SectionConflict.objects.filter(semester=semester).values_list( "id", "section1", "section2" ): mapping[(sid1, sid2)] = id conflicts = [] def log(msg): sys.stdout.write(msg) sys.stdout.flush() def perform_insert(conflicts): SectionConflict.objects.bulk_create(conflicts) count = 0 for course1, course2 in itertools.combinations(section_courses.keys(), 2): for section1, section2 in itertools.product(section_courses[course1], section_courses[course2]): if section1.conflicts_with(section2): if section1.id > section2.id: section1, section2 = section2, section1 count += 1 if sql: if count % 500 == 0: perform_insert(conflicts) conflicts = [] log(".") if (section1.id, section2.id) not in mapping: log("C") conflicts.append(SectionConflict(section1=section1, section2=section2, semester=semester)) else: Syncer.exclude_id(mapping[(section1.id, section2.id)]) else: log("C") Syncer.get_or_create(section1=section1, section2=section2, semester=semester) if sql and conflicts: log("C") perform_insert(conflicts) log("\n") Syncer.trim(semester=semester) log("\n")
def cache_conflicts(semester_year=None, semester_month=None, semester=None, sql=True, stdout=False): assert ( semester_year and semester_month ) or semester, "Semester year & month must be provided or the semester object." import sys # trash existing conflict data... if not semester: semester = courses.Semester.objects.get(year=semester_year, month=semester_month) with transaction.atomic(): # we don't want to increment IDs too quickly (ev 25 minutes) Syncer = Synchronizer( SectionConflict, SectionConflict.objects.values_list('id', flat=True)) sections = courses.Section.objects.select_related('course', 'semester') \ .by_semester(semester).prefetch_periods() section_courses = dict_by_attr(sections, 'course') mapping = {} for id, sid1, sid2 in SectionConflict.objects.filter( semester=semester).values_list('id', 'section1', 'section2'): mapping[(sid1, sid2)] = id conflicts = [] def log(msg): sys.stdout.write(msg) sys.stdout.flush() def perform_insert(conflicts): SectionConflict.objects.bulk_create(conflicts) count = 0 for course1, course2 in itertools.combinations(section_courses.keys(), 2): for section1, section2 in itertools.product( section_courses[course1], section_courses[course2]): if section1.conflicts_with(section2): if section1.id > section2.id: section1, section2 = section2, section1 count += 1 if sql: if count % 500 == 0: perform_insert(conflicts) conflicts = [] log('.') if (section1.id, section2.id) not in mapping: log('C') conflicts.append( SectionConflict(section1=section1, section2=section2, semester=semester)) else: Syncer.exclude_id(mapping[(section1.id, section2.id)]) else: log('C') Syncer.get_or_create( section1=section1, section2=section2, semester=semester, ) if sql and conflicts: log('C') perform_insert(conflicts) log('\n') Syncer.trim(semester=semester) log('\n')
def selections(request, id, version=None): selection = Selection.objects.get(id=id) sections = models.Section.objects.filter(id__in=selection.section_ids) data = dict_by_attr(sections, "course_id", "id") return {"context": {"id": selection.id, "data": data}}