def test_remove_course_references(self):
        """ Unit Test: test_remove_course_references """
        # Add a course dependency on the test organization
        api.add_organization_course(
            self.test_organization,
            self.test_course_key
        )
        self.assertEqual(len(api.get_organization_courses(self.test_organization)), 1)

        # Remove the course dependency
        with self.assertNumQueries(2):
            api.remove_course_references(self.test_course_key)
        self.assertEqual(len(api.get_organization_courses(self.test_organization)), 0)
Esempio n. 2
0
    def test_remove_course_references(self):
        """ Unit Test: test_remove_course_references """
        # Add a course dependency on the test organization
        api.add_organization_course(self.test_organization,
                                    self.test_course_key)
        self.assertEqual(
            len(api.get_organization_courses(self.test_organization)), 1)

        # Remove the course dependency
        with self.assertNumQueries(2):
            api.remove_course_references(self.test_course_key)
        self.assertEqual(
            len(api.get_organization_courses(self.test_organization)), 0)
Esempio n. 3
0
def get_organization_courses(organization_id):
    """
    Client API operation adapter/wrapper
    """
    if not organizations_enabled():
        return []
    from organizations import api as organizations_api
    return organizations_api.get_organization_courses(organization_id)
def get_organization_courses(organization_id):
    """
    Client API operation adapter/wrapper
    """
    if not organizations_enabled():
        return []
    from organizations import api as organizations_api
    return organizations_api.get_organization_courses(organization_id)
def get_organization_courses(organization_id):
    """
    Client API operation adapter/wrapper
    """
    if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
        return []
    from organizations import api as organizations_api
    return organizations_api.get_organization_courses(organization_id)
def get_organization_courses(organization_id):
    """
    Client API operation adapter/wrapper
    """
    if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
        return []
    from organizations import api as organizations_api
    return organizations_api.get_organization_courses(organization_id)
Esempio n. 7
0
    def test_add_several_organization_courses(self):
        """
        Test that the query_count of bulk_add_organization_courses does not increase
        when given more organization-course linkages to add.
        """
        org_a = api.add_organization(self.make_organization_data("org_a"))
        org_b = api.add_organization(self.make_organization_data("org_b"))
        org_c = api.add_organization(self.make_organization_data("org_c"))
        course_key_x = CourseKey.from_string("course-v1:x+x+x")
        course_key_y = CourseKey.from_string("course-v1:y+y+y")
        course_key_z = CourseKey.from_string("course-v1:z+z+z")

        # Add linkage A->X.
        api.add_organization_course(org_a, course_key_x)

        # Add linkage A->Y, and then remove (actually: deactivate).
        api.add_organization_course(org_a, course_key_y)
        api.remove_organization_course(org_a, course_key_y)

        # 1 query to load list of existing linkages,
        # 1 query to fetch organizations for existing linkages,
        # 1 query to ensure all existing linkages active,
        # 1 query to get organiations for new linkages,
        # 1 query to create new linkages.
        with self.assertNumQueries(5):
            api.bulk_add_organization_courses([
                (org_a, course_key_x),  # Already existing.
                (org_a, course_key_x),  # Already existing.
                (org_a, course_key_y),  # Reactivation.
                (org_a, course_key_z),  # The rest are new.
                (org_b, course_key_x),
                (org_b, course_key_y),
                (org_b, course_key_z),
                (org_c, course_key_x),
                (org_c, course_key_y),
                (org_c, course_key_z),
                (org_c, course_key_z),  # Redundant.
                (org_c, course_key_z),  # Redundant.
            ])
        assert len(api.get_organization_courses(org_a)) == 3
        assert len(api.get_organization_courses(org_b)) == 3
        assert len(api.get_organization_courses(org_c)) == 3
Esempio n. 8
0
    def test_dry_run(self, mock_log_info):
        """
        Test that `bulk_add_organization_courses` does nothing when `dry_run` is
        specified (except logging).
        """
        org_a = api.add_organization(self.make_organization_data("org_a"))
        course_key_x = CourseKey.from_string("course-v1:x+x+x")

        api.bulk_add_organization_courses([(org_a, course_key_x)],
                                          dry_run=True)

        assert api.get_organization_courses(org_a) == []
        # One for reactivations, one for creations.
        assert mock_log_info.call_count == 2
Esempio n. 9
0
    def test_validation_errors(self):
        """
        Test the `bulk_add_organization_courses` raises validation errors on bad input,
        and no organization-course linkages are created.
        """
        valid_org = self.make_organization_data("valid_org")
        invalid_org = {"description": "org with no short_name!"}
        valid_course_key = "course-v1:a+b+c"
        invalid_course_key = "NOT-A-COURSE-KEY"

        # Any bad org data or bad course key should cause the bulk-add to raise.
        with self.assertRaises(exceptions.InvalidCourseKeyException):
            api.bulk_add_organization_courses([
                (valid_org, valid_course_key),
                (valid_org, invalid_course_key),
            ])
        with self.assertRaises(exceptions.InvalidOrganizationException):
            api.bulk_add_organization_courses([
                (valid_org, valid_course_key),
                (invalid_org, valid_course_key),
            ])

        # In either case, no data should've been written for `valid_org`.
        assert len(api.get_organization_courses(valid_org)) == 0
Esempio n. 10
0
    def test_end_to_end(self, run_type):
        """
        Test the happy path of the backfill command without any mocking.
        """
        # org_A: already existing, with courses and a library.
        org_a = add_organization({"short_name": "org_A", "name": "Org A"})
        course_a1_key = CourseOverviewFactory(org="org_A", run="1").id
        CourseOverviewFactory(org="org_A", run="2")
        LibraryFactory(org="org_A")

        # Write linkage for org_a->course_a1.
        # (Linkage for org_a->course_a2 is purposefully left out here;
        # it should be created by the backfill).
        add_organization_course(org_a, course_a1_key)

        # org_B: already existing, but has no content.
        add_organization({"short_name": "org_B", "name": "Org B"})

        # org_C: has a few courses; should be created.
        CourseOverviewFactory(org="org_C", run="1")
        CourseOverviewFactory(org="org_C", run="2")
        # Include an Old Mongo Modulestore -style deprecated course key.
        # This can be safely removed when Old Mongo Modulestore support is
        # removed.
        CourseOverviewFactory(
            id=CourseLocator.from_string("org_C/toy/3"),
            org="org_C",
            run="3",
        )

        # org_D: has both a course and a library; should be created.
        CourseOverviewFactory(org="org_D", run="1")
        LibraryFactory(org="org_D")

        # org_E: just has a library; should be created.
        LibraryFactory(org="org_E")

        # Confirm starting condition:
        # Only orgs are org_A and org_B, and only linkage is org_a->course_a1.
        assert set(
            org["short_name"] for org in get_organizations()
        ) == {
            "org_A", "org_B"
        }
        assert len(get_organization_courses(get_organization_by_short_name('org_A'))) == 1
        assert len(get_organization_courses(get_organization_by_short_name('org_B'))) == 0

        # Run the backfill.
        call_command("backfill_orgs_and_org_courses", run_type)

        if run_type == "--dry":
            # Confirm ending conditions are the same as the starting conditions.
            assert set(
                org["short_name"] for org in get_organizations()
            ) == {
                "org_A", "org_B"
            }
            assert len(get_organization_courses(get_organization_by_short_name('org_A'))) == 1
            assert len(get_organization_courses(get_organization_by_short_name('org_B'))) == 0
        else:
            # Confirm ending condition:
            # All five orgs present. Each org a has expected number of org-course linkages.
            assert set(
                org["short_name"] for org in get_organizations()
            ) == {
                "org_A", "org_B", "org_C", "org_D", "org_E"
            }
            assert len(get_organization_courses(get_organization_by_short_name('org_A'))) == 2
            assert len(get_organization_courses(get_organization_by_short_name('org_B'))) == 0
            assert len(get_organization_courses(get_organization_by_short_name('org_C'))) == 3
            assert len(get_organization_courses(get_organization_by_short_name('org_D'))) == 1
            assert len(get_organization_courses(get_organization_by_short_name('org_E'))) == 0
Esempio n. 11
0
    def test_end_to_end(self):
        """
        Test the happy path of the backfill command without any mocking.
        """
        # org_A: already existing, with courses and a library.
        org_a = add_organization({"short_name": "org_A", "name": "Org A"})
        course_a1_key = CourseOverviewFactory(org="org_A", run="1").id
        CourseOverviewFactory(org="org_A", run="2")
        LibraryFactory(org="org_A")

        # Write linkage for org_a->course_a1.
        # (Linkage for org_a->course_a2 is purposefully left out here;
        # it should be created by the backfill).
        add_organization_course(org_a, course_a1_key)

        # org_B: already existing, but has no content.
        add_organization({"short_name": "org_B", "name": "Org B"})

        # org_C: has a couple courses; should be created.
        CourseOverviewFactory(org="org_C", run="1")
        CourseOverviewFactory(org="org_C", run="2")

        # org_D: has both a course and a library; should be created.
        CourseOverviewFactory(org="org_D", run="1")
        LibraryFactory(org="org_D")

        # org_E: just has a library; should be created.
        LibraryFactory(org="org_E")

        # Confirm starting condition:
        # Only orgs are org_A and org_B, and only linkage is org_a->course_a1.
        assert set(org["short_name"]
                   for org in get_organizations()) == {"org_A", "org_B"}
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_A'))) == 1
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_B'))) == 0

        # Run the backfill.
        call_command("backfill_orgs_and_org_courses", "--apply")

        # Confirm ending condition:
        # All five orgs present. Each org a has expected number of org-course linkages.
        assert set(org["short_name"] for org in get_organizations()) == {
            "org_A", "org_B", "org_C", "org_D", "org_E"
        }
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_A'))) == 2
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_B'))) == 0
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_C'))) == 2
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_D'))) == 1
        assert len(
            get_organization_courses(
                get_organization_by_short_name('org_E'))) == 0
Esempio n. 12
0
    def test_edge_cases(self, mock_log_info):
        """
        Test that bulk_add_organization_courses handles a few edge cases as expected.
        """
        org_a = api.add_organization(self.make_organization_data("org_a"))
        org_b = api.add_organization(self.make_organization_data("org_b"))
        course_key_x = CourseKey.from_string("course-v1:x+x+x")
        course_key_y = CourseKey.from_string("course-v1:y+y+y")
        course_key_z = CourseKey.from_string("course-v1:z+z+z")

        # Add linkage A->X
        api.add_organization_course(org_a, course_key_x)

        # Add and then remove (under the hood: deactivate) linkage between A->Y.
        api.add_organization_course(org_a, course_key_y)
        api.remove_organization_course(org_a, course_key_y)

        # Add and then remove (under the hood: deactivate) linkage between A->Z.
        # This should NOT be reactivated, as we don't include it in the bulk_add call.
        api.add_organization_course(org_a, course_key_z)
        api.remove_organization_course(org_a, course_key_z)

        # 1 query to load list of existing linkages,
        # 1 query to fetch organizations for existing linkages,
        # 1 query to ensure all existing linkages active,
        # 1 query to get organiations for new linkages,
        # 1 query to create new linkages.
        with self.assertNumQueries(5):
            api.bulk_add_organization_courses([

                # A->X: Existing linkage, should be a no-op.
                (org_a, course_key_x),

                # B->Y: Should create new linkage.
                (org_b, course_key_y),

                # A->Y: Is an inactive linkage; should be re-activated.
                (org_a, course_key_y),

                # B->Y: Is already in this list; shouldn't affect anything.
                (org_b, course_key_y),

                # B->Z: Adding with a stringified course id; should work as if we
                #       used the course key object.
                (org_b, str(course_key_z)),

                # B->Z: Adding again with the course key object; should be a no-op.
                (org_b, course_key_z),
            ])

        # Org A was linked to courses X and Y.
        # Org A also has an inactive link to course Z that we never re-activated.
        org_a_courses = api.get_organization_courses(org_a)
        assert {org_course["course_id"]
                for org_course in org_a_courses
                } == {"course-v1:x+x+x", "course-v1:y+y+y"}

        # Org B was linked to courses Y and Z.
        org_b_courses = api.get_organization_courses(org_b)
        assert {org_course["course_id"]
                for org_course in org_b_courses
                } == {"course-v1:y+y+y", "course-v1:z+z+z"}

        # Based on logging messages, make sure the expected breakdown of
        #    created vs. reactivated vs. not touched
        # is true for the org-course linkages passed to `bulk_add_organization_courses`.
        logged_linkages_to_reactivate = mock_log_info.call_args_list[0][0][2]
        assert set(logged_linkages_to_reactivate) == {
            ("org_a", str(course_key_y)),
        }
        logged_linkages_to_create = mock_log_info.call_args_list[1][0][2]
        assert set(logged_linkages_to_create) == {
            ("org_b", str(course_key_y)),
            ("org_b", str(course_key_z)),
        }