def post(self, request, provider_id): """ POST handler. """ # Get the provider, or return HTTP 404 if it doesn't exist provider = generics.get_object_or_404(CreditProvider, provider_id=provider_id) # Validate the course key course_key = request.data.get('course_key') try: course_key = CourseKey.from_string(course_key) except InvalidKeyError: raise InvalidCourseKey(course_key) # Validate the username username = request.data.get('username') if not username: raise ValidationError({'detail': 'A username must be specified.'}) # Ensure the user is actually eligible to receive credit if not CreditEligibility.is_user_eligible_for_credit(course_key, username): raise UserNotEligibleException(course_key, username) try: credit_request = create_credit_request(course_key, provider.provider_id, username) return Response(credit_request) except CreditApiBadRequest as ex: raise InvalidCreditRequest(text_type(ex))
def post(self, request, provider_id): """ POST handler. """ # Get the provider, or return HTTP 404 if it doesn't exist provider = generics.get_object_or_404(CreditProvider, provider_id=provider_id) # Validate the course key course_key = request.data.get('course_key') try: course_key = CourseKey.from_string(course_key) except InvalidKeyError: raise InvalidCourseKey(course_key) # Validate the username username = request.data.get('username') if not username: raise ValidationError({'detail': 'A username must be specified.'}) # Ensure the user is actually eligible to receive credit if not CreditEligibility.is_user_eligible_for_credit( course_key, username): raise UserNotEligibleException(course_key, username) try: credit_request = create_credit_request(course_key, provider.provider_id, username) return Response(credit_request) except CreditApiBadRequest as ex: raise InvalidCreditRequest(text_type(ex))
def is_user_eligible_for_credit(username, course_key): """ Returns a boolean indicating if the user is eligible for credit for the given course Args: username(str): The identifier for user course_key (CourseKey): The identifier for course Returns: True if user is eligible for the course else False """ return CreditEligibility.is_user_eligible_for_credit(course_key, username)
def is_user_eligible_for_credit(username, course_key): """ Returns a boolean indicating if the user is eligible for credit for the given course Args: username(str): The identifier for user course_key (CourseKey): The identifier for course Returns: True if user is eligible for the course else False """ return CreditEligibility.is_user_eligible_for_credit(course_key, username)
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")
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")