Example #1
0
 def test_has_none(self):
     _, section, _, _ = get_or_create_course_and_section(
         "CIS-120-001", TEST_SEMESTER)
     instructor, _ = Instructor.objects.get_or_create(name="Rajiv Gandhi")
     section.instructors.add(instructor)
     recompute_precomputed_fields()
     self.assertFalse(Section.objects.get(id=section.id).has_reviews)
Example #2
0
    def setUp(self):

        _, self.cis_160_001 = create_mock_data(
            code="CIS-160-001", semester=TEST_SEMESTER, meeting_days="TR"
        )

        _, self.cis_160_201 = create_mock_data(
            code="CIS-160-201", semester=TEST_SEMESTER, meeting_days="M"
        )
        self.cis_160_201.activity = "REC"
        self.cis_160_201.save()

        _, self.cis_160_202 = create_mock_data(
            code="CIS-160-202", semester=TEST_SEMESTER, meeting_days="W"
        )
        self.cis_160_202.activity = "REC"
        self.cis_160_202.save()

        def save_all():
            for section in [self.cis_160_001, self.cis_160_201, self.cis_160_202]:
                section.save()

        self.save_all = save_all
        self.all_codes = {"CIS-160"}
        self.non_open_statuses = [
            status[0] for status in Section.STATUS_CHOICES if status[0] not in ["O"]
        ]

        recompute_precomputed_fields()

        self.client = APIClient()
        set_semester()
Example #3
0
 def test_two_updates(self):
     up = record_update(self.section, TEST_SEMESTER, "C", "O", True, "JSON")
     up.save()
     up = record_update(self.section, TEST_SEMESTER, "O", "C", True, "JSON")
     up.save()
     recompute_precomputed_fields()
     self.assertTrue(
         Section.objects.get(full_code="CIS-120-001").has_status_updates)
Example #4
0
    def setUp(self):
        _, self.cis_120_001 = create_mock_data(
            "CIS-120-001", TEST_SEMESTER
        )  # time 11.0-12.0, days MWF

        _, self.cis_120_002 = create_mock_data(
            code="CIS-120-002", semester=TEST_SEMESTER, start=1200, end=1330, meeting_days="TR"
        )

        _, self.cis_160_001 = create_mock_data(
            code="CIS-160-001", semester=TEST_SEMESTER, start=500, end=630, meeting_days="TR"
        )

        _, self.cis_160_201 = create_mock_data(
            code="CIS-160-201", semester=TEST_SEMESTER, start=1100, end=1200, meeting_days="M"
        )
        self.cis_160_201.activity = "REC"
        self.cis_160_201.save()

        _, self.cis_160_202 = create_mock_data(
            code="CIS-160-202", semester=TEST_SEMESTER, start=1400, end=1500, meeting_days="W"
        )
        self.cis_160_202.activity = "REC"
        self.cis_160_202.save()

        _, self.cis_121_001 = create_mock_data(code="CIS-121-001", semester=TEST_SEMESTER)
        set_meetings(
            self.cis_121_001,
            [
                {
                    "building_code": "LLAB",
                    "room_code": "10",
                    "days": "MT",
                    "begin_time_24": 900,
                    "begin_time": "9:00 AM",
                    "end_time_24": 1000,
                    "end_time": "10:00 AM",
                },
                {
                    "building_code": "LLAB",
                    "room_code": "10",
                    "days": "WR",
                    "begin_time_24": 1330,
                    "begin_time": "1:30 PM",
                    "end_time_24": 1430,
                    "end_time": "2:30 PM",
                },
            ],
        )

        _, self.cis_262_001 = create_mock_async_class(code="CIS-262-001", semester=TEST_SEMESTER)

        recompute_precomputed_fields()

        self.all_codes = {"CIS-120", "CIS-160", "CIS-121", "CIS-262"}

        self.client = APIClient()
        set_semester()
Example #5
0
    def test_all_semesters(self):
        recompute_precomputed_fields()
        self.assertEquals(Course.objects.get(id=self.cis_160.id).num_activities, 2)
        self.assertEquals(Section.objects.get(id=self.cis_160_001.id).num_meetings, 4)
        self.assertEquals(Section.objects.get(id=self.cis_160_201.id).num_meetings, 3)
        self.assertEquals(Section.objects.get(id=self.cis_160_002.id).num_meetings, 3)

        self.assertEquals(Course.objects.get(id=self.cis_120.id).num_activities, 1)
        self.assertEquals(Section.objects.get(id=self.cis_120_001.id).num_meetings, 3)

        self.assertEquals(Course.objects.get(id=self.cis_120_old.id).num_activities, 1)
        self.assertEquals(Section.objects.get(id=self.cis_120_001_old.id).num_meetings, 3)
Example #6
0
 def test_has_one(self):
     _, section, _, _ = get_or_create_course_and_section(
         "CIS-120-001", TEST_SEMESTER)
     instructor, _ = Instructor.objects.get_or_create(name="Rajiv Gandhi")
     section.instructors.add(instructor)
     import_review(section,
                   instructor,
                   None,
                   10,
                   None, {"instructor_quality": 4},
                   lambda x, y=None: None)
     recompute_precomputed_fields()
     self.assertTrue(Section.objects.get(id=section.id).has_reviews)
Example #7
0
def create_review(section_code,
                  semester,
                  instructor_name,
                  bits,
                  responses=100):
    _, section, _, _ = get_or_create_course_and_section(section_code, semester)
    instructor, _ = Instructor.objects.get_or_create(name=instructor_name)
    section.instructors.add(instructor)
    import_review(section,
                  instructor,
                  None,
                  responses,
                  None,
                  bits,
                  lambda x, y=None: None)
    recompute_precomputed_fields()
def get_demand_data(semesters, section_query="", verbose=False):
    current_semester = get_current_semester()
    output_dict = dict()

    recompute_precomputed_fields(verbose=True)

    if verbose:
        print(f"Computing demand data for semesters {str(semesters)}...")
    for semester_num, semester in enumerate(semesters):
        try:
            validate_add_drop_semester(semester)
        except ValidationError:
            if verbose:
                print(
                    f"Skipping semester {semester} (unsupported kind for stats)."
                )
            continue
        add_drop_period = get_or_create_add_drop_period(semester)

        if verbose:
            print(f"Processing semester {semester}, "
                  f"{(semester_num+1)}/{len(semesters)}.\n")

        output_dict[semester] = []  # list of demand data dicts
        section_id_to_object = dict(
        )  # maps section id to section object (for this semester)
        volume_changes_map = dict(
        )  # maps section id to list of volume changes
        status_updates_map = dict(
        )  # maps section id to list of status updates

        iterator_wrapper = tqdm if verbose else (lambda x: x)
        if verbose:
            print("Indexing relevant sections...")
        for section in iterator_wrapper(
                Section.objects.filter(
                    extra_metrics_section_filters,
                    full_code__startswith=section_query,
                    course__semester=semester,
                ).annotate(
                    efficient_semester=F("course__semester"), ).distinct()):
            section_id_to_object[section.id] = section
            volume_changes_map[section.id] = []
            status_updates_map[section.id] = []

        if verbose:
            print(
                "Computing registration volume changes over time for each section..."
            )
        for registration in iterator_wrapper(
                Registration.objects.filter(
                    section_id__in=section_id_to_object.keys()).annotate(
                        section_capacity=F("section__capacity"))):
            section_id = registration.section_id
            volume_changes_map[section_id].append({
                "date": registration.created_at,
                "volume_change": 1
            })
            deactivated_at = registration.deactivated_at
            if deactivated_at is not None:
                volume_changes_map[section_id].append({
                    "date": deactivated_at,
                    "volume_change": -1
                })

        if verbose:
            print("Collecting status updates over time for each section...")
        for status_update in iterator_wrapper(
                StatusUpdate.objects.filter(
                    section_id__in=section_id_to_object.keys(),
                    in_add_drop_period=True)):
            section_id = status_update.section_id
            status_updates_map[section_id].append({
                "date":
                status_update.created_at,
                "old_status":
                status_update.old_status,
                "new_status":
                status_update.new_status,
            })

        if verbose:
            print("Joining updates for each section and sorting...")
        all_changes = sorted(
            [{
                "type": "status_update",
                "section_id": section_id,
                **update
            }
             for section_id, status_updates_list in status_updates_map.items()
             for update in status_updates_list] + [{
                 "type": "volume_change",
                 "section_id": section_id,
                 **change
             } for section_id, changes_list in volume_changes_map.items()
                                                   for change in changes_list],
            key=lambda x: (x["date"], int(x["type"] != "status_update")),
            # put status updates first on matching dates
        )

        # Initialize variables to be maintained in our main all_changes loop
        latest_popularity_dist_estimate = None
        registration_volumes = {
            section_id: 0
            for section_id in section_id_to_object.keys()
        }
        demands = {section_id: 0 for section_id in section_id_to_object.keys()}

        # Initialize section statuses
        section_status = {
            section_id: None
            for section_id in section_id_to_object.keys()
        }
        for change in all_changes:
            section_id = change["section_id"]
            if change["type"] == "status_update":
                if section_status[section_id] is None:
                    section_status[section_id] = change["old_status"]

        percent_through = (add_drop_period.get_percent_through_add_drop(
            timezone.now()) if semester == current_semester else 1)
        if percent_through == 0:
            if verbose:
                print(
                    f"Skipping semester {semester} because the add/drop period "
                    f"hasn't started yet.")
            continue
        distribution_estimate_threshold = sum(
            len(changes_list)
            for changes_list in volume_changes_map.values()) // (
                ROUGH_MINIMUM_DEMAND_DISTRIBUTION_ESTIMATES * percent_through)
        num_changes_without_estimate = 0

        if verbose:
            print(f"Compiling demand data for semester {semester}...")
        for change in iterator_wrapper(all_changes):
            section_id = change["section_id"]

            if section_status[section_id] is None:
                section_status[section_id] = (
                    "O" if section_id_to_object[section_id].percent_open > 0.5
                    else "C")
            if change["type"] == "status_update":
                section_status[section_id] = change["new_status"]
                continue

            date = change["date"]
            volume_change = change["volume_change"]
            registration_volumes[section_id] += volume_change
            demands[section_id] = (registration_volumes[section_id] /
                                   section_id_to_object[section_id].capacity)
            max_id = max(demands.keys(), key=lambda x: demands[x])
            min_id = min(demands.keys(), key=lambda x: demands[x])
            if (latest_popularity_dist_estimate is None
                    or section_id == latest_popularity_dist_estimate[
                        "highest_demand_section"].id or section_id ==
                    latest_popularity_dist_estimate["lowest_demand_section"].id
                    or
                    latest_popularity_dist_estimate["highest_demand_section"].
                    id != max_id or
                    latest_popularity_dist_estimate["lowest_demand_section"].id
                    != min_id or num_changes_without_estimate >=
                    distribution_estimate_threshold):
                num_changes_without_estimate = 0
                output_dict[semester].append({
                    "percent_through":
                    percent_through,
                    "demands": [
                        val for sec_id, val in demands.items()
                        if section_status[sec_id] == "C"
                    ],
                })

                latest_popularity_dist_estimate = {
                    "created_at": date,
                    "semester": semester,
                    "highest_demand_section": section_id_to_object[max_id],
                    "highest_demand_section_volume":
                    registration_volumes[max_id],
                    "lowest_demand_section": section_id_to_object[min_id],
                    "lowest_demand_section_volume":
                    registration_volumes[min_id],
                }
            else:
                num_changes_without_estimate += 1

    return output_dict
Example #9
0
    def setUp(self):
        _, self.cis_120_001 = create_mock_data(
            "CIS-120-001", TEST_SEMESTER
        )  # time 11.0-12.0, days MWF

        _, self.cis_120_002 = create_mock_data(
            code="CIS-120-002", semester=TEST_SEMESTER, start=1200, end=1330, meeting_days="TR"
        )

        _, self.cis_160_001 = create_mock_data(
            code="CIS-160-001", semester=TEST_SEMESTER, start=500, end=630, meeting_days="TR"
        )

        _, self.cis_160_201 = create_mock_data(
            code="CIS-160-201", semester=TEST_SEMESTER, start=1100, end=1200, meeting_days="M"
        )
        self.cis_160_201.activity = "REC"
        self.cis_160_201.save()

        _, self.cis_160_202 = create_mock_data(
            code="CIS-160-202", semester=TEST_SEMESTER, start=1400, end=1500, meeting_days="W"
        )
        self.cis_160_202.activity = "REC"
        self.cis_160_202.save()

        _, self.cis_121_001 = create_mock_data(code="CIS-121-001", semester=TEST_SEMESTER)
        set_meetings(
            self.cis_121_001,
            [
                {
                    "building_code": "LLAB",
                    "room_code": "10",
                    "days": "MT",
                    "begin_time_24": 900,
                    "begin_time": "9:00 AM",
                    "end_time_24": 1000,
                    "end_time": "10:00 AM",
                },
                {
                    "building_code": "LLAB",
                    "room_code": "10",
                    "days": "WR",
                    "begin_time_24": 1330,
                    "begin_time": "1:30 PM",
                    "end_time_24": 1430,
                    "end_time": "2:30 PM",
                },
            ],
        )

        _, self.cis_262_001 = create_mock_async_class(code="CIS-262-001", semester=TEST_SEMESTER)

        recompute_precomputed_fields()

        self.all_codes = {"CIS-120", "CIS-160", "CIS-121", "CIS-262"}

        self.user = User.objects.create_user(
            username="******", email="*****@*****.**", password="******"
        )

        self.empty_schedule = Schedule(
            person=self.user,
            semester=TEST_SEMESTER,
            name="Empty Schedule",
        )
        self.empty_schedule.save()

        self.all_available_schedule = Schedule(
            person=self.user,
            semester=TEST_SEMESTER,
            name="All Classes Available Schedule",
        )
        self.all_available_schedule.save()
        self.all_available_schedule.sections.set([self.cis_120_001])

        self.only_120_262_available_schedule = Schedule(
            person=self.user,
            semester=TEST_SEMESTER,
            name="Only CIS-120 and CIS-262 Available Schedule",
        )
        self.only_120_262_available_schedule.save()
        self.only_120_262_available_schedule.sections.set([self.cis_120_001, self.cis_121_001])

        self.only_262_available_schedule = Schedule(
            person=self.user,
            semester=TEST_SEMESTER,
            name="Only CIS-262 Available Schedule",
        )
        self.only_262_available_schedule.save()
        self.only_262_available_schedule.sections.set(
            [self.cis_120_001, self.cis_120_002, self.cis_121_001]
        )

        self.client = APIClient()
        set_semester()
Example #10
0
 def test_no_updates(self):
     recompute_precomputed_fields()
     self.assertFalse(
         Section.objects.get(full_code="CIS-120-001").has_status_updates)