Exemple #1
0
    def test_valid_data(self):
        test_posts = """\
            forum_key|user_key|thread|post|timestamp
            1|1|Name of thread|User 1 post|2017-10-05 13:30:00+00:00
            1|2|Name of thread|User 2 post|2017-10-05 13:30:00+00:00
        """

        importer = BlackboardImport('ignore.zip', self.offering)

        posted_dt = datetime.datetime(2017,
                                      10,
                                      5,
                                      13,
                                      30,
                                      0,
                                      tzinfo=datetime.timezone.utc)
        page = PageFactory(content_id=1,
                           is_forum=True,
                           content_type='resource/x-bb-discussionboard',
                           course_offering=self.offering)
        user1 = LMSUserFactory(lms_user_id=1, course_offering=self.offering)
        user2 = LMSUserFactory(lms_user_id=2, course_offering=self.offering)

        csv_data = io.StringIO(dedent(test_posts))
        posts_data = csv.DictReader(csv_data, delimiter='|')
        importer._process_posts(posts_data)

        self.assertTrue(
            SummaryPost.objects.filter(page=page,
                                       lms_user=user1,
                                       posted_at=posted_dt).exists())
        self.assertTrue(
            SummaryPost.objects.filter(page=page,
                                       lms_user=user2,
                                       posted_at=posted_dt).exists())
    def test_students_one_page(self):
        course_offering = CourseOfferingFactory(start_date=self.course_offering.start_date, no_weeks=2)
        course_offering.owners.add(self.user)
        self.api_url = reverse('olap:communication_students', kwargs={'course_id': course_offering.id})
        # Two pages, two users
        page1 = PageFactory(course_offering=course_offering, content_type='resource/x-bb-discussionboard')
        page2 = PageFactory(course_offering=course_offering, content_type='resource/x-bb-discussionboard')
        lms_user1 = LMSUserFactory(course_offering=course_offering)
        lms_user2 = LMSUserFactory(course_offering=course_offering)

        # 2 pages and 2 weeks gives a 2x2 grid.
        # Top left: 1xU1, 2xU2
        visit_dt = self.course_offering.start_datetime + datetime.timedelta(days=3)
        visit = PageVisitFactory(page=page1, lms_user=lms_user1, visited_at=visit_dt)
        visit = PageVisitFactory(page=page1, lms_user=lms_user2, visited_at=visit_dt)
        visit = PageVisitFactory(page=page2, lms_user=lms_user2, visited_at=visit_dt)
        # Top right: 1xU1
        visit_dt = self.course_offering.start_datetime + datetime.timedelta(days=10)
        visit = PageVisitFactory(page=page1, lms_user=lms_user1, visited_at=visit_dt)
        # Bottom left: 1xU2
        visit_dt = self.course_offering.start_datetime + datetime.timedelta(days=5)
        visit = PageVisitFactory(page=page2, lms_user=lms_user2, visited_at=visit_dt)
        # Nothing in bottom right

        # Call the API endpoint
        response = self.client.get(self.api_url)
        self.assertEqual(response.status_code, HTTP_200_OK)
        expected = {
            'pageSet': [
                {
                    'id': page1.id,
                    'title': page1.title,
                    'content_type': page1.content_type,
                    'weeks': [2, 1],
                    'total': 2,
                    'percent': 100.0, # 100% of users interacted with this page
               },
                {
                    'id': page2.id,
                    'title': page2.title,
                    'content_type': page2.content_type,
                    'weeks': [1, 0],
                    'total': 1,
                    'percent': 50.0, # 50% of users (u2) interacted with this page
                },
            ],
            'totalsByWeek': [
                2, # 2 users (u1, u2) viewed the two pages in week 1
                1, # 1 users viewed the two pages in week 2
                2,
            ],
        }
        response_dict = json.loads(response.content.decode('utf-8'))
        self.assertEqual(response_dict, expected)
Exemple #3
0
    def test_multiusers_same_pages(self):
        # Have two users accessing almost the same pages.  Make sure the sessions calculated for both users include
        #  the common pages, and the distinct ones.

        u1 = LMSUserFactory(course_offering=self.offering)
        u2 = LMSUserFactory(course_offering=self.offering)

        pre_page = PageFactory()
        common_visit_pages = PageFactory.create_batch(3)
        post_page = PageFactory()

        user_visit_info = {
            u1.pk: ((-2, pre_page), (0, common_visit_pages[0]),
                    (15, common_visit_pages[1]), (30, common_visit_pages[2]),
                    (39, post_page)),
            u2.pk: (
                (-1, pre_page),
                (5, common_visit_pages[0]),
                (25, common_visit_pages[1]),
                (45, common_visit_pages[2]),
                (89, post_page),
            )
        }

        for user_pk, visit_list in user_visit_info.items():
            user = LMSUser.objects.get(pk=user_pk)
            for visit_offset_mins, page in visit_list:
                visit_obj = PageVisitFactory(
                    lms_user=user,
                    page=page,
                    visited_at=self.test_start_datetime +
                    datetime.timedelta(minutes=visit_offset_mins))

        importer = ImportLmsData(self.offering, 'ignore.txt')
        importer._calculate_sessions()

        sessions = LMSSession.objects.all()
        session_info_extractor = lambda s: (
            s.first_visit.lms_user, s.first_visit.visited_at, s.
            session_length_in_mins, s.pageviews)
        extracted_session_info = tuple(
            session_info_extractor(session) for session in sessions)

        expected_session_info = (
            (u1, self.test_start_datetime + datetime.timedelta(minutes=-2), 41,
             5),
            (u2, self.test_start_datetime + datetime.timedelta(minutes=-1), 46,
             4),
            (u2, self.test_start_datetime + datetime.timedelta(minutes=89), 0,
             1),
        )

        self.assertEqual(expected_session_info, extracted_session_info)
Exemple #4
0
    def test_only_include_pages_for_a_given_user(self):
        # Have four pages split across two users.  Make sure the calculated sessions for both users are correct,
        # especially the aspect that visits due to one user are ignored when calculating the session for another user.

        u1 = LMSUserFactory(course_offering=self.offering)
        u1_p1 = PageFactory()
        u1_p2 = PageFactory()

        u2 = LMSUserFactory(course_offering=self.offering)
        u2_p1 = PageFactory()
        u2_p2 = PageFactory()

        orphan = PageFactory()

        # Visits for u1 which should result in a session starting at test_start_dt, lasting 25 mins
        u1_expected_session_start_dt = self.test_start_datetime
        v_u1_p1 = PageVisitFactory(lms_user=u1,
                                   page=u1_p1,
                                   visited_at=u1_expected_session_start_dt)
        v_u1_p2 = PageVisitFactory(lms_user=u1,
                                   page=u1_p2,
                                   visited_at=u1_expected_session_start_dt +
                                   datetime.timedelta(minutes=25))
        # Visits for u2 which should result in a session starting at test_start_dt+10mins, and lasting 21 mins
        u2_expected_session_start_dt = self.test_start_datetime + datetime.timedelta(
            minutes=10)
        v_u2_p1 = PageVisitFactory(lms_user=u2,
                                   page=u2_p1,
                                   visited_at=u2_expected_session_start_dt)
        v_u2_p2 = PageVisitFactory(lms_user=u2,
                                   page=u2_p2,
                                   visited_at=u2_expected_session_start_dt +
                                   datetime.timedelta(minutes=21))

        importer = ImportLmsData(self.offering, 'ignore.txt')
        importer._calculate_sessions()

        sessions = LMSSession.objects.all()
        session_info_extractor = lambda s: (
            s.first_visit.lms_user, s.first_visit.visited_at, s.
            session_length_in_mins, s.pageviews)
        extracted_session_info = tuple(
            session_info_extractor(session) for session in sessions)

        expected_session_info = (
            (u1, u1_expected_session_start_dt, 25, 2),
            (u2, u2_expected_session_start_dt, 21, 2),
        )

        self.assertEqual(expected_session_info, extracted_session_info)
    def test_three_students_three_assessments_simple(self):
        course_offering  = self.course_offering
        # Three students, three assessments, for 9 submission attempts (ie, no double-ups, none missing)
        self.lms_user.delete() # If we don't delete it, it throws off the view code, which iterates over all students
        students = LMSUserFactory.create_batch(3, course_offering=course_offering)
        assessments = PageFactory.create_batch(3, course_offering=course_offering, content_type='resource/x-turnitin-assignment')
        for student in students:
            for assessment in assessments:
                sa = SubmissionAttemptFactory(page=assessment, lms_user=student, attempted_at=self.get_dt_in_courseoffering_window())

        # Call the API endpoint
        api_url = reverse('olap:assessment_grades', kwargs={'course_id': self.course_offering.id})
        response = self.client.get(api_url)
        self.assertEqual(response.status_code, HTTP_200_OK)
        users = []
        for user in students:
            expected_grades = {str(sa.page.pk): {'pk': sa.page.pk, 'grade': float(sa.grade)} for sa in SubmissionAttempt.objects.filter(lms_user=user)}
            users.append({'pk': user.id, 'name': user.full_name(), 'grades': expected_grades})
        expected = {
            'users': users,
            'assessments': [{'pk': assessment.id, 'title': assessment.title} for assessment in assessments],
        }

        response_dict = json.loads(response.content.decode('utf-8'))
        self.assertEqual(response_dict, expected)
Exemple #6
0
    def test_invalid_access_datetimes(self):
        test_posts = """\
            forum_key|user_key|thread|post|timestamp
            1|1|Name of thread|User 1 post|2016-10-05 13:30:00+00:00
            1|2|Name of thread|User 2 post|2018-10-05 13:30:00+00:00
        """

        importer = BlackboardImport('ignore.zip', self.offering)

        PageFactory(content_id=1, is_forum=True, course_offering=self.offering)
        LMSUserFactory(lms_user_id=1, course_offering=self.offering)
        LMSUserFactory(lms_user_id=2, course_offering=self.offering)

        csv_data = io.StringIO(dedent(test_posts))
        posts_data = csv.DictReader(csv_data, delimiter='|')
        importer._process_posts(posts_data)

        self.assertEqual(len(importer.error_list), 2)
Exemple #7
0
    def test_invalid_attempt_datetimes(self):
        test_submissions = """\
            user_key|content_key|user_grade|timestamp
            1|1|0|2018-10-05 13:30:00+00:00
            2|1|10.5|2016-10-05 13:30:00+00:00
        """

        importer = BlackboardImport('ignore.zip', self.offering)

        PageFactory(content_id=1, course_offering=self.offering)
        LMSUserFactory(lms_user_id=1, course_offering=self.offering)
        LMSUserFactory(lms_user_id=2, course_offering=self.offering)

        csv_data = io.StringIO(dedent(test_submissions))
        submissions_data = csv.DictReader(csv_data, delimiter='|')
        importer._process_submission_attempts(submissions_data)

        self.assertEqual(len(importer.non_critical_error_list), 2)
    def setUp(self):
        self.our_tz = get_current_timezone()
        self.user = LecturerFactory(password='******')
        self.course_offering = CourseOfferingFactory()
        self.course_offering.owners.add(self.user)
        self.lms_user = LMSUserFactory(course_offering=self.course_offering)

        self.client = APIClient()
        login = self.client.login(username=self.user.email, password='******')
Exemple #9
0
    def test_valid_data(self):
        test_activity = """\
            user_key|content_key|forum_key|timestamp
            1|1||2017-10-05 13:30:00+00:00
            2|1||2017-10-05 13:30:00+00:00
            2||2|2017-10-05 13:30:00+00:00
        """

        importer = BlackboardImport('ignore.zip', self.offering)

        visit_dt = datetime.datetime(2017,
                                     10,
                                     5,
                                     13,
                                     30,
                                     0,
                                     tzinfo=datetime.timezone.utc)
        resource_page = PageFactory(content_id=1,
                                    is_forum=False,
                                    course_offering=self.offering)
        post_page = PageFactory(content_id=2,
                                is_forum=True,
                                course_offering=self.offering)
        user1 = LMSUserFactory(lms_user_id=1, course_offering=self.offering)
        user2 = LMSUserFactory(lms_user_id=2, course_offering=self.offering)

        csv_data = io.StringIO(dedent(test_activity))
        activity_data = csv.DictReader(csv_data, delimiter='|')
        importer._process_access_log(activity_data)

        self.assertTrue(
            PageVisit.objects.filter(page=resource_page,
                                     lms_user=user1,
                                     visited_at=visit_dt).exists())
        self.assertTrue(
            PageVisit.objects.filter(page=resource_page,
                                     lms_user=user2,
                                     visited_at=visit_dt).exists())
        self.assertTrue(
            PageVisit.objects.filter(page=post_page,
                                     lms_user=user2,
                                     visited_at=visit_dt).exists())
Exemple #10
0
    def test_valid_data(self):
        test_submissions = """\
            user_key|content_key|user_grade|timestamp
            1|1|0|2017-10-05 13:30:00+00:00
            2|1|10.5|2017-10-05 13:30:00+00:00
        """

        importer = BlackboardImport('ignore.zip', self.offering)

        attempt_dt = datetime.datetime(2017,
                                       10,
                                       5,
                                       13,
                                       30,
                                       0,
                                       tzinfo=datetime.timezone.utc)
        page = PageFactory(content_id=1,
                           content_type='course/x-bb-courseassessment',
                           course_offering=self.offering)
        user1 = LMSUserFactory(lms_user_id=1, course_offering=self.offering)
        user2 = LMSUserFactory(lms_user_id=2, course_offering=self.offering)

        csv_data = io.StringIO(dedent(test_submissions))
        submissions_data = csv.DictReader(csv_data, delimiter='|')
        importer._process_submission_attempts(submissions_data)

        self.assertTrue(
            SubmissionAttempt.objects.filter(page=page,
                                             lms_user=user1,
                                             grade='0',
                                             attempted_at=attempt_dt).exists())
        self.assertTrue(
            SubmissionAttempt.objects.filter(page=page,
                                             lms_user=user2,
                                             grade='10.5',
                                             attempted_at=attempt_dt).exists())
Exemple #11
0
    def test_missing_related_objects(self):
        test_activity = """\
            user_key|content_key|forum_key|timestamp
            1|500||2017-10-05 13:30:00+00:00
            1||500|2017-10-05 13:30:00+00:00
            500|1||2017-10-05 13:30:00+00:00
        """

        importer = BlackboardImport('ignore.zip', self.offering)

        PageFactory(content_id=1,
                    is_forum=False,
                    course_offering=self.offering)
        LMSUserFactory(lms_user_id=1, course_offering=self.offering)

        csv_data = io.StringIO(dedent(test_activity))
        activity_data = csv.DictReader(csv_data, delimiter='|')
        importer._process_access_log(activity_data)

        self.assertEqual(len(importer.non_critical_error_list), 2)
        self.assertEqual(len(importer.error_list), 1)
Exemple #12
0
    def test_session_calculation(self):
        mins_in_24h10m = 24 * 60 + 10

        cases = (
            # PageVisit t= Inputs # Expected session starts, durations and pageviews
            ((), []),  # No visits => no sessions
            ((0, ), [(0, 0, 1)]),  # One visit => one session of 0 mins
            ((0, 20), [(0, 20, 2)
                       ]),  # Two visits 20m apart => one session of 20 mins
            ((0, 50), [(0, 0, 1), (50, 0, 1)
                       ]),  # Two visits 50m apart => two sessions of 0 mins
            ((0, 20, 30), [
                (0, 30, 3)
            ]),  # Three visits 20 then 10 mins apart => one session of 30 mins
            ((0, 20, 50), [
                (0, 50, 3)
            ]),  # Three visits 20 then 30 mins apart => one session of 50 mins
            ((0, 40, 80), [
                (0, 80, 3)
            ]),  # Three visits 40 then 40 mins apart => one session of 80 mins
            ((0, 40, 90), [
                (0, 40, 2), (90, 0, 1)
            ]),  # Three visits 40 then 50 mins apart => two sessions
            ((0, mins_in_24h10m), [
                (0, 0, 1), (mins_in_24h10m, 0, 1)
            ]),  # Two visits 1d10m apart => two sessions
        )

        lms_user = LMSUserFactory(course_offering=self.offering)
        page = PageFactory()

        importer = ImportLmsData(self.offering, 'ignore.txt')

        for visit_event_times, expected_outputs in cases:
            # Erase results from last case
            PageVisit.objects.all().delete()
            LMSSession.objects.all().delete()
            # Turn tuple of time-in-minutes between views, into a tuple of visit datetimes
            visit_datetimes = (self.test_start_datetime +
                               datetime.timedelta(minutes=int(m))
                               for m in visit_event_times)

            # Create the series of visits, spaced apart by the event times
            for dt in visit_datetimes:
                visit = PageVisitFactory(lms_user=lms_user,
                                         page=page,
                                         visited_at=dt)

            # Run the session calculator
            importer.calculate_session_for_user(lms_user)

            # Analyse the sessions created by the calculator, and turn the start time, duration and pageviews into
            # tuples for comparison against the expected result
            actual_outputs = []
            for session in LMSSession.objects.all():
                session_start_timedelta = session.first_visit.visited_at - self.test_start_datetime
                session_start_time_in_mins = int(
                    session_start_timedelta.total_seconds() / 60)
                actual_outputs.append(
                    (session_start_time_in_mins,
                     session.session_length_in_mins, session.pageviews))

            # Now that we've assembled tuples of session starts, durations and pageviews, do they match what we expect?
            self.assertEqual(expected_outputs, actual_outputs)