コード例 #1
0
def test_delete_submission_of_group(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow
):
    with describe('setup'), logged_in(admin_user):
        watch_signal(signals.WORK_CREATED, clear_all_but=[])
        watch_signal(signals.GRADE_UPDATED, clear_all_but=[])
        watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[])
        stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = stub_function(
            pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade'
        )

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig = helpers.create_lti1p3_assignment(
            session, course, state='done', deadline=tomorrow
        )

        # Create some users and let them have individual submissions
        g_user1 = helpers.create_lti1p3_user(session, lti1p3_provider)
        g_user2 = helpers.create_lti1p3_user(session, lti1p3_provider)
        for user in [g_user1, g_user2]:
            course_conn.maybe_add_user_to_course(user, ['Learner'])
            sub = helpers.create_submission(test_client, assig, for_user=user)
            helpers.to_db_object(sub, m.Work).set_grade(
                5.0, m.User.resolve(admin_user)
            )

        # Place the users in a group, with a submission with a different grade
        # than their individual submission.
        gset = helpers.create_group_set(test_client, course, 1, 2, [assig])
        helpers.create_group(test_client, gset, [g_user1, g_user2])
        gsub = helpers.create_submission(test_client, assig, for_user=g_user2)
        helpers.to_db_object(gsub, m.Work).set_grade(
            6.0, m.User.resolve(admin_user)
        )

        session.commit()

    with describe(
        'deleting group submission passes back individual submissions'
    ):
        with logged_in(admin_user):
            test_client.req(
                'delete', f'/api/v1/submissions/{helpers.get_id(gsub)}', 204
            )

        assert stub_passback.called_amount == 2

        # The grade passed back is that of their individual submission
        assert stub_passback.args[0][0].get_score_given() == 5.0
        assert stub_passback.args[1][0].get_score_given() == 5.0
コード例 #2
0
def test_passback_single_submission(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow
):
    with describe('setup'), logged_in(admin_user):
        watch_signal(signals.WORK_CREATED, clear_all_but=[])
        watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[])
        signal = watch_signal(
            signals.GRADE_UPDATED,
            clear_all_but=[m.LTI1p3Provider._passback_submission]
        )

        stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = stub_function(
            pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade',
            raise_pylti1p3_exc
        )

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig = helpers.create_lti1p3_assignment(
            session, course, state='done', deadline=tomorrow
        )

        user = helpers.create_lti1p3_user(session, lti1p3_provider)
        course_conn.maybe_add_user_to_course(user, [])

        older_sub, newest_sub = [
            helpers.to_db_object(
                helpers.create_submission(test_client, assig, for_user=user),
                m.Work
            ) for _ in range(2)
        ]

    with describe('calling directly with non existing assignment is a noop'):
        m.LTI1p3Provider._passback_submission((newest_sub.id, 100000))
        assert not stub_passback.called

    with describe('changing grade of non newest sub does not passback'):
        older_sub.set_grade(5.0, admin_user)
        assert signal.was_send_once
        assert not stub_passback.called

    with describe('changing grade on newest sub passes the new grade back'):
        newest_sub.set_grade(9.5, m.User.resolve(admin_user))
        assert signal.was_send_once
        assert stub_passback.called_amount == 1
        assert stub_passback.args[0][0].get_score_given() == 9.5

    with describe('unsetting grade should passback something without grade'):
        newest_sub.set_grade(None, m.User.resolve(admin_user))

        assert signal.was_send_once
        assert stub_passback.called_amount == 1
        assert stub_passback.args[0][0].get_score_given() is None
        assert stub_passback.args[0][0].get_activity_progress() == 'Submitted'
コード例 #3
0
def test_passback_with_bonus_points(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow
):
    with describe('setup'), logged_in(admin_user):
        watch_signal(signals.WORK_CREATED, clear_all_but=[])
        watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[])
        signal = watch_signal(
            signals.GRADE_UPDATED,
            clear_all_but=[m.LTI1p3Provider._passback_submission]
        )

        stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = stub_function(
            pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade',
            raise_pylti1p3_exc
        )

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig = helpers.create_lti1p3_assignment(
            session, course, state='done', deadline=tomorrow
        )
        test_client.req(
            'patch',
            f'/api/v1/assignments/{helpers.get_id(assig)}',
            200,
            data={'max_grade': 15}
        )

        user = helpers.create_lti1p3_user(session, lti1p3_provider)
        course_conn.maybe_add_user_to_course(user, [])

        sub = helpers.to_db_object(
            helpers.create_submission(test_client, assig, for_user=user),
            m.Work
        )

    with describe('can passback bonus points'), logged_in(admin_user):
        test_client.req(
            'patch',
            f'/api/v1/submissions/{helpers.get_id(sub)}',
            200,
            data={'grade': 14},
        )
        assert signal.was_send_once
        assert stub_passback.called_amount == 1

        assert stub_passback.args[0][0].get_score_given() == 14
        assert stub_passback.args[0][0].get_score_maximum() == 10
コード例 #4
0
def test_failing_passback(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow
):
    with describe('setup'), logged_in(admin_user):
        watch_signal(signals.WORK_CREATED, clear_all_but=[])
        watch_signal(signals.GRADE_UPDATED, clear_all_but=[])
        watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[])
        stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = stub_function(
            pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade',
            raise_pylti1p3_exc
        )

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig = helpers.create_lti1p3_assignment(
            session, course, state='done', deadline=tomorrow
        )

        user = helpers.create_lti1p3_user(session, lti1p3_provider)
        course_conn.maybe_add_user_to_course(user, [])

        sub = helpers.to_db_object(
            helpers.create_submission(test_client, assig, for_user=user),
            m.Work
        )
        sub.set_grade(10.0, m.User.resolve(admin_user))
        session.commit()
        hist = m.GradeHistory.query.filter_by(work=sub).one()
        assert hist
        assert not hist.passed_back

    with describe('failing passback should not update history'):

        m.LTI1p3Provider._passback_grades(assig.id)
        assert stub_passback.called
        hist = m.GradeHistory.query.filter_by(work=sub).one()
        assert hist
        assert not hist.passed_back
コード例 #5
0
def test_passback_for_new_student(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow
):
    with describe('setup'), logged_in(admin_user):
        signal = watch_signal(
            signals.USER_ADDED_TO_COURSE,
            flush_db=True,
            clear_all_but=[m.LTI1p3Provider._passback_new_user]
        )
        stub_function(
            pylti1p3.names_roles.NamesRolesProvisioningService,
            'get_members',
            lambda: [],
        )
        stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = stub_function(
            pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade'
        )

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig1 = helpers.create_lti1p3_assignment(
            session, course, deadline=tomorrow
        )
        assig2 = helpers.create_lti1p3_assignment(
            session, course, deadline=tomorrow
        )
        user = helpers.create_lti1p3_user(session, lti1p3_provider)

    with describe('non enrolled student should not do anything'):
        assert not user.is_enrolled(course)
        m.LTI1p3Provider._passback_new_user((user.id, course.id, None))
        assert not stub_passback.called

    with describe('enrolling student should passback the grades'):
        course_conn.maybe_add_user_to_course(user, [])
        assert signal.was_send_once
        assert stub_passback.called_amount == 2
コード例 #6
0
def test_passing_back_all_grades(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow, make_function_spy
):
    with describe('setup'), logged_in(admin_user):
        watch_signal(signals.WORK_CREATED, clear_all_but=[])
        watch_signal(signals.GRADE_UPDATED, clear_all_but=[])
        watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[])
        watch_signal(signals.WORK_DELETED, clear_all_but=[])
        signal = watch_signal(
            signals.ASSIGNMENT_STATE_CHANGED,
            clear_all_but=[m.LTI1p3Provider._passback_grades]
        )

        stub_get_acccess_token = stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = make_function_spy(
            pylti1p3.assignments_grades.AssignmentsGradesService,
            'put_grade',
            pass_self=True
        )

        # Make a session with a response that returns json when the `json`
        # method is called
        req_session = requests_stubs.session_maker()()
        req_session.Response.json = lambda _=None: {}
        stub_requests_post = stub_function(requests, 'post', req_session.post)

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig = helpers.create_lti1p3_assignment(
            session, course, state='hidden', deadline=tomorrow
        )

        user1 = helpers.create_lti1p3_user(session, lti1p3_provider)
        user2 = helpers.create_lti1p3_user(session, lti1p3_provider)
        user3 = helpers.create_lti1p3_user(session, lti1p3_provider)
        all_students = [user1, user2, user3]

        for u in all_students:
            course_conn.maybe_add_user_to_course(u, ['Learner'])

        lti_user_ids = [
            m.UserLTIProvider.query.filter_by(user=u).one().lti_user_id
            for u in all_students
        ]

        gset = helpers.create_group_set(test_client, course, 1, 2, [assig])
        helpers.create_group(test_client, gset, [user1, user3])

        sub = helpers.to_db_object(
            helpers.create_submission(test_client, assig, for_user=user1),
            m.Work
        )
        sub.set_grade(2.5, admin_user)

        # Make sure user2 does not have a non deleted submission
        user2_sub = helpers.create_submission(
            test_client, assig, for_user=user2
        )
        test_client.req(
            'delete', f'/api/v1/submissions/{helpers.get_id(user2_sub)}', 204
        )

    with describe('changing assignment state to "open" does not passback'):
        assig.set_state_with_string('open')
        assert signal.was_send_once
        assert not stub_passback.called
        assert not stub_get_acccess_token.called

    with describe('changing to done does passback'):
        assig.set_state_with_string('done')
        assert signal.was_send_once
        assert stub_passback.called_amount == len(all_students)
        # Calls should be cached
        assert stub_get_acccess_token.called_amount == 1

        p1, p2, p3 = stub_passback.args

        assert p1[0] != p2[0] != p3[0]

        assert p1[0].get_score_given() == 2.5
        assert p2[0].get_score_given() == 2.5
        assert {p1[0].get_user_id(),
                p2[0].get_user_id()} == {lti_user_ids[0], lti_user_ids[2]}

        # Does not have a submission
        assert p3[0].get_score_given() is None
        assert p3[0].get_user_id() == lti_user_ids[1]

    with describe('toggling to open and done should do a new passback'):
        assig.set_state_with_string('open')
        assert signal.was_send_once
        assert not stub_passback.called
        assert not stub_get_acccess_token.called

        assig.set_state_with_string('done')
        assert signal.was_send_n_times(2)
        assert stub_passback.called
        # access token should still be cached
        assert not stub_get_acccess_token.called
コード例 #7
0
def test_delete_submission_passback(
    lti1p3_provider, describe, logged_in, admin_user, watch_signal,
    stub_function, test_client, session, tomorrow
):
    with describe('setup'), logged_in(admin_user):
        watch_signal(signals.WORK_CREATED, clear_all_but=[])
        watch_signal(signals.GRADE_UPDATED, clear_all_but=[])
        watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[])
        stub_function(
            pylti1p3.service_connector.ServiceConnector,
            'get_access_token', lambda: ''
        )
        stub_passback = stub_function(
            pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade'
        )

        course, course_conn = helpers.create_lti1p3_course(
            test_client, session, lti1p3_provider
        )
        assig = helpers.create_lti1p3_assignment(
            session, course, state='done', deadline=tomorrow
        )
        user = helpers.create_lti1p3_user(session, lti1p3_provider)
        lti_user_id = m.UserLTIProvider.query.filter_by(
            user=m.User.resolve(user)
        ).one().lti_user_id
        course_conn.maybe_add_user_to_course(user, ['Learner'])

        sub_oldest, sub_older, sub_middle, sub_newest = [
            helpers.to_db_object(
                helpers.create_submission(test_client, assig, for_user=user),
                m.Work
            ) for _ in range(4)
        ]
        signal = watch_signal(
            signals.WORK_DELETED,
            clear_all_but=[m.LTI1p3Provider._delete_submission]
        )

        def do_delete(sub):
            with logged_in(admin_user):
                test_client.req(
                    'delete', f'/api/v1/submissions/{helpers.get_id(sub)}', 204
                )

    with describe('Delete non newest'):
        do_delete(sub_older)
        assert signal.was_send_once
        assert not stub_passback.called

    with describe('Calling method for non existing work simply does nothing'):
        m.LTI1p3Provider._delete_submission(1000000)
        assert not stub_passback.called

    with describe('Calling method for non deleted work does nothing'):
        m.LTI1p3Provider._delete_submission(helpers.get_id(sub_newest))
        assert not stub_passback.called

    with describe('Delete newest should passback grade of new newest'):
        sub_middle.set_grade(5.0, m.User.resolve(admin_user))
        session.commit()
        # We should have removed the grade_updated signal
        assert not stub_passback.called

        do_delete(sub_newest)
        assert signal.was_send_once
        assert stub_passback.called_amount == 1
        grade, = stub_passback.all_args[0].values()
        assert grade.get_score_given() == 5.0
        assert grade.get_user_id() == lti_user_id

    with describe('Deleting new newest should passback next non deleted'):
        sub_older.set_grade(6.0, m.User.resolve(admin_user))
        sub_oldest.set_grade(8.0, m.User.resolve(admin_user))
        session.commit()
        assert not m.GradeHistory.query.filter_by(work=sub_oldest
                                                  ).one().passed_back

        do_delete(sub_middle)
        assert signal.was_send_once
        assert stub_passback.called_amount == 1
        grade, = stub_passback.all_args[0].values()
        # Should passback oldest as we deleted older in an earlier block
        assert grade.get_score_given() == 8.0
        assert grade.get_user_id() == lti_user_id

        # Should update history
        assert m.GradeHistory.query.filter_by(work=sub_oldest
                                              ).one().passed_back

    with describe('Deleting without any existing submission should passback'):
        do_delete(sub_oldest)
        assert signal.was_send_once
        assert stub_passback.called_amount == 1
        grade, = stub_passback.all_args[0].values()
        assert grade.get_score_given() is None
        assert grade.get_grading_progress() == 'NotReady'
        assert grade.get_user_id() == lti_user_id

    with describe('Passing back deleted sub should do nothing'):
        lti1p3_provider._passback_grade(
            assignment=assig,
            sub=sub_newest,
            timestamp=DatetimeWithTimezone.utcnow()
        )
        assert not stub_passback.called