def test_get_active_users_for_month(self): date_before = datetime.date(2019, 8, 30) dates_in = [ datetime.date(2019, 9, 1), datetime.date(2019, 9, 15), datetime.date(2019, 9, 30) ] start_date = dates_in[0] end_date = dates_in[-1] date_after = datetime.date(2019, 10, 1) sm_out = [ StudentModuleFactory( modified=figures.helpers.as_datetime(date_before)), StudentModuleFactory( modified=figures.helpers.as_datetime(date_after)), ] sm_in = [] for date_rec in dates_in: sm_in.append( StudentModuleFactory( modified=figures.helpers.as_datetime(date_rec))) # We do this to make sure that we have a duplicate we don't count in the total sm_in.append( StudentModuleFactory(student=sm_in[0].student, modified=figures.helpers.as_datetime( dates_in[-1]))) count = get_active_users_for_time_period(site=self.site, start_date=start_date, end_date=end_date) assert count == len(sm_in) - 1
def test_get_student_modules_for_course_in_site(self): course_overviews = [CourseOverviewFactory() for i in range(3)] for co in course_overviews[:-1]: OrganizationCourseFactory(organization=self.organization, course_id=str(co.id)) assert get_user_model().objects.count() == 0 user = UserFactory() UserOrganizationMappingFactory(user=user, organization=self.organization) sm_count = 2 sm_expected = [StudentModuleFactory(course_id=course_overviews[0].id, student=user ) for i in range(sm_count)] # StudentModule for other course StudentModuleFactory(course_id=course_overviews[1].id) # StudentModule for course not in organization StudentModuleFactory(course_id=course_overviews[2].id) sm = figures.sites.get_student_modules_for_course_in_site( site=self.site, course_id=course_overviews[0].id) assert sm.count() == len(sm_expected) sm = figures.sites.get_student_modules_for_site(site=self.site) assert sm.count() == len(sm_expected) + 1
def create_student_module_test_data(start_date, end_date): ''' NOTE: There are many combinations of unique students, courses, and student state. We're going to start with a relatively simple set 1. A single course 2. A single set per student (learner) 3. a small number of students to reduce test run time If we create a record per day then we can work off of a unique datapoint per day ''' student_modules = [] user = UserFactory() course_overview = CourseOverviewFactory() for dt in rrule(DAILY, dtstart=start_date, until=end_date): student_modules.append( StudentModuleFactory( student=user, course_id=course_overview.id, created=dt, modified=dt, )) # we'll return everything we create return dict( user=user, course_overview=course_overview, student_modules=student_modules, )
def sm_test_data(db): """ WIP StudentModule test data to test MAU """ year_for = 2019 month_for = 10 created_date = datetime(year_for, month_for, 1).replace(tzinfo=utc) modified_date = datetime(year_for, month_for, 10).replace(tzinfo=utc) course_overviews = [CourseOverviewFactory() for i in range(3)] site = SiteFactory() sm = [] for co in course_overviews: sm += [StudentModuleFactory(course_id=co.id, created=created_date, modified=modified_date) for co in course_overviews] if organizations_support_sites(): org = OrganizationFactory(sites=[site]) for co in course_overviews: OrganizationCourseFactory(organization=org, course_id=str(co.id)) for rec in sm: UserOrganizationMappingFactory(user=rec.student, organization=org) else: org = OrganizationFactory() return dict(site=site, organization=org, course_overviews=course_overviews, student_modules=sm, year_for=year_for, month_for=month_for)
def setup(self, db): self.site = SiteFactory() self.date_1 = datetime(2020, 2, 2, tzinfo=utc) self.date_2 = self.date_1 + relativedelta(months=1) # future of date_1 self.course_enrollment = CourseEnrollmentFactory() self.student_modules = [ StudentModuleFactory(student=self.course_enrollment.user, course_id=self.course_enrollment.course_id, modified=self.date_1), StudentModuleFactory(student=self.course_enrollment.user, course_id=self.course_enrollment.course_id, modified=self.date_2) ] self.progress_data = dict(points_possible=100, points_earned=25, sections_worked=4, count=5)
def test_student_modules_active_on_date(self): our_date_for = fake.date_this_year() our_created_sm = [ StudentModuleFactory(course_id=self.course_overview.id, created=our_date_for) for _ in range(2) ] our_modified_sm = [ StudentModuleFactory(course_id=self.course_overview.id, modified=our_date_for) for _ in range(2) ] # Create record with a different date StudentModuleFactory(course_id=self.course_overview.id, created=days_from(our_date_for, -2), modified=days_from(our_date_for, -1)) course = Course(self.course_overview.id) found_sm = course.student_modules_active_on_date(our_date_for) assert set(our_created_sm + our_modified_sm) == set(found_sm)
def smm_test_data(db): """ Minimal test data for very simple test case """ site = SiteFactory() mock_today = datetime(year=2020, month=2, day=1, tzinfo=utc) last_month = mock_today - relativedelta(months=1) month_before = last_month - relativedelta(months=1) month_before_sm = [StudentModuleFactory(created=month_before, modified=month_before)] last_month_sm = [StudentModuleFactory(created=last_month, modified=last_month) for i in range(2)] return dict(site=site, mock_today=mock_today, last_month=last_month, month_before=month_before, last_month_sm=last_month_sm, month_before_sm=month_before_sm)
def test_mau_1g_for_month_as_of_day_first_day_next_month(db): """ Test getting live MAU 1G values from StudentModule for the given day Quick-n-dirty data setup: We want to make sure we get the right records when the query happens on the first day of the next month. So we do the following * Add StudentModule records for the month before we want to capture records * Add StudentModule records for the month we want to capture records * Add StudentModule records for the month after we want to capture records This sets up the scenario that we run the daily pipeline to capture MAU "as of" yesterday (the last day of the previous month) to capture MAU for the previous month and not capture any records before the previous month, nor capture records for the "current month" """ month_before = [as_datetime('2020-02-02'), as_datetime('2020-02-29')] month_after = [as_datetime('2020-04-01'), as_datetime('2020-04-01 12:00')] in_month = [ as_datetime('2020-03-01'), as_datetime('2020-03-15'), as_datetime('2020-03-31'), as_datetime('2020-03-31 12:00') ] date_for = as_date('2020-03-31') # Create student modules for the month before, month after, and in the # month for which we want to retrieve records [StudentModuleFactory(created=dt, modified=dt) for dt in month_before] [StudentModuleFactory(created=dt, modified=dt) for dt in month_after] sm_in = [ StudentModuleFactory(created=rec, modified=rec) for rec in in_month ] expected_user_ids = [obj.student_id for obj in sm_in] sm_queryset = StudentModule.objects.all() user_ids = mau_1g_for_month_as_of_day(sm_queryset=sm_queryset, date_for=date_for) assert len(user_ids) == len(in_month) assert set([rec['student__id'] for rec in user_ids]) == set(expected_user_ids)
def setup(self, db): self.today = date.today() self.site = SiteFactory() if organizations_support_sites(): self.org = OrganizationFactory(sites=[self.site]) else: self.org = OrganizationFactory() self.datetime_1 = datetime(2020, 2, 2, tzinfo=utc) self.datetime_2 = self.datetime_1 + relativedelta( months=1) # future of date_1 self.course_overview = CourseOverviewFactory() self.course_enrollment = CourseEnrollmentFactory( course_id=self.course_overview.id) self.course_enrollment_2 = CourseEnrollmentFactory( course_id=self.course_overview.id) if organizations_support_sites(): OrganizationCourseFactory(organization=self.org, course_id=str(self.course_overview.id)) UserOrganizationMappingFactory(organization=self.org, user=self.course_enrollment.user) UserOrganizationMappingFactory(organization=self.org, user=self.course_enrollment_2.user) self.student_modules = [ StudentModuleFactory(student=self.course_enrollment.user, course_id=self.course_enrollment.course_id, modified=self.datetime_1), StudentModuleFactory(student=self.course_enrollment.user, course_id=self.course_enrollment.course_id, modified=self.datetime_2), # This student module does not belong to the user in course_enrollment StudentModuleFactory(course_id=self.course_enrollment.course_id, modified=self.datetime_2) ] self.learner_sm = StudentModule.objects.filter( course_id=self.course_enrollment.course_id, student=self.course_enrollment.user).order_by('-modified') self.progress_data = dict(points_possible=100, points_earned=25, sections_worked=4, count=5)
def simple_mau_test_data(settings): """ Pytest fixture to create the base test data we need for the MAU tests here We set up single site vs multisite mode in this fixture based on which edx-organizations package is declared in the pip requirements file used to run the tests: Community: edx-organizations==0.4.10 Tahoe: git+https://github.com/appsembler/[email protected] """ our_site = SiteFactory() our_org = OrganizationFactory() our_course = CourseOverviewFactory() our_other_course = CourseOverviewFactory() other_site = SiteFactory() other_site_course = CourseOverviewFactory() our_course_data = create_student_module_recs(our_course.id) our_other_course_sm = [ StudentModuleFactory(course_id=our_other_course.id) for i in range(10) ] month_for = date(year=our_course_data['year_for'], month=our_course_data['month_for'], day=1) expected_mau_ids = set( [rec.student.id for rec in our_course_data['in_range']]) test_data = dict( month_for=month_for, expected_mau_ids=expected_mau_ids, our_site=our_site, our_course=our_course, our_course_data=our_course_data, our_other_course=our_other_course, our_other_course_sm=our_other_course_sm, other_site=other_site, other_site_course=other_site_course, ) if organizations_support_sites(): settings.FEATURES['FIGURES_IS_MULTISITE'] = True our_org = OrganizationFactory(sites=[our_site]) for user in set([obj.student for obj in our_course_data['in_range']]): UserOrganizationMappingFactory(user=user, organization=our_org) for course_id in set( [obj.course_id for obj in our_course_data['in_range']]): OrganizationCourseFactory(organization=our_org, course_id=str(course_id)) return test_data
def test_mau_1g_for_month_as_of_day_first_day_next_month(db): """ Test getting live MAU 1G values from StudentModule for the given day Quick-n-dirty data setup: We want to make sure we get the right records when the query happens on the first day of the next month. So we do the following * Add a StudentModule record for two months before * Add at least one StudentModule record for the month we want * Add at least one StudentModule record for after the month we want This sets up the scenario that we run the daily pipeline to capture MAU "as of" yesterday (the last day of the previous month) to capture MAU for the previous month """ mock_today = datetime(year=2020, month=4, day=1).replace(tzinfo=utc) month_before = datetime(year=2020, month=2, day=2).replace(tzinfo=utc) in_dates = [ datetime(year=2020, month=3, day=1).replace(tzinfo=utc), datetime(year=2020, month=3, day=15).replace(tzinfo=utc), datetime(year=2020, month=3, day=31).replace(tzinfo=utc) ] date_for = mock_today.date() - relativedelta(days=1) # Create a student module in the month before, and in month after StudentModuleFactory(created=month_before, modified=month_before) StudentModuleFactory(created=mock_today, modified=mock_today) sm_in = [ StudentModuleFactory(created=rec, modified=rec) for rec in in_dates ] expected_user_ids = [obj.student_id for obj in sm_in] sm_queryset = StudentModule.objects.all() user_ids = mau_1g_for_month_as_of_day(sm_queryset=sm_queryset, date_for=date_for) assert set([rec['student__id'] for rec in user_ids]) == set(expected_user_ids)
def create_student_module_recs(course_id): """ Create StudentModule records for our test data We create records within the test month and before and after our test month Improve this by passing the year and month for which we want to check record inclusion and deriving dates outside of our month range for the StudentModule records we want to exclude """ # Create SM in our month year_for = 2020 month_for = 1 start_dt = datetime(year_for, month_for, 1, tzinfo=fuzzy.compat.UTC) end_dt = datetime(year_for, month_for, 31, tzinfo=fuzzy.compat.UTC) date_gen = fuzzy.FuzzyDateTime(start_dt=start_dt, end_dt=end_dt) in_range = [ StudentModuleFactory(created=start_dt, modified=date_gen.evaluate(2, None, False), course_id=course_id) for i in range(3) ] # Create a rec before before_date = datetime(2019, 12, 31, tzinfo=fuzzy.compat.UTC) # Create a rec after after_date = datetime(2020, 2, 1, tzinfo=fuzzy.compat.UTC) out_range = [ StudentModuleFactory(created=before_date, modified=before_date, course_id=course_id), StudentModuleFactory(created=after_date, modified=after_date, course_id=course_id) ] return dict( year_for=year_for, month_for=month_for, in_range=in_range, out_range=out_range, )
def setup(self, db): self.course_enrollments = [CourseEnrollmentFactory() for i in range(1, 5)] if organizations_support_sites(): self.my_site = SiteFactory(domain='my-site.test') self.my_site_org = OrganizationFactory(sites=[self.my_site]) for ce in self.course_enrollments: OrganizationCourseFactory(organization=self.my_site_org, course_id=str(ce.course.id)) UserOrganizationMappingFactory(user=ce.user, organization=self.my_site_org) self.student_module = StudentModuleFactory()
def setup(self, db): self.today = datetime.date(2018, 6, 1) self.course_overview = CourseOverviewFactory() if OPENEDX_RELEASE == GINKGO: self.course_enrollments = [ CourseEnrollmentFactory(course_id=self.course_overview.id) for i in range(4) ] else: self.course_enrollments = [ CourseEnrollmentFactory(course=self.course_overview) for i in range(4) ] if organizations_support_sites(): self.my_site = SiteFactory(domain='my-site.test') self.my_site_org = OrganizationFactory(sites=[self.my_site]) OrganizationCourseFactory(organization=self.my_site_org, course_id=str(self.course_overview.id)) for ce in self.course_enrollments: UserOrganizationMappingFactory(user=ce.user, organization=self.my_site_org) self.course_access_roles = [ CourseAccessRoleFactory( user=self.course_enrollments[i].user, course_id=self.course_enrollments[i].course_id, role=role, ) for i, role in enumerate(self.COURSE_ROLES) ] # create student modules for yesterday and today for day in [prev_day(self.today), self.today]: self.student_modules = [ StudentModuleFactory(course_id=ce.course_id, student=ce.user, created=ce.created, modified=as_datetime(day)) for ce in self.course_enrollments ] self.cert_days_to_complete = [10, 20, 30] self.expected_avg_cert_days_to_complete = 20 self.generated_certificates = [ GeneratedCertificateFactory( user=self.course_enrollments[i].user, course_id=self.course_enrollments[i].course_id, created_date=(self.course_enrollments[i].created + datetime.timedelta(days=days)), ) for i, days in enumerate(self.cert_days_to_complete) ]
def test_student_modules_for_course_enrollment(monkeypatch): """Test we get the correct student modules for the given course enrollment """ site = SiteFactory() ce = CourseEnrollmentFactory() ce_sm = [StudentModuleFactory(student=ce.user, course_id=ce.course_id)] # Create another student module record to make sure this is not in our # query results StudentModuleFactory() if organizations_support_sites(): monkeypatch.setattr('figures.sites.is_multisite', lambda: True) our_org = OrganizationFactory(sites=[site]) other_org = OrganizationFactory(sites=[SiteFactory()]) other_org_ce = CourseEnrollmentFactory() other_sm = StudentModuleFactory(student=other_org_ce.user, course_id=other_org_ce.course_id) UserOrganizationMappingFactory(user=ce.user, organization=our_org) UserOrganizationMappingFactory(user=other_org_ce.user, organization=other_org) sm = figures.sites.student_modules_for_course_enrollment(site, ce) assert set(sm) == set(ce_sm)
def test_collect_progress_data(db, monkeypatch): """Tests the `_collect_progress_data` function The function under test instantiates a LearnerCourseGrade object, calls its `progress` We can use the default values created from the mock `LearnerCourseGrades` class """ student_module = StudentModuleFactory() progress_data = _collect_progress_data(student_module) # Simply checking the keys assert set(progress_data.keys()) == set( ['count', 'sections_worked', 'points_possible', 'points_earned'])
def backfill_test_data(db): """ TODO: make counts different for each course per month """ months_back = 6 sm_per_month = [10 + i for i in range(months_back + 1)] site = SiteFactory() now = datetime.utcnow().replace(tzinfo=utc) first_month = now - relativedelta(months=months_back) last_month = now - relativedelta(months=1) course_overviews = [CourseOverviewFactory() for i in range(1)] count_check = [] sm = [] for i, dt in enumerate( rrule(freq=MONTHLY, dtstart=first_month, until=last_month)): for co in course_overviews: sm_count = sm_per_month[i] month_sm = [ StudentModuleFactory(course_id=co.id, created=dt, modified=dt) for i in range(sm_count) ] sm += month_sm count_check.append(dict(month=dt, month_sm=month_sm, sm_count=sm_count)) if organizations_support_sites(): org = OrganizationFactory(sites=[site]) for co in course_overviews: OrganizationCourseFactory(organization=org, course_id=str(co.id)) for rec in sm: UserOrganizationMappingFactory(user=rec.student, organization=org) else: org = OrganizationFactory() return dict(site=site, organization=org, course_overview=course_overviews, student_modules=sm, first_month=first_month, now=now, months_back=months_back, sm_per_month=sm_per_month, count_check=count_check)
def test_bulk_calculate_course_progress_data_happy_path(db, monkeypatch): """Tests 'bulk_calculate_course_progress_data' function The function under test iterates over a set of course enrollment records, So we create a couple of records to iterate over and mock the collect function """ course_overview = CourseOverviewFactory() course_enrollments = [ CourseEnrollmentFactory(course_id=course_overview.id) for i in range(2) ] mapping = { ce.course_id: LearnerCourseGradeMetricsFactory(course_id=str(ce.course_id), user=ce.user, sections_worked=1, sections_possible=2) for ce in course_enrollments } StudentModuleFactory() def mock_metrics(course_enrollment, **_kwargs): return mapping[course_enrollment.course_id] monkeypatch.setattr( 'figures.pipeline.enrollment_metrics.get_site_for_course', lambda val: SiteFactory()) monkeypatch.setattr( 'figures.pipeline.enrollment_metrics.collect_metrics_for_enrollment', mock_metrics) monkeypatch.setattr( 'figures.pipeline.enrollment_metrics.course_enrollments_for_course', lambda val: CourseEnrollment.objects.all()) # monkeypatch.setattr('figures.pipeline.enrollment_metrics.student_modules_for_course_enrollment', # mock_get_student_modules) monkeypatch.setattr( 'figures.pipeline.enrollment_metrics.student_modules_for_course_enrollment', lambda **_kwargs: StudentModule.objects.all()) data = bulk_calculate_course_progress_data(course_overview.id) assert data['average_progress'] == 0.5
def test_get_site_mau_current_month(db): mock_today = date(year=2020, month=3, day=1) freezer = freeze_time(mock_today) freezer.start() start_dt = datetime(mock_today.year, mock_today.month, 1, tzinfo=fuzzy.compat.UTC) end_dt = datetime(mock_today.year, mock_today.month, 31, tzinfo=fuzzy.compat.UTC) date_gen = fuzzy.FuzzyDateTime(start_dt=start_dt, end_dt=end_dt) site = SiteFactory() course_overviews = [CourseOverviewFactory() for i in range(2)] users = [UserFactory() for i in range(2)] sm = [] for user in users: for co in course_overviews: sm.append( StudentModuleFactory(course_id=co.id, student=user, modified=date_gen.evaluate( 2, None, False))) if organizations_support_sites(): org = OrganizationFactory(sites=[site]) for co in course_overviews: OrganizationCourseFactory(organization=org, course_id=str(co.id)) for user in users: UserOrganizationMappingFactory(user=user, organization=org) active_user_count = get_site_mau_current_month(site) freezer.stop() assert active_user_count == len(users)
def setup(self, db): self.today = datetime.date(2018, 6, 1) self.course_overview = CourseOverviewFactory() self.course_enrollments = [ CourseEnrollmentFactory(course_id=self.course_overview.id) for i in range(4) ] self.course_access_roles = [ CourseAccessRoleFactory( user=self.course_enrollments[i].user, course_id=self.course_enrollments[i].course_id, role=role, ) for i, role in enumerate(self.COURSE_ROLES) ] # create student modules for yesterday and today for day in [prev_day(self.today), self.today]: self.student_modules = [ StudentModuleFactory(course_id=ce.course_id, student=ce.user, created=ce.created, modified=as_datetime(day)) for ce in self.course_enrollments ] self.cert_days_to_complete = [10, 20, 30] self.expected_avg_cert_days_to_complete = 20 self.generated_certificates = [ GeneratedCertificateFactory( user=self.course_enrollments[i].user, course_id=self.course_enrollments[i].course_id, created_date=(self.course_enrollments[i].created + datetime.timedelta(days=days)), ) for i, days in enumerate(self.cert_days_to_complete) ]
def test_create_student_module_factory(self): obj = StudentModuleFactory() assert obj
def setup(self, db): self.course_enrollments = [ CourseEnrollmentFactory() for i in range(1, 5) ] self.student_module = StudentModuleFactory() self.date_for = datetime.datetime.utcnow().date()
def setup(self, db): self.student_module = StudentModuleFactory()
def setup(self, db): self.course_enrollments = [ CourseEnrollmentFactory() for i in range(1, 5) ] self.student_module = StudentModuleFactory()
def mock_student_modules_for_site(site): users = [UserFactory() for i in range(2)] for user in users: StudentModuleFactory(student=user, modified=modified) StudentModuleFactory(student=user, modified=modified) return StudentModule.objects.filter(student__in=users)
def mock_student_modules_for_site(site): for user in [UserFactory() for i in range(2)]: StudentModuleFactory(student=user, modified=modified) StudentModuleFactory(student=user, modified=modified) return StudentModule.objects.all()