示例#1
0
def update_course(course_id):
    course = DegreeProgressCourse.find_by_id(course_id)
    if course:
        params = request.get_json()
        accent_color = normalize_accent_color(get_param(params, 'accentColor'))

        grade = get_param(params, 'grade')
        grade = course.grade if grade is None else grade
        name = get_param(params, 'name')
        name = course.display_name if name is None else name
        if name is None:
            raise BadRequestError('name is required.')

        note = get_param(params, 'note')
        # Courses are mapped to degree_progress_unit_requirements
        value = get_param(request.get_json(), 'unitRequirementIds')
        unit_requirement_ids = list(filter(
            None, value.split(','))) if isinstance(value, str) else value
        units = get_param(params, 'units') or None

        course = DegreeProgressCourse.update(
            accent_color=accent_color,
            course_id=course_id,
            grade=grade,
            name=name,
            note=note,
            unit_requirement_ids=unit_requirement_ids,
            units=units,
        )
        # 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.')
示例#2
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.')
示例#3
0
def delete_course(course_id):
    course = DegreeProgressCourse.find_by_id(course_id)
    if course:
        # Verify that course is a copy
        matches = 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,
        )

        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)

        matches = sorted(matches, key=lambda c: c.created_at)
        if matches[0].id == course.id:
            if course.manually_created_by:
                for match in matches:
                    _delete_course(match)
            else:
                raise BadRequestError('Only copied courses can be deleted.')
        else:
            _delete_course(course)

        # Update updated_at date of top-level record
        DegreeProgressTemplate.refresh_updated_at(course.degree_check_id,
                                                  current_user.get_id())
        return tolerant_jsonify({'message':
                                 f'Course {course_id} deleted'}), 200
    else:
        raise ResourceNotFoundError('Course not found.')
示例#4
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)
示例#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())