예제 #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 update_category(category_id):
    params = request.get_json()
    units_lower = get_param(params, 'unitsLower')
    units_upper = get_param(params, 'unitsUpper')
    description = get_param(params, 'description')
    name = get_param(params, 'name')
    parent_category_id = get_param(params, 'parentCategoryId')
    # Courses can be 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

    category = DegreeProgressCategory.update(
        category_id=category_id,
        course_units_lower=units_lower,
        course_units_upper=units_upper,
        description=description,
        parent_category_id=parent_category_id,
        name=name,
        unit_requirement_ids=unit_requirement_ids,
    )
    # Update updated_at date of top-level record
    DegreeProgressTemplate.refresh_updated_at(category.template_id,
                                              current_user.get_id())
    return tolerant_jsonify(category.to_api_json())
예제 #3
0
def recommend_category(category_id):
    params = request.get_json()
    is_recommended = get_param(params, 'isRecommended')
    if is_recommended is None:
        raise BadRequestError('Parameter \'isRecommended\' is required')

    accent_color = normalize_accent_color(get_param(params, 'accentColor'))
    grade = get_param(params, 'grade')
    note = get_param(params, 'note')
    units_lower = get_param(params, 'unitsLower')
    units_upper = get_param(params, 'unitsUpper')

    category = DegreeProgressCategory.recommend(
        accent_color=accent_color,
        category_id=category_id,
        course_units_lower=units_lower,
        course_units_upper=units_upper,
        grade=grade,
        is_recommended=is_recommended,
        note=(note or '').strip() or None,
    )
    # Update updated_at date of top-level record
    DegreeProgressTemplate.refresh_updated_at(category.template_id,
                                              current_user.get_id())
    return tolerant_jsonify(category.to_api_json())
예제 #4
0
def toggle_campus_requirement(category_id):
    params = request.get_json()
    is_satisfied = get_param(params, 'isSatisfied')
    if is_satisfied is None:
        raise BadRequestError('Parameter \'isSatisfied\' is required')
    category = _get_degree_category(category_id)
    if category.category_type not in [
            'Campus Requirement, Satisfied', 'Campus Requirement, Unsatisfied'
    ]:
        raise BadRequestError('Category must be a \'Campus Requirement\' type')
    if ((category.category_type == 'Campus Requirement, Satisfied'
         and is_satisfied is True)
            or (category.category_type == 'Campus Requirement, Unsatisfied'
                and is_satisfied is False)):
        app.logger.info(
            f'Request ignored: set is_satisfied={is_satisfied} on {category.category_type}'
        )
    else:
        category = DegreeProgressCategory.set_campus_requirement_satisfied(
            category_id=category_id,
            is_satisfied=is_satisfied,
        )
        # Update updated_at date of top-level record
        DegreeProgressTemplate.refresh_updated_at(category.template_id,
                                                  current_user.get_id())
    return tolerant_jsonify(category.to_api_json())
예제 #5
0
def create_course():
    params = request.get_json()
    accent_color = normalize_accent_color(get_param(params, 'accentColor'))
    degree_check_id = get_param(params, 'degreeCheckId')
    grade = get_param(params, 'grade')
    name = get_param(params, 'name')
    note = get_param(params, 'note')
    sid = get_param(params, 'sid')
    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')

    if 0 in map(lambda v: len(str(v).strip()) if v else 0, (name, sid)):
        raise BadRequestError('Missing one or more required parameters')
    course = DegreeProgressCourse.create(
        accent_color=accent_color,
        degree_check_id=degree_check_id,
        display_name=name,
        grade=grade,
        manually_created_at=datetime.now(),
        manually_created_by=current_user.get_id(),
        note=note,
        section_id=None,
        sid=sid,
        term_id=None,
        unit_requirement_ids=unit_requirement_ids or (),
        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())
예제 #6
0
def delete_degree_category(category_id):
    category = _get_degree_category(category_id)
    DegreeProgressCategory.delete(category.id)
    # Update updated_at date of top-level record
    DegreeProgressTemplate.refresh_updated_at(category.template_id,
                                              current_user.get_id())
    return tolerant_jsonify({'message':
                             f'Template {category_id} deleted'}), 200
예제 #7
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.')
예제 #8
0
def create_category():
    params = request.get_json()
    category_type = get_param(params, 'categoryType')
    course_units_lower = get_param(params, 'unitsLower')
    course_units_upper = get_param(params, 'unitsUpper')
    description = get_param(params, 'description')
    name = get_param(params, 'name')
    parent_category_id = get_param(params, 'parentCategoryId')
    position = get_param(params, 'position')
    template_id = get_param(params, 'templateId')
    # Categories 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

    if not category_type or not name or not _is_valid_position(
            position) or not template_id:
        raise BadRequestError(
            "Insufficient data: categoryType, name, position and templateId are required.'"
        )
    if category_type in ['Category', 'Campus Requirements'
                         ] and parent_category_id:
        raise BadRequestError(f'{category_type} cannot have a parent.')
    if category_type in ('Course Requirement',
                         'Subcategory') and not parent_category_id:
        raise BadRequestError(
            f"The parentCategoryId param is required when categoryType equals '{category_type}'."
        )
    if parent_category_id:
        parent = _get_degree_category(parent_category_id)
        parent_type = parent.category_type
        if parent_type == 'Course Requirement' or (
                parent_type == 'Subcategory' and category_type == 'Category'):
            raise BadRequestError(
                f'Type {category_type} not allowed, based on parent type: {parent_type}.'
            )
        if position != parent.position:
            raise BadRequestError(
                f'Category position ({position}) must match its parent ({parent.position}).'
            )

    category = _create_category(
        category_type=category_type,
        course_units_lower=course_units_lower,
        course_units_upper=course_units_upper,
        description=description,
        name=name,
        parent_category_id=parent_category_id,
        position=position,
        template_id=template_id,
        unit_requirement_ids=unit_requirement_ids,
    )
    # Update updated_at date of top-level record
    DegreeProgressTemplate.refresh_updated_at(template_id,
                                              current_user.get_id())
    return tolerant_jsonify(category.to_api_json())
def delete_unit_requirement(unit_requirement_id):
    unit_requirement = DegreeProgressUnitRequirement.find_by_id(unit_requirement_id)
    if unit_requirement:
        DegreeProgressUnitRequirement.delete(unit_requirement_id)
        # Update updated_at date of top-level record
        DegreeProgressTemplate.refresh_updated_at(unit_requirement.template_id, current_user.get_id())

        return tolerant_jsonify({'message': f'Unit requirement {unit_requirement_id} deleted'}), 200
    else:
        raise ResourceNotFoundError(f'No unit_requirement found with id={unit_requirement_id}.')
예제 #10
0
def update_degree_note(degree_check_id):
    params = request.get_json()
    body = get_param(params, 'body')
    note = DegreeProgressNote.upsert(
        body=body,
        template_id=degree_check_id,
        updated_by=current_user.get_id(),
    )
    # Update updated_at date of top-level record
    DegreeProgressTemplate.refresh_updated_at(degree_check_id,
                                              current_user.get_id())
    return tolerant_jsonify(note.to_api_json())
def clone(template, created_by, name=None, sid=None):
    clone = DegreeProgressTemplate.create(
        advisor_dept_codes=dept_codes_where_advising(current_user),
        created_by=created_by,
        degree_name=name or template.degree_name,
        parent_template_id=template.id if sid else None,
        student_sid=sid,
    )
    unit_requirements_by_source_id = {}
    for unit_requirement in template.unit_requirements:
        source_id = unit_requirement.id
        unit_requirements_by_source_id[
            source_id] = DegreeProgressUnitRequirement.create(
                created_by=created_by,
                min_units=unit_requirement.min_units,
                name=unit_requirement.name,
                template_id=clone.id,
            )

    def _create_category(category_, parent_id):
        unit_requirement_ids = []
        for u in category_['unitRequirements']:
            source_id_ = u['id']
            cross_reference = unit_requirements_by_source_id[source_id_]
            unit_requirement_ids.append(cross_reference.id)
        return DegreeProgressCategory.create(
            category_type=category_['categoryType'],
            name=category_['name'],
            position=category_['position'],
            template_id=clone.id,
            course_units_lower=category_['unitsLower'],
            course_units_upper=category_['unitsUpper'],
            description=category_['description'],
            parent_category_id=parent_id,
            unit_requirement_ids=unit_requirement_ids,
        )

    for category in DegreeProgressCategory.get_categories(
            template_id=template.id):
        c = _create_category(category_=category, parent_id=None)
        for course in category['courseRequirements']:
            _create_category(category_=course, parent_id=c.id)
        for subcategory in category['subcategories']:
            s = _create_category(category_=subcategory, parent_id=c.id)
            for course in subcategory['courseRequirements']:
                _create_category(category_=course, parent_id=s.id)

    # TODO: Unit requirements?
    return DegreeProgressTemplate.find_by_id(clone.id)
def update_unit_requirement(unit_requirement_id):
    params = request.get_json()
    name = params.get('name')
    min_units = params.get('minUnits')
    if not name or not min_units:
        raise BadRequestError('Unit requirement \'name\' and \'minUnits\' must be provided.')
    unit_requirement = DegreeProgressUnitRequirement.update(
        id_=unit_requirement_id,
        min_units=min_units,
        name=name,
        updated_by=current_user.get_id(),
    )
    # Update updated_at date of top-level record
    DegreeProgressTemplate.refresh_updated_at(unit_requirement.template_id, current_user.get_id())
    return tolerant_jsonify(unit_requirement.to_api_json())
예제 #13
0
    def test_edit_degree_note(self, client, fake_auth, mock_note):
        """Authorized user can edit a degree note."""
        template_id = mock_note.template_id
        original_updated_at = DegreeProgressTemplate.find_by_id(
            template_id).updated_at

        fake_auth.login(coe_advisor_read_write_uid)
        body = 'Stróż pchnął kość w quiz gędźb vel fax myjń.'
        api_json = self._api_update_degree_note(client,
                                                body=body,
                                                template_id=template_id)
        assert api_json['templateId']
        assert api_json['body'] == body
        # Verify update of updated_at
        assert DegreeProgressTemplate.find_by_id(
            template_id).updated_at != original_updated_at
 def test_delete(self, client, fake_auth, mock_template):
     """COE advisor can delete degree category."""
     fake_auth.login(coe_advisor_read_write_uid)
     original_updated_at = mock_template.updated_at
     category = _api_create_category(
         category_type='Category',
         client=client,
         name='Blister in the sun',
         position=3,
         template_id=mock_template.id,
     )
     subcategory = _api_create_category(
         category_type='Subcategory',
         client=client,
         name='Gone Daddy Gone',
         parent_category_id=category['id'],
         position=3,
         template_id=mock_template.id,
     )
     course = _api_create_category(
         category_type='Course Requirement',
         client=client,
         name='Blister in the sun',
         parent_category_id=subcategory['id'],
         position=3,
         template_id=mock_template.id,
     )
     category_id = category['id']
     assert client.delete(f'/api/degree/category/{category_id}').status_code == 200
     # Verify that all were deleted.
     for object_id in (category_id, subcategory['id'], course['id']):
         assert client.get(f'/api/degree/category/{object_id}').status_code == 404
     # Verify update of updated_at
     std_commit(allow_test_environment=True)
     assert DegreeProgressTemplate.find_by_id(mock_template.id).updated_at != original_updated_at
예제 #15
0
def mock_degree_check():
    return DegreeProgressTemplate.create(
        advisor_dept_codes=['COENG'],
        created_by=AuthorizedUser.get_id_per_uid(coe_advisor_read_write_uid),
        degree_name=f'Degree check of {coe_student_sid}',
        student_sid='11667051',
    )
    def test_update_template(self, client, fake_auth):
        """Authorized user can edit a template."""
        user = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
        fake_auth.login(user.uid)

        template = DegreeProgressTemplate.create(
            advisor_dept_codes=get_dept_codes(user),
            created_by=user.id,
            degree_name='Boogie Down Productions',
        )
        for index in (1, 2, 3):
            DegreeProgressUnitRequirement.create(
                created_by=user.id,
                min_units=index,
                name=f'Unit Requirement #{index}',
                template_id=template.id,
            )

        updated_name = 'KRS One'
        api_json = self._api_clone_template(client=client,
                                            name=updated_name,
                                            template_id=template.id)
        assert api_json['id'] != template.id
        assert api_json['name'] == updated_name
        assert len(api_json['unitRequirements']) == 3
예제 #17
0
    def test_create_nonunique_unit_requirements(self, db):
        """Requires unit requirements in a single template to have unique names."""
        coe_advisor_id = AuthorizedUser.get_id_per_uid(coe_advisor_uid)
        template = DegreeProgressTemplate.create(
            advisor_dept_codes=['COENG'],
            created_by=coe_advisor_id,
            degree_name='Celtic Studies BA 2021',
        )
        name = 'Celtic Requirements'
        unit_requirement = DegreeProgressUnitRequirement.create(
            created_by=coe_advisor_id,
            min_units=70,
            name=name,
            template_id=template.id,
        )
        assert unit_requirement

        with pytest.raises(IntegrityError):
            DegreeProgressUnitRequirement.create(
                created_by=coe_advisor_id,
                min_units=53,
                name=name,
                template_id=template.id,
            )
        db.session.rollback()
예제 #18
0
def get_degree_checks(uid):
    sid = get_sid_by_uid(uid)
    if sid:
        return tolerant_jsonify(
            DegreeProgressTemplate.find_by_sid(student_sid=sid))
    else:
        raise ResourceNotFoundError('Student not found')
def get_degree_template(template_id):
    template = fetch_degree_template(template_id).to_api_json(include_courses=True)
    parent_template_id = template['parentTemplateId']
    parent_template = DegreeProgressTemplate.find_by_id(parent_template_id) if parent_template_id else None
    if parent_template:
        template['parentTemplateUpdatedAt'] = _isoformat(parent_template.updated_at)
    return tolerant_jsonify(template)
예제 #20
0
def update_degree_template(template_id):
    name = request.get_json().get('name')
    template_id = int(template_id)
    validate_template_upsert(name=name, template_id=template_id)
    template = DegreeProgressTemplate.update(name=name,
                                             template_id=template_id)
    return tolerant_jsonify(template.to_api_json())
예제 #21
0
    def test_create_unit_requirement(self):
        coe_advisor_id = AuthorizedUser.get_id_per_uid(coe_advisor_uid)
        template = DegreeProgressTemplate.create(
            advisor_dept_codes=['COENG'],
            created_by=coe_advisor_id,
            degree_name='Celtic Studies BA 2021',
        )
        min_units = 45
        name = 'Celtic Requirements'
        unit_requirement = DegreeProgressUnitRequirement.create(
            created_by=coe_advisor_id,
            min_units=min_units,
            name=name,
            template_id=template.id,
        )
        assert unit_requirement
        assert unit_requirement.__repr__(
        ) == f"""<DegreeProgressUnitRequirement id={unit_requirement.id},
                    name={name},
                    min_units={min_units},
                    template_id={template.id},
                    created_at={unit_requirement.created_at},
                    created_by={coe_advisor_id},
                    updated_at={unit_requirement.updated_at},
                    updated_by={coe_advisor_id}>"""

        assert template.unit_requirements
        assert len(template.unit_requirements) == 1
        assert unit_requirement.template
예제 #22
0
 def test_assign_and_unassign_course(self, client, fake_auth):
     """User can assign and unassign a course."""
     advisor = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
     fake_auth.login(advisor.uid)
     sid = '11667051'
     # Set up
     degree_check = DegreeProgressTemplate.create(
         advisor_dept_codes=['COENG'],
         created_by=advisor.id,
         degree_name=f'Degree check for {sid}',
         student_sid=sid,
     )
     original_updated_at = degree_check.updated_at
     category = DegreeProgressCategory.create(
         category_type='Category',
         name=f'Category for {sid}',
         position=1,
         template_id=degree_check.id,
     )
     std_commit(allow_test_environment=True)
     # Assign
     api_json = _api_get_degree(client, degree_check_id=degree_check.id)
     course_id = api_json['courses']['unassigned'][-1]['id']
     course = _api_assign_course(category_id=category.id,
                                 client=client,
                                 course_id=course_id)
     # Verify assignment
     api_json = _api_get_degree(client, degree_check_id=degree_check.id)
     assert course['categoryId'] == category.id
     assert course_id in [c['id'] for c in api_json['courses']['assigned']]
     assert course_id not in [
         c['id'] for c in api_json['courses']['unassigned']
     ]
     # Unassign
     _api_assign_course(category_id=None,
                        client=client,
                        course_id=course_id)
     api_json = _api_get_degree(client, degree_check_id=degree_check.id)
     assert course_id not in [
         c['id'] for c in api_json['courses']['assigned']
     ]
     assert course_id in [
         c['id'] for c in api_json['courses']['unassigned']
     ]
     # Verify update of updated_at
     assert DegreeProgressTemplate.find_by_id(
         degree_check.id).updated_at != original_updated_at
def mock_template():
    user = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
    marker = datetime.now().timestamp()
    return DegreeProgressTemplate.create(
        advisor_dept_codes=['COENG'],
        created_by=user.id,
        degree_name=f'I am a mock template, made for a mock category ({marker})',
    )
 def test_delete(self, client, fake_auth):
     """COE advisor can delete template."""
     fake_auth.login(coe_advisor_read_write_uid)
     user = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
     assert user.degree_progress_permission == 'read_write'
     template = DegreeProgressTemplate.create(['COENG'], user.id, f'Classical Civilizations, by {user.id}')
     assert client.delete(f'/api/degree/{template.id}').status_code == 200
     assert client.get(f'/api/degree/{template.id}').status_code == 404
def clone_degree_template(template_id, name=None, sid=None):
    template = DegreeProgressTemplate.find_by_id(template_id)
    if template_id and not template:
        raise ResourceNotFoundError(
            f'No template found with id={template_id}.')
    if name:
        validate_template_upsert(name=name, template_id=template_id)

    created_by = current_user.get_id()
    return clone(template, created_by, name=name, sid=sid)
def mock_degree_check():
    user_id = AuthorizedUser.get_id_per_uid(coe_advisor_read_write_uid)
    parent_template = DegreeProgressTemplate.create(
        advisor_dept_codes=['COENG'],
        created_by=user_id,
        degree_name='Zoology BS 2021',
    )
    degree_check = DegreeProgressTemplate.create(
        advisor_dept_codes=['COENG'],
        created_by=user_id,
        degree_name='Zoology BS 2021',
        parent_template_id=parent_template.id,
        student_sid=coe_student_sid,
    )
    std_commit(allow_test_environment=True)
    yield degree_check
    # Avoid polluting other tests
    DegreeProgressTemplate.delete(degree_check.id)
    std_commit(allow_test_environment=True)
def create_degree():
    params = request.get_json()
    name = get_param(params, 'name', None)
    validate_template_upsert(name=name)
    degree = DegreeProgressTemplate.create(
        advisor_dept_codes=dept_codes_where_advising(current_user),
        created_by=current_user.get_id(),
        degree_name=name,
    )
    return tolerant_jsonify(degree.to_api_json())
예제 #28
0
def mock_degree_checks():
    user = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid)
    marker = datetime.now().timestamp()
    degree_checks = []
    for index in (1, 2, 3):
        parent_template = DegreeProgressTemplate.create(
            advisor_dept_codes=['COENG'],
            created_by=user.id,
            degree_name=f'I am a mock template ({marker}_{index})',
        )
        degree_checks.append(
            DegreeProgressTemplate.create(
                advisor_dept_codes=['COENG'],
                created_by=user.id,
                degree_name=f'I am a mock degree check ({marker}_{index})',
                student_sid=coe_student_sid,
                parent_template_id=parent_template.id,
            ), )
    std_commit(allow_test_environment=True)
    return degree_checks
def validate_template_upsert(name, template_id=None):
    if not name:
        raise BadRequestError('\'name\' is required.')
    # Name must be unique across non-deleted templates
    template = DegreeProgressTemplate.find_by_name(name=name,
                                                   case_insensitive=True)
    if template and (template_id is None or template_id != template.id):
        raise BadRequestError(
            f'A degree named <strong>{name}</strong> already exists. Please choose a different name.'
        )
    return template
def mock_unit_requirement():
    template = DegreeProgressTemplate.create(
        advisor_dept_codes=['COENG'],
        created_by=AuthorizedUser.get_id_per_uid(coe_advisor_read_write_uid),
        degree_name='Ursinology 2025',
    )
    return DegreeProgressUnitRequirement.create(
        created_by=AuthorizedUser.get_id_per_uid(coe_advisor_read_write_uid),
        min_units=16,
        name='Aureum Ursi',
        template_id=template.id,
    )