def test_add_course_requirement_namespace(self): credit_course = add_credit_course(self.course_key) requirement = { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, } credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement, 0) assert credit_course == credit_req.course assert created is True requirement = { "namespace": "new_grade", "name": "new_grade", "display_name": "New Grade", "criteria": {}, } credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement, 1) assert credit_course == credit_req.course assert created is True requirements = CreditRequirement.get_course_requirements( self.course_key) assert len(requirements) == 2 requirements = CreditRequirement.get_course_requirements( self.course_key, namespace="grade") assert len(requirements) == 1
def test_add_course_requirement_namespace(self): credit_course = add_credit_course(self.course_key) requirement = { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement, 0) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirement = { "namespace": "new_grade", "name": "new_grade", "display_name": "New Grade", "criteria": {}, } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement, 1) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(requirements), 2) requirements = CreditRequirement.get_course_requirements(self.course_key, namespace="grade") self.assertEqual(len(requirements), 1)
def test_add_course_requirement_namespace(self): credit_course = self.add_credit_course() requirement = { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } } credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirement = {"namespace": "icrv", "name": "midterm", "criteria": ""} credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements( self.course_key) self.assertEqual(len(requirements), 2) requirements = CreditRequirement.get_course_requirements( self.course_key, namespace="grade") self.assertEqual(len(requirements), 1)
def test_add_course_requirement_namespace(self): credit_course = self.add_credit_course() requirement = { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, } credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement, 0) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirement = { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement, 1) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements( self.course_key) self.assertEqual(len(requirements), 2) requirements = CreditRequirement.get_course_requirements( self.course_key, namespace="grade") self.assertEqual(len(requirements), 1)
def test_add_course_requirement_namespace(self): credit_course = self.add_credit_course() requirement = { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirement = { "namespace": "icrv", "name": "midterm", "criteria": "" } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(requirements), 2) requirements = CreditRequirement.get_course_requirements(self.course_key, namespace="grade") self.assertEqual(len(requirements), 1)
def test_add_course_requirement_namespace(self): credit_course = self.add_credit_course() requirement = { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirement = { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(requirements), 2) requirements = CreditRequirement.get_course_requirements(self.course_key, namespace="grade") self.assertEqual(len(requirements), 1)
def set_credit_requirements(course_key, requirements): """ Add requirements to given course. Args: course_key(CourseKey): The identifier for course requirements(list): List of requirements to be added Example: >>> set_credit_requirements( "course-v1-edX-DemoX-1T2015", [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, }, { "namespace": "proctored_exam", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Final Exam", "criteria": {}, }, { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.8}, }, ]) Raises: InvalidCreditRequirements Returns: None """ invalid_requirements = _validate_requirements(requirements) if invalid_requirements: invalid_requirements = ", ".join(invalid_requirements) raise InvalidCreditRequirements(invalid_requirements) try: credit_course = CreditCourse.get_credit_course(course_key=course_key) except CreditCourse.DoesNotExist: raise InvalidCreditCourse() old_requirements = CreditRequirement.get_course_requirements( course_key=course_key) requirements_to_disable = _get_requirements_to_disable( old_requirements, requirements) if requirements_to_disable: CreditRequirement.disable_credit_requirements(requirements_to_disable) for order, requirement in enumerate(requirements): CreditRequirement.add_or_update_course_requirement( credit_course, requirement, order)
def set_credit_requirements(course_key, requirements): """ Add requirements to given course. Args: course_key(CourseKey): The identifier for course requirements(list): List of requirements to be added Example: >>> set_credit_requirements( "course-v1-edX-DemoX-1T2015", [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, }, { "namespace": "proctored_exam", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Final Exam", "criteria": {}, }, { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.8}, }, ]) Raises: InvalidCreditRequirements Returns: None """ invalid_requirements = _validate_requirements(requirements) if invalid_requirements: invalid_requirements = ", ".join(invalid_requirements) raise InvalidCreditRequirements(invalid_requirements) try: credit_course = CreditCourse.get_credit_course(course_key=course_key) except CreditCourse.DoesNotExist: raise InvalidCreditCourse() old_requirements = CreditRequirement.get_course_requirements(course_key=course_key) requirements_to_disable = _get_requirements_to_disable(old_requirements, requirements) if requirements_to_disable: CreditRequirement.disable_credit_requirements(requirements_to_disable) for order, requirement in enumerate(requirements): CreditRequirement.add_or_update_course_requirement(credit_course, requirement, order)
def test_get_course_requirements(self): credit_course = self.add_credit_course() requirement = { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(requirements), 1)
def add_course_requirements(self): """ Add requirements to course. """ requirements = ({ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "new_grade", "name": "new_grade", "display_name": "new_grade", "criteria": { "min_grade": 0.8 }, }) for i, requirement in enumerate(requirements): credit_requirement, _ = CreditRequirement.add_or_update_course_requirement( self.credit_course, requirement, i) CreditRequirementStatus.add_or_update_requirement_status( self.old_username, credit_requirement, "satisfied", {"final_grade": 0.95})
def get_credit_requirement_status(course_key, username, namespace=None, name=None): """ Retrieve the user's status for each credit requirement in the course. Args: course_key (CourseKey): The identifier for course username (str): The identifier of the user Example: >>> get_credit_requirement_status("course-v1-edX-DemoX-1T2015", "john") [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "In Course Reverification", "criteria": {}, "reason": {}, "status": "failed", "status_date": "2015-06-26 07:49:13", }, { "namespace": "proctored_exam", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Proctored Mid Term Exam", "criteria": {}, "reason": {}, "status": "satisfied", "status_date": "2015-06-26 11:07:42", }, { "namespace": "grade", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Minimum Passing Grade", "criteria": {"min_grade": 0.8}, "reason": {"final_grade": 0.95}, "status": "satisfied", "status_date": "2015-06-26 11:07:44", }, ] Returns: list of requirement statuses """ requirements = CreditRequirement.get_course_requirements(course_key, namespace=namespace, name=name) requirement_statuses = CreditRequirementStatus.get_statuses(requirements, username) requirement_statuses = dict((o.requirement, o) for o in requirement_statuses) statuses = [] for requirement in requirements: requirement_status = requirement_statuses.get(requirement) statuses.append({ "namespace": requirement.namespace, "name": requirement.name, "display_name": requirement.display_name, "criteria": requirement.criteria, "reason": requirement_status.reason if requirement_status else None, "status": requirement_status.status if requirement_status else None, "status_date": requirement_status.modified if requirement_status else None, }) return statuses
def test_get_course_requirements(self): credit_course = self.add_credit_course() requirement = { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } credit_req, created = CreditRequirement.add_or_update_course_requirement( credit_course, requirement) self.assertEqual(credit_course, credit_req.course) self.assertEqual(created, True) requirements = CreditRequirement.get_course_requirements( self.course_key) self.assertEqual(len(requirements), 1)
def get_credit_requirement_status(course_key, username, namespace=None, name=None): """ Retrieve the user's status for each credit requirement in the course. Args: course_key (CourseKey): The identifier for course username (str): The identifier of the user Example: >>> get_credit_requirement_status("course-v1-edX-DemoX-1T2015", "john") [ { "namespace": "proctored_exam", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Proctored Mid Term Exam", "criteria": {}, "reason": {}, "status": "satisfied", "status_date": "2015-06-26 11:07:42", "order": 1, }, { "namespace": "grade", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Minimum Passing Grade", "criteria": {"min_grade": 0.8}, "reason": {"final_grade": 0.95}, "status": "satisfied", "status_date": "2015-06-26 11:07:44", "order": 2, }, ] Returns: list of requirement statuses """ requirements = CreditRequirement.get_course_requirements(course_key, namespace=namespace, name=name) requirement_statuses = CreditRequirementStatus.get_statuses(requirements, username) requirement_statuses = {o.requirement: o for o in requirement_statuses} statuses = [] for requirement in requirements: requirement_status = requirement_statuses.get(requirement) statuses.append({ "namespace": requirement.namespace, "name": requirement.name, "display_name": requirement.display_name, "criteria": requirement.criteria, "reason": requirement_status.reason if requirement_status else None, "status": requirement_status.status if requirement_status else None, "status_date": requirement_status.modified if requirement_status else None, # We retain the old name "order" in the API because changing APIs takes a lot more coordination. "order": requirement.sort_value, }) return statuses
def test_set_credit_requirement_status(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] set_credit_requirements(self.course_key, requirements) course_requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(course_requirements), 2) requirement = get_credit_requirement(self.course_key, "grade", "grade") set_credit_requirement_status("staff", requirement, 'satisfied', {}) course_requirement = CreditRequirement.get_course_requirement( requirement['course_key'], requirement['namespace'], requirement['name'] ) status = CreditRequirementStatus.objects.get(username="******", requirement=course_requirement) self.assertEqual(status.requirement.namespace, requirement['namespace']) self.assertEqual(status.status, "satisfied") set_credit_requirement_status( "staff", requirement, 'failed', {'failure_reason': "requirements not satisfied"} ) status = CreditRequirementStatus.objects.get(username="******", requirement=course_requirement) self.assertEqual(status.requirement.namespace, requirement['namespace']) self.assertEqual(status.status, "failed")
def remove_credit_requirement_status(username, course_key, req_namespace, req_name): """ Remove the user's requirement status. This will remove the record from the credit requirement status table. The user will still be eligible for the credit in a course. Args: username (str): Username of the user course_key (CourseKey): Identifier for the course associated with the requirement. req_namespace (str): Namespace of the requirement (e.g. "grade" or "reverification") req_name (str): Name of the requirement (e.g. "grade" or the location of the ICRV XBlock) Example: >>> remove_credit_requirement_status( "staff", CourseKey.from_string("course-v1-edX-DemoX-1T2015"), "reverification", "i4x://edX/DemoX/edx-reverification-block/assessment_uuid". ) """ # Find the requirement we're trying to remove req_to_remove = CreditRequirement.get_course_requirements(course_key, namespace=req_namespace, name=req_name) # If we can't find the requirement, then the most likely explanation # is that there was a lag removing the credit requirements after the course # was published. We *could* attempt to remove the requirement here, # but that could cause serious performance issues if many users attempt to # lock the row at the same time. # Instead, we skip removing the requirement and log an error. if not req_to_remove: log.error( ( u'Could not remove credit requirement in course "%s" ' u'with namespace "%s" and name "%s" ' u'because the requirement does not exist. ' ), unicode(course_key), req_namespace, req_name ) return # Remove the requirement status CreditRequirementStatus.remove_requirement_status( username, req_to_remove )
def test_requirements_to_disable(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) old_requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(old_requirements), 1) requirements = [ { "namespace": "reverification", "name": "midterm", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 1) self.assertEqual(requirements_to_disabled[0], old_requirements[0].id) requirements = [ { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 0)
def get_credit_requirements(course_key, namespace=None): """ Get credit eligibility requirements of a given course and namespace. Args: course_key(CourseKey): The identifier for course namespace(str): Namespace of requirements Example: >>> get_credit_requirements("course-v1-edX-DemoX-1T2015") { requirements = [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, }, { "namespace": "proctored_exam", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Final Exam", "criteria": {}, }, { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.8}, }, ] } Returns: Dict of requirements in the given namespace """ requirements = CreditRequirement.get_course_requirements(course_key, namespace) return [ { "namespace": requirement.namespace, "name": requirement.name, "display_name": requirement.display_name, "criteria": requirement.criteria } for requirement in requirements ]
def test_requirements_to_disable(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) old_requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(old_requirements), 1) requirements = [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 1) self.assertEqual(requirements_to_disabled[0], old_requirements[0].id) requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 0)
def test_requirements_to_disable(self): self.add_credit_course() requirements = [{ "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }] set_credit_requirements(self.course_key, requirements) old_requirements = CreditRequirement.get_course_requirements( self.course_key) self.assertEqual(len(old_requirements), 1) requirements = [{ "namespace": "reverification", "name": "midterm", "criteria": {} }] requirements_to_disabled = _get_requirements_to_disable( old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 1) self.assertEqual(requirements_to_disabled[0], old_requirements[0].id) requirements = [{ "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "criteria": {} }] requirements_to_disabled = _get_requirements_to_disable( old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 0)
def add_course_requirements(self): """ Add requirements to course. """ requirements = ( { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "new_grade", "name": "new_grade", "display_name": "new_grade", "criteria": { "min_grade": 0.8 }, } ) for i, requirement in enumerate(requirements): credit_requirement, _ = CreditRequirement.add_or_update_course_requirement( self.credit_course, requirement, i ) CreditRequirementStatus.add_or_update_requirement_status( self.old_username, credit_requirement, "satisfied", { "final_grade": 0.95 } )
def set_credit_requirement_status(user, course_key, req_namespace, req_name, status="satisfied", reason=None): """ Update the user's requirement status. This will record whether the user satisfied or failed a particular requirement in a course. If the user has satisfied all requirements, the user will be marked as eligible for credit in the course. Args: user(User): User object to set credit requirement for. course_key (CourseKey): Identifier for the course associated with the requirement. req_namespace (str): Namespace of the requirement (e.g. "grade" or "reverification") req_name (str): Name of the requirement (e.g. "grade" or the location of the ICRV XBlock) Keyword Arguments: status (str): Status of the requirement (either "satisfied" or "failed") reason (dict): Reason of the status """ # Check whether user has credit eligible enrollment. enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user( user, course_key) has_credit_eligible_enrollment = ( CourseMode.is_credit_eligible_slug(enrollment_mode) and is_active) # Refuse to set status of requirement if the user enrollment is not credit eligible. if not has_credit_eligible_enrollment: return # Do not allow students who have requested credit to change their eligibility if CreditRequest.get_user_request_status(user.username, course_key): log.info( u'Refusing to set status of requirement with namespace "%s" and name "%s" because the ' u'user "%s" has already requested credit for the course "%s".', req_namespace, req_name, user.username, course_key) return # Do not allow a student who has earned eligibility to un-earn eligibility eligible_before_update = CreditEligibility.is_user_eligible_for_credit( course_key, user.username) if eligible_before_update and status == 'failed': log.info( u'Refusing to set status of requirement with namespace "%s" and name "%s" to "failed" because the ' u'user "%s" is already eligible for credit in the course "%s".', req_namespace, req_name, user.username, course_key) return # Retrieve all credit requirements for the course # We retrieve all of them to avoid making a second query later when # we need to check whether all requirements have been satisfied. reqs = CreditRequirement.get_course_requirements(course_key) # Find the requirement we're trying to set req_to_update = next( (req for req in reqs if req.namespace == req_namespace and req.name == req_name), None) # If we can't find the requirement, then the most likely explanation # is that there was a lag updating the credit requirements after the course # was published. We *could* attempt to create the requirement here, # but that could cause serious performance issues if many users attempt to # lock the row at the same time. # Instead, we skip updating the requirement and log an error. if req_to_update is None: log.error( (u'Could not update credit requirement in course "%s" ' u'with namespace "%s" and name "%s" ' u'because the requirement does not exist. ' u'The user "%s" should have had his/her status updated to "%s".'), unicode(course_key), req_namespace, req_name, user.username, status) return # Update the requirement status CreditRequirementStatus.add_or_update_requirement_status(user.username, req_to_update, status=status, reason=reason) # If we're marking this requirement as "satisfied", there's a chance that the user has met all eligibility # requirements, and should be notified. However, if the user was already eligible, do not send another notification. if status == "satisfied" and not eligible_before_update: is_eligible, eligibility_record_created = CreditEligibility.update_eligibility( reqs, user.username, course_key) if eligibility_record_created and is_eligible: try: send_credit_notifications(user.username, course_key) except Exception: # pylint: disable=broad-except log.exception("Error sending email")
def get_credit_requirement_status(course_key, username, namespace=None, name=None): """ Retrieve the user's status for each credit requirement in the course. Args: course_key (CourseKey): The identifier for course username (str): The identifier of the user Example: >>> get_credit_requirement_status("course-v1-edX-DemoX-1T2015", "john") [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "In Course Reverification", "criteria": {}, "reason": {}, "status": "failed", "status_date": "2015-06-26 07:49:13", "order": 0, }, { "namespace": "proctored_exam", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Proctored Mid Term Exam", "criteria": {}, "reason": {}, "status": "satisfied", "status_date": "2015-06-26 11:07:42", "order": 1, }, { "namespace": "grade", "name": "i4x://edX/DemoX/proctoring-block/final_uuid", "display_name": "Minimum Passing Grade", "criteria": {"min_grade": 0.8}, "reason": {"final_grade": 0.95}, "status": "satisfied", "status_date": "2015-06-26 11:07:44", "order": 2, }, ] Returns: list of requirement statuses """ requirements = CreditRequirement.get_course_requirements(course_key, namespace=namespace, name=name) requirement_statuses = CreditRequirementStatus.get_statuses(requirements, username) requirement_statuses = dict((o.requirement, o) for o in requirement_statuses) statuses = [] for requirement in requirements: requirement_status = requirement_statuses.get(requirement) statuses.append({ "namespace": requirement.namespace, "name": requirement.name, "display_name": requirement.display_name, "criteria": requirement.criteria, "reason": requirement_status.reason if requirement_status else None, "status": requirement_status.status if requirement_status else None, "status_date": requirement_status.modified if requirement_status else None, "order": requirement.order, }) return statuses
def set_credit_requirement_status(user, course_key, req_namespace, req_name, status="satisfied", reason=None): """ Update the user's requirement status. This will record whether the user satisfied or failed a particular requirement in a course. If the user has satisfied all requirements, the user will be marked as eligible for credit in the course. Args: user(User): User object to set credit requirement for. course_key (CourseKey): Identifier for the course associated with the requirement. req_namespace (str): Namespace of the requirement (e.g. "grade" or "reverification") req_name (str): Name of the requirement (e.g. "grade" or the location of the ICRV XBlock) Keyword Arguments: status (str): Status of the requirement (either "satisfied" or "failed") reason (dict): Reason of the status """ # Check whether user has credit eligible enrollment. enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(user, course_key) has_credit_eligible_enrollment = (CourseMode.is_credit_eligible_slug(enrollment_mode) and is_active) # Refuse to set status of requirement if the user enrollment is not credit eligible. if not has_credit_eligible_enrollment: return # Do not allow students who have requested credit to change their eligibility if CreditRequest.get_user_request_status(user.username, course_key): log.info( u'Refusing to set status of requirement with namespace "%s" and name "%s" because the ' u'user "%s" has already requested credit for the course "%s".', req_namespace, req_name, user.username, course_key ) return # Do not allow a student who has earned eligibility to un-earn eligibility eligible_before_update = CreditEligibility.is_user_eligible_for_credit(course_key, user.username) if eligible_before_update and status == 'failed': log.info( u'Refusing to set status of requirement with namespace "%s" and name "%s" to "failed" because the ' u'user "%s" is already eligible for credit in the course "%s".', req_namespace, req_name, user.username, course_key ) return # Retrieve all credit requirements for the course # We retrieve all of them to avoid making a second query later when # we need to check whether all requirements have been satisfied. reqs = CreditRequirement.get_course_requirements(course_key) # Find the requirement we're trying to set req_to_update = next(( req for req in reqs if req.namespace == req_namespace and req.name == req_name ), None) # If we can't find the requirement, then the most likely explanation # is that there was a lag updating the credit requirements after the course # was published. We *could* attempt to create the requirement here, # but that could cause serious performance issues if many users attempt to # lock the row at the same time. # Instead, we skip updating the requirement and log an error. if req_to_update is None: log.error( ( u'Could not update credit requirement in course "%s" ' u'with namespace "%s" and name "%s" ' u'because the requirement does not exist. ' u'The user "%s" should have had his/her status updated to "%s".' ), unicode(course_key), req_namespace, req_name, user.username, status ) return # Update the requirement status CreditRequirementStatus.add_or_update_requirement_status( user.username, req_to_update, status=status, reason=reason ) # If we're marking this requirement as "satisfied", there's a chance that the user has met all eligibility # requirements, and should be notified. However, if the user was already eligible, do not send another notification. if status == "satisfied" and not eligible_before_update: is_eligible, eligibility_record_created = CreditEligibility.update_eligibility(reqs, user.username, course_key) if eligibility_record_created and is_eligible: try: send_credit_notifications(user.username, course_key) except Exception: # pylint: disable=broad-except log.exception("Error sending email")
def set_credit_requirement_status(username, course_key, req_namespace, req_name, status="satisfied", reason=None): """ Update the user's requirement status. This will record whether the user satisfied or failed a particular requirement in a course. If the user has satisfied all requirements, the user will be marked as eligible for credit in the course. Args: username (str): Username of the user course_key (CourseKey): Identifier for the course associated with the requirement. req_namespace (str): Namespace of the requirement (e.g. "grade" or "reverification") req_name (str): Name of the requirement (e.g. "grade" or the location of the ICRV XBlock) Keyword Arguments: status (str): Status of the requirement (either "satisfied" or "failed") reason (dict): Reason of the status Example: >>> set_credit_requirement_status( "staff", CourseKey.from_string("course-v1-edX-DemoX-1T2015"), "reverification", "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", status="satisfied", reason={} ) """ # Check if we're already eligible for credit. # If so, short-circuit this process. if CreditEligibility.is_user_eligible_for_credit(course_key, username): log.info( u'Skipping update of credit requirement with namespace "%s" ' u'and name "%s" because the user "%s" is already eligible for credit ' u'in the course "%s".', req_namespace, req_name, username, course_key ) return # Retrieve all credit requirements for the course # We retrieve all of them to avoid making a second query later when # we need to check whether all requirements have been satisfied. reqs = CreditRequirement.get_course_requirements(course_key) # Find the requirement we're trying to set req_to_update = next(( req for req in reqs if req.namespace == req_namespace and req.name == req_name ), None) # If we can't find the requirement, then the most likely explanation # is that there was a lag updating the credit requirements after the course # was published. We *could* attempt to create the requirement here, # but that could cause serious performance issues if many users attempt to # lock the row at the same time. # Instead, we skip updating the requirement and log an error. if req_to_update is None: log.error( ( u'Could not update credit requirement in course "%s" ' u'with namespace "%s" and name "%s" ' u'because the requirement does not exist. ' u'The user "%s" should have had his/her status updated to "%s".' ), unicode(course_key), req_namespace, req_name, username, status ) return # Update the requirement status CreditRequirementStatus.add_or_update_requirement_status( username, req_to_update, status=status, reason=reason ) # If we're marking this requirement as "satisfied", there's a chance # that the user has met all eligibility requirements. if status == "satisfied": is_eligible, eligibility_record_created = CreditEligibility.update_eligibility(reqs, username, course_key) if eligibility_record_created and is_eligible: try: send_credit_notifications(username, course_key) except Exception: # pylint: disable=broad-except log.error("Error sending email")