def post(self, request): """ POST /api/user/v1/accounts/retire_misc/ { 'username': '******' } Retires the user with the given username in the LMS. """ username = request.data['username'] try: retirement = UserRetirementStatus.get_retirement_for_retirement_action( username) RevisionPluginRevision.retire_user(retirement.user) ArticleRevision.retire_user(retirement.user) PendingNameChange.delete_by_user_value(retirement.user, field='user') PasswordHistory.retire_user(retirement.user.id) course_enrollments = CourseEnrollment.objects.filter( user=retirement.user) ManualEnrollmentAudit.retire_manual_enrollments( course_enrollments, retirement.retired_email) CreditRequest.retire_user(retirement) ApiAccessRequest.retire_user(retirement.user) CreditRequirementStatus.retire_user(retirement) # This signal allows code in higher points of LMS to retire the user as necessary USER_RETIRE_LMS_MISC.send(sender=self.__class__, user=retirement.user) # This signal allows code in higher points of LMS to unsubscribe the user # from various types of mailings. USER_RETIRE_MAILINGS.send(sender=self.__class__, email=retirement.original_email, new_email=retirement.retired_email, user=retirement.user) except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT)
def get_credit_request_status(username, course_key): """Get the credit request status. This function returns the status of credit request of user for given course. It returns the latest request status for the any credit provider. The valid status are 'pending', 'approved' or 'rejected'. Args: username(str): The username of user course_key(CourseKey): The course locator key Returns: A dictionary of credit request user has made if any """ credit_request = CreditRequest.get_user_request_status(username, course_key) return { "uuid": credit_request.uuid, "timestamp": credit_request.modified, "course_key": credit_request.course.course_key, "provider": { "id": credit_request.provider.provider_id, "display_name": credit_request.provider.display_name }, "status": credit_request.status } if credit_request else {}
def get_credit_requests_for_user(username): """ Retrieve the status of a credit request. Returns either "pending", "approved", or "rejected" Arguments: username (unicode): The username of the user who initiated the requests. Returns: list Example Usage: >>> get_credit_request_status_for_user("bob") [ { "uuid": "557168d0f7664fe59097106c67c3f847", "timestamp": 1434631630, "course_key": "course-v1:HogwartsX+Potions101+1T2015", "provider": { "id": "HogwartsX", "display_name": "Hogwarts School of Witchcraft and Wizardry", }, "status": "pending" # or "approved" or "rejected" } ] """ return CreditRequest.credit_requests_for_user(username)
def get_credit_request_status(username, course_key): """Get the credit request status. This function returns the status of credit request of user for given course. It returns the latest request status for the any credit provider. The valid status are 'pending', 'approved' or 'rejected'. Args: username(str): The username of user course_key(CourseKey): The course locator key Returns: A dictionary of credit request user has made if any """ credit_request = CreditRequest.get_user_request_status( username, course_key) return { "uuid": credit_request.uuid, "timestamp": credit_request.modified, "course_key": credit_request.course.course_key, "provider": { "id": credit_request.provider.provider_id, "display_name": credit_request.provider.display_name }, "status": credit_request.status } if credit_request else {}
def post(self, request): """ POST /api/user/v1/accounts/retire_misc/ { 'username': '******' } Retires the user with the given username in the LMS. """ username = request.data['username'] try: retirement = UserRetirementStatus.get_retirement_for_retirement_action(username) RevisionPluginRevision.retire_user(retirement.user) ArticleRevision.retire_user(retirement.user) PendingNameChange.delete_by_user_value(retirement.user, field='user') PasswordHistory.retire_user(retirement.user.id) course_enrollments = CourseEnrollment.objects.filter(user=retirement.user) ManualEnrollmentAudit.retire_manual_enrollments(course_enrollments, retirement.retired_email) CreditRequest.retire_user(retirement.original_username, retirement.retired_username) ApiAccessRequest.retire_user(retirement.user) CreditRequirementStatus.retire_user(retirement.user.username) # This signal allows code in higher points of LMS to retire the user as necessary USER_RETIRE_LMS_MISC.send(sender=self.__class__, user=retirement.user) # This signal allows code in higher points of LMS to unsubscribe the user # from various types of mailings. USER_RETIRE_MAILINGS.send( sender=self.__class__, email=retirement.original_email, new_email=retirement.retired_email, user=retirement.user ) except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT)
def post(self, request): """ POST /api/user/v1/accounts/retire_misc/ { 'username': '******' } Retires the user with the given username in the LMS. """ username = request.data['username'] if is_username_retired(username): return Response(status=status.HTTP_404_NOT_FOUND) try: retirement = UserRetirementStatus.get_retirement_for_retirement_action( username) RevisionPluginRevision.retire_user(retirement.user) ArticleRevision.retire_user(retirement.user) PendingNameChange.delete_by_user_value(retirement.user, field='user') PasswordHistory.retire_user(retirement.user.id) course_enrollments = CourseEnrollment.objects.filter( user=retirement.user) ManualEnrollmentAudit.retire_manual_enrollments( course_enrollments, retirement.retired_email) CreditRequest.retire_user(retirement.original_username, retirement.retired_username) ApiAccessRequest.retire_user(retirement.user) CreditRequirementStatus.retire_user(retirement.user.username) SurveyAnswer.retire_user(retirement.user.id) except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT)
def test_cannot_retire_nonexistent_user(self): test_parameters = {'hi': 'there'} CreditRequest.objects.create( username=self.user.username, course=self.credit_course, provider=self.provider, parameters=test_parameters, ) another_user = UserFactory.create() another_retirement = UserRetirementStatus.create_retirement( another_user) credit_request_before_retire = CreditRequest.objects.filter( username=self.retirement.original_username)[0] was_retired = CreditRequest.retire_user(another_retirement) credit_request_before_retire.refresh_from_db() assert not was_retired assert credit_request_before_retire.parameters == test_parameters
def test_cannot_retire_nonexistent_user(self): test_parameters = {'hi': 'there'} CreditRequest.objects.create( username=self.user.username, course=self.credit_course, provider=self.provider, parameters=test_parameters, ) another_user = UserFactory.create() another_retirement = UserRetirementStatus.create_retirement(another_user) credit_request_before_retire = CreditRequest.objects.filter( username=self.retirement.original_username )[0] was_retired = CreditRequest.retire_user(another_retirement) credit_request_before_retire.refresh_from_db() self.assertFalse(was_retired) self.assertEqual(credit_request_before_retire.parameters, test_parameters)
def test_can_retire_user_from_credit_request(self): test_parameters = {'hi': 'there'} CreditRequest.objects.create( username=self.user.username, course=self.credit_course, provider=self.provider, parameters=test_parameters, ) credit_request_before_retire = CreditRequest.objects.filter( username=self.user.username)[0] assert credit_request_before_retire.parameters == test_parameters user_was_retired = CreditRequest.retire_user(self.retirement) credit_request_before_retire.refresh_from_db() credit_requests_after_retire = CreditRequest.objects.filter( username=self.retirement.original_username) assert user_was_retired assert credit_request_before_retire.parameters == {} assert not credit_requests_after_retire.exists()
def test_can_retire_user_from_credit_request(self): test_parameters = {'hi': 'there'} CreditRequest.objects.create( username=self.user.username, course=self.credit_course, provider=self.provider, parameters=test_parameters, ) credit_request_before_retire = CreditRequest.objects.filter( username=self.user.username)[0] self.assertEqual(credit_request_before_retire.parameters, test_parameters) user_was_retired = CreditRequest.retire_user(self.retirement) credit_request_before_retire.refresh_from_db() credit_requests_after_retire = CreditRequest.objects.filter( username=self.retirement.original_username) self.assertTrue(user_was_retired) self.assertEqual(credit_request_before_retire.parameters, {}) self.assertFalse(credit_requests_after_retire.exists())
def test_can_retire_user_from_credit_request(self): test_parameters = {'hi': 'there'} CreditRequest.objects.create( username=self.user.username, course=self.credit_course, provider=self.provider, parameters=test_parameters, ) credit_request_before_retire = CreditRequest.objects.filter( username=self.user.username )[0] self.assertEqual(credit_request_before_retire.parameters, test_parameters) user_was_retired = CreditRequest.retire_user(self.retirement) credit_request_before_retire.refresh_from_db() credit_requests_after_retire = CreditRequest.objects.filter( username=self.retirement.original_username ) self.assertTrue(user_was_retired) self.assertEqual(credit_request_before_retire.parameters, {}) self.assertFalse(credit_requests_after_retire.exists())
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(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 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 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.error("Error sending email")