def validate_user_id(self, user_id): try: self.user = get_user_by_username_or_email(user_id) return user_id except DjangoUser.DoesNotExist: raise ValidationError( u"'{}' is not a valid student identifier".format(user_id))
def remove_user_from_cohort(cohort, username_or_email): """ Look up the given user, and if successful, remove them from the specified cohort. Arguments: cohort: CourseUserGroup username_or_email: string. Treated as email if has '@' Raises: User.DoesNotExist if can't find user. ValueError if user not already present in this cohort. """ user = get_user_by_username_or_email(username_or_email) try: membership = CohortMembership.objects.get(course_user_group=cohort, user=user) course_key = membership.course_id membership.delete() COHORT_MEMBERSHIP_UPDATED.send(sender=None, user=user, course_key=course_key) except CohortMembership.DoesNotExist: raise ValueError(u"User {} was not present in cohort {}".format( username_or_email, cohort))
def validate_user_id(self, user_id): # lint-amnesty, pylint: disable=missing-function-docstring try: self.user = get_user_by_username_or_email(user_id) return user_id except User.DoesNotExist: raise ValidationError( f"'{user_id}' is not a valid student identifier") # lint-amnesty, pylint: disable=raise-missing-from
def get_student_from_identifier(unique_student_identifier): """ Gets a student object using either an email address or username. Returns the student object associated with `unique_student_identifier` Raises User.DoesNotExist if no user object can be found, the user was retired, or the user is in the process of being retired. DEPRECATED: use student.models.get_user_by_username_or_email instead. """ return get_user_by_username_or_email(unique_student_identifier)
def delete_student_attempt(self, student_identifier, course_id, content_id, requesting_user): """ Deletes student state for a problem. requesting_user may be kept as an audit trail. Takes some of the following query parameters - student_identifier is an email or username - content_id is a url-name of a problem - course_id is the id for the course """ course_id = CourseKey.from_string(course_id) try: student = get_user_by_username_or_email(student_identifier) except ObjectDoesNotExist: err_msg = ( 'Error occurred while attempting to reset student attempts for user ' f'{student_identifier} for content_id {content_id}. ' 'User does not exist!') log.error(err_msg) return try: module_state_key = UsageKey.from_string(content_id) except InvalidKeyError: err_msg = (f'Invalid content_id {content_id}!') log.error(err_msg) return if student: try: enrollment.reset_student_attempts( course_id, student, module_state_key, requesting_user=requesting_user, delete_module=True, ) except (StudentModule.DoesNotExist, enrollment.sub_api.SubmissionError): err_msg = ( 'Error occurred while attempting to reset student attempts for user ' f'{student_identifier} for content_id {content_id}.') log.error(err_msg) # In some cases, reset_student_attempts does not clear the entire exam's completion state. # One example of this is an exam with multiple units (verticals) within it and the learner # never viewing one of the units. All of the content in that unit will still be marked complete, # but the reset code is unable to handle clearing the completion in that scenario. update_exam_completion_task.apply_async( (student_identifier, content_id, 0.0))
def validate_user_id(self, user_id): """ Validate user id Args: user_id (str): username or email Returns: str: user id if valid """ try: self.user = get_user_by_username_or_email(user_id) return user_id except User.DoesNotExist as err: raise ValidationError( f"'{user_id}' is not a valid student identifier") from err
def get_user_group_ids(course_id, content, user=None): """ Given a user, course ID, and the content of the thread or comment, returns the group ID for the current user and the user that posted the thread/comment. """ content_user_group_id = None user_group_id = None if course_id is not None: if content.get('username'): try: content_user = get_user_by_username_or_email(content.get('username')) content_user_group_id = get_group_id_for_user_from_cache(content_user, course_id) except User.DoesNotExist: content_user_group_id = None user_group_id = get_group_id_for_user_from_cache(user, course_id) if user else None return user_group_id, content_user_group_id
def get(self, request, course_id=None, username=None): """ Return user-metadata for the given course and user """ try: user = get_user_by_username_or_email(username) except User.DoesNotExist: # Note: this will only be seen by staff, for administrative de-bugging purposes message = "Provided user is not found" return JsonResponse({'message': message}, status=404) try: course = courses.get_course_by_id(CourseKey.from_string(course_id)) except Http404: message = "Provided course is not found" return JsonResponse({'message': message}, status=404) context = get_experiment_user_metadata_context(course, user) user_metadata = context.get('user_metadata') return JsonResponse(user_metadata)
def delete_student_attempt(self, student_identifier, course_id, content_id, requesting_user): """ Deletes student state for a problem. requesting_user may be kept as an audit trail. Takes some of the following query parameters - student_identifier is an email or username - content_id is a url-name of a problem - course_id is the id for the course """ course_id = CourseKey.from_string(course_id) try: student = get_user_by_username_or_email(student_identifier) except ObjectDoesNotExist: err_msg = ( 'Error occurred while attempting to reset student attempts for user ' f'{student_identifier} for content_id {content_id}. ' 'User does not exist!') log.error(err_msg) return try: module_state_key = UsageKey.from_string(content_id) except InvalidKeyError: err_msg = (f'Invalid content_id {content_id}!') log.error(err_msg) return if student: try: enrollment.reset_student_attempts( course_id, student, module_state_key, requesting_user=requesting_user, delete_module=True, ) except (StudentModule.DoesNotExist, enrollment.sub_api.SubmissionError): err_msg = ( 'Error occurred while attempting to reset student attempts for user ' f'{student_identifier} for content_id {content_id}.') log.error(err_msg)
def get_anonymous_user_id(self, username, course_id): """ Get the anonymous user id for a user. Args: username(str): username of a user. course_id(str): course id of particular course. Returns: A unique anonymous_user_id for (user, course) pair. None for Non-staff users. """ if not self.get_current_user().opt_attrs.get(ATTR_KEY_USER_IS_STAFF): return None try: user = get_user_by_username_or_email(username_or_email=username) except User.DoesNotExist: return None course_id = CourseKey.from_string(course_id) return anonymous_id_for_user(user=user, course_id=course_id)
def get_state_as_dict(self, username_or_email, block_id): """ Return dict containing user state for a given set of parameters. Arguments: username_or_email: username or email of the user for whom the data is being retrieved block_id: string/object representation of the block whose user state is required Returns: Returns a dict containing user state, if present, else empty. """ try: user = get_user_by_username_or_email( username_or_email=username_or_email) except User.DoesNotExist: return {} try: student_module = StudentModule.objects.get( student=user, module_state_key=block_id) return json.loads(student_module.state) except StudentModule.DoesNotExist: return {}
def get_quiz_data(self): pr_class = ProblemResponses().__class__ user_id = user_by_anonymous_id( self.xmodule_runtime.anonymous_student_id).id course_key = self.course_id valid_cohorts = self.get_cohorts() usage_key = self.get_quiz_unit() if not usage_key: raise InvalidKeyError user = get_user_model().objects.get(pk=user_id) student_data = [] store = modulestore() with store.bulk_operations(course_key): try: course_blocks = get_course_blocks(user, usage_key) except: raise QuizNotFound usernames = set() for title, path, block_key in pr_class._build_problem_list( course_blocks, usage_key): # Chapter and sequential blocks are filtered out since they include state # which isn't useful for this report. if block_key.block_type != "problem": continue block = store.get_item(block_key) generated_report_data = defaultdict(list) # Blocks can implement the generate_report_data method to provide their own # human-readable formatting for user state. try: user_state_iterator = iter_all_for_block(block_key) for username, state in self.generate_report_data( block, user_state_iterator): generated_report_data[username].append(state) except NotImplementedError: pass cohorted = is_course_cohorted(self.course_id) def in_cohort(user): if cohorted: cohort = get_cohort(user, course_key, assign=False, use_cached=True) if not cohort or cohort.name not in valid_cohorts or ( self.cohort and cohort.name != self.cohort): # skip this one if not on the requested cohort or has no cohort (instructor) return False return True responses = [] for response in list_problem_responses(course_key, block_key): # A block that has a single state per user can contain multiple responses # within the same state. try: user = get_user_by_username_or_email( response['username']) except User.DoesNotExist: continue usernames.add(user.username) if not in_cohort(user): continue response['name'] = self.format_name(user.profile.name) user_states = generated_report_data.get( response['username']) response['state'] = json.loads(response['state']) response['state'].pop('input_state', None) response['state'].pop('student_answers', None) if user_states: response['user_states'] = user_states responses.append(response) enrollments = CourseEnrollment.objects.filter( course_id=self.course_id) for enr in enrollments: if enr.user.username in usernames: continue if in_cohort(enr.user): # add missing students student_data.append({ 'username': enr.user.username, 'name': self.format_name(enr.user.profile.name) }) usernames.add(enr.user.username) student_data += responses return student_data
def complete_student_attempt_task(user_identifier: str, content_id: str) -> None: """ Marks all completable children of content_id as complete for the user Submits all completable xblocks inside of the content_id block to the Completion Service to mark them as complete. One use case of this function is for special exams (timed/proctored) where regardless of submission status on individual problems, we want to mark the entire exam as complete when the exam is finished. params: user_identifier (str): username or email of a user content_id (str): the block key for a piece of content """ err_msg_prefix = ( 'Error occurred while attempting to complete student attempt for user ' f'{user_identifier} for content_id {content_id}. ' ) err_msg = None try: user = get_user_by_username_or_email(user_identifier) block_key = UsageKey.from_string(content_id) root_descriptor = modulestore().get_item(block_key) except ObjectDoesNotExist: err_msg = err_msg_prefix + 'User does not exist!' except InvalidKeyError: err_msg = err_msg_prefix + 'Invalid content_id!' except ItemNotFoundError: err_msg = err_msg_prefix + 'Block not found in the modulestore!' if err_msg: log.error(err_msg) return # This logic has been copied over from openedx/core/djangoapps/schedules/content_highlights.py # in the _get_course_module function. # I'm not sure if this is an anti-pattern or not, so if you can avoid re-copying this, please do. # We are using it here because we ran into issues with the User service being undefined when we # encountered a split_test xblock. # Fake a request to fool parts of the courseware that want to inspect it. request = get_request_or_stub() request.user = user # Now evil modulestore magic to inflate our descriptor with user state and # permissions checks. field_data_cache = FieldDataCache.cache_for_descriptor_descendents( root_descriptor.course_id, user, root_descriptor, read_only=True, ) root_module = get_module_for_descriptor( user, request, root_descriptor, field_data_cache, root_descriptor.course_id, ) if not root_module: err_msg = err_msg_prefix + 'Module unable to be created from descriptor!' log.error(err_msg) return def _submit_completions(block, user): """ Recursively submits the children for completion to the Completion Service """ mode = XBlockCompletionMode.get_mode(block) if mode == XBlockCompletionMode.COMPLETABLE: block.runtime.publish(block, 'completion', {'completion': 1.0, 'user_id': user.id}) elif mode == XBlockCompletionMode.AGGREGATOR: # I know this looks weird, but at the time of writing at least, there isn't a good # single way to get the children assigned for a partcular user. Some blocks define the # child descriptors method, but others don't and with blocks like Randomized Content # (Library Content), the get_children method returns all children and not just assigned # children. So this is our way around situations like that. See also Split Test Module # for another use case where user state has to be taken into account via get_child_descriptors block_children = ((hasattr(block, 'get_child_descriptors') and block.get_child_descriptors()) or (hasattr(block, 'get_children') and block.get_children()) or []) for child in block_children: _submit_completions(child, user) _submit_completions(root_module, user)
def add_user_to_cohort(cohort, username_or_email_or_user): """ Look up the given user, and if successful, add them to the specified cohort. Arguments: cohort: CourseUserGroup username_or_email_or_user: user or string. Treated as email if has '@' Returns: User object (or None if the email address is preassigned), string (or None) indicating previous cohort, and whether the user is a preassigned user or not Raises: User.DoesNotExist if can't find user. However, if a valid email is provided for the user, it is stored in a database so that the user can be added to the cohort if they eventually enroll in the course. ValueError if user already present in this cohort. ValidationError if an invalid email address is entered. User.DoesNotExist if a user could not be found. """ try: if hasattr(username_or_email_or_user, 'email'): user = username_or_email_or_user else: user = get_user_by_username_or_email(username_or_email_or_user) membership, previous_cohort = CohortMembership.assign(cohort, user) tracker.emit( "edx.cohort.user_add_requested", { "user_id": user.id, "cohort_id": cohort.id, "cohort_name": cohort.name, "previous_cohort_id": getattr(previous_cohort, 'id', None), "previous_cohort_name": getattr(previous_cohort, 'name', None), } ) cache = RequestCache(COHORT_CACHE_NAMESPACE).data cache_key = _cohort_cache_key(user.id, membership.course_id) cache[cache_key] = membership.course_user_group COHORT_MEMBERSHIP_UPDATED.send(sender=None, user=user, course_key=membership.course_id) return user, getattr(previous_cohort, 'name', None), False except User.DoesNotExist as ex: # If username_or_email is an email address, store in database. try: validate_email(username_or_email_or_user) try: assignment = UnregisteredLearnerCohortAssignments.objects.get( email=username_or_email_or_user, course_id=cohort.course_id ) assignment.course_user_group = cohort assignment.save() except UnregisteredLearnerCohortAssignments.DoesNotExist: assignment = UnregisteredLearnerCohortAssignments.objects.create( course_user_group=cohort, email=username_or_email_or_user, course_id=cohort.course_id ) tracker.emit( "edx.cohort.email_address_preassigned", { "user_email": assignment.email, "cohort_id": cohort.id, "cohort_name": cohort.name, } ) return (None, None, True) except ValidationError as invalid: if "@" in username_or_email_or_user: # lint-amnesty, pylint: disable=no-else-raise raise invalid else: raise ex # lint-amnesty, pylint: disable=raise-missing-from
def get_student(): """ Fetches student instance if an identifier is provided, else return None """ return None if not student_identifier else get_user_by_username_or_email( student_identifier)