Exemple #1
0
def assign_course(course_id):
    params = request.get_json()
    course = DegreeProgressCourse.find_by_id(course_id)
    if course:
        # Get existing category assignment. If it's a placeholder then delete it at end of transaction.
        previous_category = DegreeProgressCategory.find_by_id(
            course.category_id) if course.category_id else None
        category_id = get_param(params, 'categoryId')
        category = DegreeProgressCategory.find_by_id(
            category_id) if category_id else None
        if category:
            if category.template_id != course.degree_check_id:
                raise BadRequestError(
                    'The category and course do not belong to the same degree_check instance.'
                )
            children = DegreeProgressCategory.find_by_parent_category_id(
                parent_category_id=category_id)
            if next((c for c in children if c.category_type == 'Subcategory'),
                    None):
                raise BadRequestError(
                    'A course cannot be assigned to a category with a subcategory.'
                )
            course = DegreeProgressCourse.assign(category_id=category_id,
                                                 course_id=course.id)

        else:
            # When user un-assigns a course we delete all copies of that course,.
            for copy_of_course in DegreeProgressCourse.get_courses(
                    degree_check_id=course.degree_check_id,
                    manually_created_at=course.manually_created_at,
                    manually_created_by=course.manually_created_by,
                    section_id=course.section_id,
                    sid=course.sid,
                    term_id=course.term_id,
            ):
                if copy_of_course.id != course.id:
                    DegreeProgressCourseUnitRequirement.delete(
                        copy_of_course.id)
                    category = DegreeProgressCategory.find_by_id(
                        copy_of_course.category_id)
                    if category and 'Placeholder' in category.category_type:
                        DegreeProgressCategory.delete(category.id)
                    DegreeProgressCourse.delete(copy_of_course)
            ignore = to_bool_or_none(get_param(params, 'ignore'))
            course = DegreeProgressCourse.unassign(course_id=course.id,
                                                   ignore=ignore)

        # If previous assignment was a "placeholder" category then delete it.
        if previous_category and 'Placeholder' in previous_category.category_type:
            DegreeProgressCategory.delete(previous_category.id)

        # Update updated_at date of top-level record
        DegreeProgressTemplate.refresh_updated_at(course.degree_check_id,
                                                  current_user.get_id())
        return tolerant_jsonify(course.to_api_json())
    else:
        raise ResourceNotFoundError('Course not found.')
Exemple #2
0
    def test_delete(self, client, fake_auth):
        """Advisor can delete course."""
        advisor = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
        fake_auth.login(advisor.uid)
        sid = '11667051'

        degree_check = DegreeProgressTemplate.create(
            advisor_dept_codes=['COENG'],
            created_by=advisor.id,
            degree_name=f'Degree for {sid}',
            student_sid=sid,
        )
        api_json = _api_get_degree(client, degree_check_id=degree_check.id)
        course_id = api_json['courses']['unassigned'][-1]['id']
        categories = []
        for index in (1, 2, 3):
            categories.append(
                self._api_create_category(
                    category_type='Category',
                    client=client,
                    name=f'Category {index}',
                    position=index,
                    template_id=degree_check.id,
                ), )
        # Category #2 gets a child 'Course Requirement'
        course_requirement = self._api_create_category(
            category_type='Course Requirement',
            client=client,
            name='Course Requirement',
            parent_category_id=categories[1]['id'],
            position=categories[1]['position'],
            template_id=degree_check.id,
        )
        # Assign course to Category #1
        course = _api_assign_course(
            category_id=categories[0]['id'],
            client=client,
            course_id=course_id,
        )
        # Copy course to Category #2 and then assign it to the 'Course Requirement'
        course_id = course['id']
        course_copy_1 = _api_copy_course(
            category_id=categories[1]['id'],
            client=client,
            course_id=course_id,
        )
        placeholder_category_id = course_copy_1['categoryId']
        course_copy_1 = _api_assign_course(
            category_id=course_requirement['id'],
            client=client,
            course_id=course_copy_1['id'],
        )
        # Placeholder category is auto-deleted during re-assignment
        assert not DegreeProgressCategory.find_by_id(placeholder_category_id)
        # Delete the course_copy_1 and expect the underlying 'Course Requirement' to survive
        assert client.delete(
            f"/api/degree/course/{course_copy_1['id']}").status_code == 200

        # Copy course to Category #3 and then delete. Expect removal of course and its 'Placeholder' category.
        course_copy_2 = _api_copy_course(
            category_id=categories[2]['id'],
            client=client,
            course_id=course_id,
        )
        placeholder_category_id = course_copy_2['categoryId']
        assert 'Placeholder' in DegreeProgressCategory.find_by_id(
            placeholder_category_id).category_type
        assert client.delete(
            f"/api/degree/course/{course_copy_2['id']}").status_code == 200
        assert not DegreeProgressCategory.find_by_id(placeholder_category_id)
Exemple #3
0
    def test_copy_course(self, client, fake_auth):
        """User can copy course and add it to a category."""
        advisor = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
        fake_auth.login(advisor.uid)
        sid = '11667051'
        degree_check = DegreeProgressTemplate.create(
            advisor_dept_codes=['COENG'],
            created_by=advisor.id,
            degree_name=f'Degree for {sid}',
            student_sid=sid,
        )
        std_commit(allow_test_environment=True)
        degree_check_id = degree_check.id

        # Set up
        def _create_category(category_type='Category',
                             parent_category_id=None):
            category = DegreeProgressCategory.create(
                category_type=category_type,
                name=
                f'{category_type} for {sid} ({datetime.now().timestamp()})',
                parent_category_id=parent_category_id,
                position=1,
                template_id=degree_check_id,
            )
            return category

        category_1 = _create_category()
        category_2 = _create_category()
        std_commit(allow_test_environment=True)

        # Get sample course from list of unassigned courses
        api_json = _api_get_degree(client=client,
                                   degree_check_id=degree_check_id)
        course = api_json['courses']['unassigned'][-1]
        course_id = course['id']
        section_id = course['sectionId']
        copied_course_ids = []

        def _copy_course(category_id, expected_status_code=200):
            course_copy = _api_copy_course(
                category_id=category_id,
                client=client,
                course_id=course_id,
                expected_status_code=expected_status_code,
            )
            if expected_status_code == 200:
                copied_course_ids.append(course_copy['id'])
            return course_copy

        # Verify: user cannot copy an unassigned course.
        _copy_course(category_id=category_1.id, expected_status_code=400)
        # Verify: user cannot copy course to a category which already has the course.
        _api_assign_course(category_id=category_1.id,
                           client=client,
                           course_id=course_id)
        by_id = DegreeProgressCourse.find_by_id(course_id)
        assert by_id.category_id == category_1.id
        _copy_course(category_id=category_1.id, expected_status_code=400)

        subcategory = _create_category(category_type='Subcategory',
                                       parent_category_id=category_1.id)
        # Verify: user cannot copy course to a category which has subcategories.
        _copy_course(category_id=category_1.id, expected_status_code=400)

        # Verify we can create a copy of course in subcategory, thereby provisioning a new 'Course Requirement'
        child_count = len(
            DegreeProgressCategory.find_by_parent_category_id(subcategory.id))
        copy_of_course = _copy_course(category_id=subcategory.id)
        children = DegreeProgressCategory.find_by_parent_category_id(
            subcategory.id)
        assert len(children) == child_count + 1
        assert children[0].category_type == 'Placeholder: Course Copy'
        assert copy_of_course['categoryId'] == children[0].id

        # Assign the copied course to an actual Course Requirement and verify that "placeholder" category is deleted.
        course_requirement = _create_category(
            category_type='Course Requirement',
            parent_category_id=subcategory.id)
        placeholder_category_id = copy_of_course['categoryId']
        _api_assign_course(category_id=course_requirement.id,
                           client=client,
                           course_id=copy_of_course['id'])
        assert DegreeProgressCategory.find_by_id(
            placeholder_category_id) is None

        # Finally, we create a copy for a separate category and expect success.
        copy_of_course = _copy_course(category_id=category_2.id)
        assert copy_of_course['id'] != course_id
        assert copy_of_course['sectionId'] == section_id
        # Verify 'isCopy' property per course
        degree_json = _api_get_degree(client=client,
                                      degree_check_id=degree_check_id)
        assigned_courses = degree_json['courses']['assigned']
        unassigned_courses = degree_json['courses']['unassigned']
        assert len(assigned_courses)
        assert len(unassigned_courses)
        # Expect no "copies" in the Unassigned set of courses.
        assert True not in [c['isCopy'] for c in unassigned_courses]
        for assigned_course in assigned_courses:
            course_id = assigned_course['id']
            assert assigned_course['isCopy'] == (course_id
                                                 in copied_course_ids)
Exemple #4
0
def _get_degree_category(category_id):
    category = DegreeProgressCategory.find_by_id(category_id)
    if not category:
        raise ResourceNotFoundError(f'No category found with id={category_id}.')
    return category
Exemple #5
0
def copy_course():
    params = request.get_json()
    category_id = to_int_or_none(get_param(params, 'categoryId'))
    category = category_id and DegreeProgressCategory.find_by_id(category_id)
    if not category:
        raise ResourceNotFoundError('Category not found.')
    course_id = to_int_or_none(get_param(params, 'courseId'))
    course = DegreeProgressCourse.find_by_id(course_id)
    if not course:
        raise ResourceNotFoundError('Course not found.')

    sid = course.sid
    section_id = course.section_id
    term_id = course.term_id
    degree_check_id = category.template_id
    courses = DegreeProgressCourse.get_courses(
        degree_check_id=degree_check_id,
        manually_created_at=course.manually_created_at,
        manually_created_by=course.manually_created_by,
        section_id=section_id,
        sid=sid,
        term_id=term_id,
    )
    if not len(courses):
        raise ResourceNotFoundError('Course not found.')

    elif len(courses) == 1 and not courses[0].category_id:
        raise BadRequestError(
            f'Course {courses[0].id} is unassigned. Use the /assign API instead.'
        )

    else:
        if not _can_accept_course_requirements(category):
            raise BadRequestError(
                f'A \'Course Requirement\' cannot be added to a {category.category_type}.'
            )

        # Verify that course is not already in the requested category/subcategory.
        for c in DegreeProgressCourse.find_by_category_id(category.id):
            if c.section_id == section_id and c.sid == sid and c.term_id:
                raise BadRequestError(
                    f'Course already belongs to category {category.name}.')
        for child in DegreeProgressCategory.find_by_parent_category_id(
                category_id):
            if child.id == category_id:
                raise BadRequestError(
                    f'Course already belongs to category {category.name}.')

        # Create a new course instance and a new 'Course Requirement'.
        course = courses[0]
        course = DegreeProgressCourse.create(
            accent_color=course.accent_color,
            degree_check_id=degree_check_id,
            display_name=course.display_name,
            grade=course.grade,
            manually_created_at=course.manually_created_at,
            manually_created_by=course.manually_created_by,
            section_id=section_id,
            sid=sid,
            term_id=term_id,
            unit_requirement_ids=[
                u.unit_requirement_id for u in course.unit_requirements
            ],
            units=course.units,
        )
        course_requirement = DegreeProgressCategory.create(
            category_type='Placeholder: Course Copy',
            name=course.display_name,
            position=category.position,
            template_id=degree_check_id,
            course_units_lower=course.units,
            course_units_upper=course.units,
            parent_category_id=category.id,
            unit_requirement_ids=[
                u.unit_requirement_id for u in category.unit_requirements
            ],
        )
        DegreeProgressCourse.assign(category_id=course_requirement.id,
                                    course_id=course.id)
        return tolerant_jsonify(
            DegreeProgressCourse.find_by_id(course.id).to_api_json())
Exemple #6
0
 def _delete_course(c):
     category = DegreeProgressCategory.find_by_id(c.category_id)
     DegreeProgressCourseUnitRequirement.delete(c.id)
     DegreeProgressCourse.delete(c)
     if category and 'Placeholder' in category.category_type:
         DegreeProgressCategory.delete(category_id=category.id)