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
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())
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())
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())
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.')
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
def test_illegal_assign(self, client, fake_auth): """A course cannot be assigned to a category with a subcategory.""" 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 for {sid}', student_sid=sid, ) category = DegreeProgressCategory.create( category_type='Category', name=f'Category for {sid}', position=1, template_id=degree_check.id, ) # Subcategory subcategory = DegreeProgressCategory.create( category_type='Subcategory', name=f'Subcategory for {sid}', parent_category_id=category.id, position=category.position, template_id=degree_check.id, ) std_commit(allow_test_environment=True) api_json = _api_get_degree(client, degree_check_id=degree_check.id) course_id = api_json['courses']['unassigned'][-1]['id'] # Expect failure _api_assign_course(category_id=category.id, client=client, course_id=course_id, expected_status_code=400) # Expect success _api_assign_course(category_id=subcategory.id, client=client, course_id=course_id)
def _create_category( category_type, course_units_lower, course_units_upper, description, name, parent_category_id, position, template_id, unit_requirement_ids, ): campus_requirements = [] if category_type == 'Campus Requirements': category_type = 'Category' name = 'Campus Requirements' campus_requirements = [ 'Entry Level Writing', 'American History', 'American Institutions', 'American Cultures' ] category = DegreeProgressCategory.create( 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, ) for name in campus_requirements: DegreeProgressCategory.create( category_type='Campus Requirement, Unsatisfied', name=name, parent_category_id=category.id, position=position, template_id=template_id, ) return category
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 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 test_recommend_category(self, client, fake_auth, mock_template): """Authorized user can edit a category.""" user = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid) fake_auth.login(user.uid) category = DegreeProgressCategory.create( category_type='Category', name='Recommend me to your friends.', position=1, template_id=mock_template.id, ) category_id = category.id note = 'Four out of five dentists surveyed recommend sugarless gum for their patients who chew gum.' self._api_recommend_category( category_id=category_id, client=client, grade='B', is_recommended=True, note=note, units_lower=3, units_upper=4, ) # Verify the update api_json = _api_get_template(client=client, template_id=mock_template.id) category = api_json['categories'][0] assert category['id'] == category_id assert category['grade'] == 'B' assert category['isRecommended'] is True assert category['note'] == note assert category['unitsLower'] == 3 assert category['unitsUpper'] == 4 # Set to False self._api_recommend_category( category_id=category_id, client=client, is_recommended=False, note='', ) api_json = _api_get_template(client=client, template_id=mock_template.id) category = api_json['categories'][0] assert category['id'] == category_id assert category['grade'] is None assert category['isRecommended'] is False assert category['note'] is None assert category['unitsLower'] is None assert category['unitsUpper'] is None
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, )
def test_create_campus_requirements(self, client, fake_auth, mock_template, app): """Campus Requirements is created as a category with four children.""" fake_auth.login(coe_advisor_read_write_uid) api_json = _api_create_category( client, category_type='Campus Requirements', name='hacked name', position=2, template_id=mock_template.id, ) assert api_json['id'] assert api_json['categoryType'] == 'Category' assert api_json['name'] == 'Campus Requirements' assert api_json['parentCategoryId'] is None assert api_json['position'] == 2 assert api_json['templateId'] == mock_template.id children = DegreeProgressCategory.find_by_parent_category_id(api_json['id']) assert len(children) == 4
def test_unassigned_courses(self, client, fake_auth): """Authorized user can un-assign a course.""" advisor = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid) fake_auth.login(advisor.uid) # Set up sid = '11667051' degree_check = DegreeProgressTemplate.create( advisor_dept_codes=['COENG'], created_by=advisor.id, degree_name=f'Degree check for {sid}', student_sid=sid, ) category = DegreeProgressCategory.create( category_type='Category', name=f'Category for {sid}', position=1, template_id=degree_check.id, ) std_commit(allow_test_environment=True) # Fetch api_json = _api_get_degree(client, degree_check_id=degree_check.id) assigned_courses = api_json['courses']['assigned'] unassigned_courses = api_json['courses']['unassigned'] assert len(unassigned_courses) assigned_course_count = len(assigned_courses) unassigned_course_count = len(unassigned_courses) # Assign unassigned_course = unassigned_courses[-1] _api_assign_course(client=client, category_id=category.id, course_id=unassigned_course['id']) api_json = _api_get_degree(client, degree_check_id=degree_check.id) # Verify assigned_courses = api_json['courses']['assigned'] assert len(assigned_courses) == assigned_course_count + 1 assert next((c for c in unassigned_courses if c['sectionId'] == unassigned_course['sectionId']), None) unassigned_courses = api_json['courses']['unassigned'] assert len(unassigned_courses) == unassigned_course_count - 1 assert next((c for c in unassigned_courses if c['sectionId'] == unassigned_course['sectionId']), None) is None
def test_satisfy_requirement(self, client, fake_auth, mock_template): """Authorized user can toggle a Campus Requirement satisfied/unsatisfied.""" user = AuthorizedUser.find_by_uid(coe_advisor_read_write_uid) fake_auth.login(user.uid) parent_category = _api_create_category( client, category_type='Campus Requirements', name='Campus Requirements', position=3, template_id=mock_template.id, ) # Requirements are initially unsatisfied requirements = DegreeProgressCategory.find_by_parent_category_id( parent_category['id']) for requirement in requirements: assert requirement.category_type == 'Campus Requirement, Unsatisfied' requirement_id = requirements[0].id self._api_toggle_satisfied( category_id=requirement_id, client=client, is_satisfied=True, ) api_json = _api_get_template(client=client, template_id=mock_template.id) category = api_json['categories'][0] assert category['id'] == parent_category['id'] assert category['courseRequirements'][0]['id'] == requirement_id assert category['courseRequirements'][0][ 'categoryType'] == 'Campus Requirement, Satisfied' self._api_toggle_satisfied( category_id=requirements[0].id, client=client, is_satisfied=False, ) api_json = _api_get_template(client=client, template_id=mock_template.id) category = api_json['categories'][0] assert category['id'] == parent_category['id'] assert category['courseRequirements'][0]['id'] == requirement_id assert category['courseRequirements'][0][ 'categoryType'] == 'Campus Requirement, Unsatisfied'
def to_api_json(self, include_courses=False): unit_requirements = [u.to_api_json() for u in self.unit_requirements] api_json = { 'id': self.id, 'advisorDeptCodes': self.advisor_dept_codes, 'categories': DegreeProgressCategory.get_categories(template_id=self.id), 'createdAt': _isoformat(self.created_at), 'createdBy': self.created_by, 'name': self.degree_name, 'note': self.note.to_api_json() if self.note else None, 'parentTemplateId': self.parent_template_id, 'sid': self.student_sid, 'unitRequirements': sorted(unit_requirements, key=lambda r: r['name']), 'updatedAt': _isoformat(self.updated_at), 'updatedBy': self.updated_by, } if self.student_sid and include_courses: assigned_courses, ignored_courses, unassigned_courses = self._get_partitioned_courses_json() api_json['courses'] = { 'assigned': sorted(assigned_courses, key=lambda c: c['name']), 'ignored': sorted(ignored_courses, key=lambda c: c['name']), 'unassigned': sorted(unassigned_courses, key=lambda c: c['name']), } return api_json
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())
def test_assign_and_unassign_course(self, app, 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) original_grade = course['grade'] assert original_grade == 'P' # 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'] ] # Modify grade in data lake and verify that assigned course receives the update modified_grade = 'C+' _change_grade_in_data_loch( app=app, grade=modified_grade, section_id=course['sectionId'], sid=sid, term_id=course['termId'], ) api_json = _api_get_degree(client, degree_check_id=degree_check.id) assigned_course = api_json['courses']['assigned'][0] assert assigned_course['id'] == course_id assert assigned_course['grade'] == modified_grade _change_grade_in_data_loch( app=app, grade=original_grade, section_id=course['sectionId'], sid=sid, term_id=course['termId'], ) # 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'] ] unassigned_course = api_json['courses']['unassigned'][-1] assert unassigned_course['id'] == course_id assert unassigned_course['grade'] == original_grade # Verify update of updated_at assert DegreeProgressTemplate.find_by_id( degree_check.id).updated_at != original_updated_at
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)
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)
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)
def _get_category_children(category_id, category_type): children = DegreeProgressCategory.find_by_parent_category_id(category_id) return list(filter(lambda c: c.category_type == category_type, children))
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